Reputation: 93
I want a query that returns all the distinct tags belonging to all the entries in Model A. I suspect it's possible with clever query and a bit of filtering, but I can't work it out. I want to minimise database hits.
Models:
class Tag(models.Model):
name = models.CharField(max_length = 50,)
category = models.CharField(max_length = 50, blank=True)
class A(models.Model):
...
tags = models.ManyToManyField(Tag, blank=True)
class B(models.Model):
...
tags = models.ManyToManyField(Tag, blank=True)
My solution in the meantime is:
tags = list()
for a in A.objects.all():
for t in a.tags.all():
if t not in tags:
tags.append(t)
Is there an elegant solution to achieve the above functionality?
Upvotes: 1
Views: 191
Reputation: 37319
Tag.objects.filter(a__isnull=False).distinct()
ought to do it.
It's not ultimately relevant here, because the database will be able to do it for you (I may have misremembered a bit of syntax above but if so it'll be correctable, this is definitely doable in a single query) but if you were in a situation where you were operating on data that's not in a database, you'd be much better off using tags = set()
rather than tags = list()
- it'll manage uniqueness for you, and much more efficiently than repeatedly testing for membership in a list. EG:
tags = set()
for a in some_iterable:
for t in a.some_function():
tags.add(t)
That can be condensed into a nested generator expression, which would be somewhat faster - but the use of set
rather than list
is the key change, so I kept it similar to your original attempt. The nested generator expression would look something like:
tags = set(t for a in some_iterable for t in a.some_function())
And if you're not planning to further modify tags
, frozenset(t for a in ...)
instead is a good habit to build.
Upvotes: 3