Andrew M
Andrew M

Reputation: 93

Django: return distinct set of many to many relationship belonging to a particular model

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

Answers (1)

Peter DeGlopper
Peter DeGlopper

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

Related Questions