Django Q object not chaining correctly

Issue

I’m querying a ManyToMany field (tags). The values come from a list:

tag_q = Q()

tag_list = ["work", "friends"]
for tag in tag_list:
    tag_q &= Q(tags__tag=tag)

Post.objects.filter(tag_q)

When I have only one value, it works flawlessly, but when more objects are stacked it always return an empty queryset.

I didn’t used tags__tag__in=tag_list because it returned any post that contained any of the tags in the tag list (an OR filter), and I need an AND filter here.

This is my models:

class Tag(models.Model):
    tag = models.CharField(max_length=19, choices=TagChoices.choices())

class Post(models.Model):
    tags = models.ManyToManyField(Tag, related_name='posts', blank=True)

This is the Q object that is being passed in the filter query:

(AND: ('tags__tag', 'work'), ('tags__tag', 'friends')

Solution

You can not work with a Q object like that: a filter is an existential quantifier, not a universal. It means you are looking for a single Tag that has as tag name work and friends at the same time.

What you can do is work with a list of tags, and then count if the number of Tags is the same as the number of items to search for, like:

tag_list = ['work', 'friends']

Post.objects.filter(tags__tag__in=tag_list).annotate(
    ntags=Count('tags')
).filter(ntags=len(set(tag_list))

since , you can work with .alias(…) [Django-doc] instead of .annotate(…) [Django-doc]:

tag_list = ['work', 'friends']

Post.objects.filter(tags__tag__in=tag_list).alias(
    ntags=Count('tags')
).filter(ntags=len(set(tag_list))

Answered By – Willem Van Onsem

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published