Why does this Django filter query return empty list?

151 Views Asked by At

In my Vechicle table:

Vechicle.objects.all().first().tags.slugs()
<QuerySet ['car', 'awd']>

when I test it in python manage.py shell

>>> from django.db.models import Q
>>> q_objects = Q(tags__slug='car')
>>> q_objects &= Q(tags__slug='awd')
>>> q_objects
<Q: (AND: ('tags__slug', 'car'), ('tags__slug', 'awd'))>
>>> from partpicker.models import Vechicle 
>>> Vechicle .objects.all().filter(q_objects)
<QuerySet []>

EDIT:

Just to show that the normal queries work

>>> q_objects
<Q: (AND: ('tags__slug', 'car'), ('tags__slug', 'awd'))>
>>> Vechicle.objects.all().filter(tags__slug='car')
<QuerySet [<Vechicle: Vechicle object (2)>, <Vechicle: Vechicle object (27)>, <Vechicle: Vechicle object (29)>, <Vechicle: Vechicle object (30)>]>
>>> Vechicle.objects.all().filter(tags__slug='awd')
<QuerySet [<Vechicle: Vechicle object (29)>, <Vechicle: Vechicle object (30)>, <Vechicle: Vechicle object (31)>, <Vechicle: Vechicle object (32)>]>
>>> Vechicle.objects.all().filter(q_objects)
<QuerySet []>

this query should return object 29 and 30 correct?

I actually got it to work if I do it manually but with the q_object it still returns nothing:

>>> awd = Vechicle.objects.all().filter(tags__slug='awd')
>>> car = Vechicle.objects.all().filter(tags__slug='car')
>>> awd & car
<QuerySet [<Vechicle: Vechicle object (29)>, <Vechicle: Vechicle object (30)>]>
>>> q_objects = Q(tags__slug='awd') & Q(tags__slug='car')
>>> Vechicle.objects.all().filter(q_objects)
<QuerySet []>
1

There are 1 best solutions below

1
willeM_ Van Onsem On

Your query looks like Q(tag__slug='car') & Q(tag__slug='awd'), this means that the slug of the same tag should both be Car and Slug, which indeed does not work.

As you found out yourself, you need multiple .filter(…) [Django-doc] calls that will make joins.

You can however also use a trick with an or filter and counting. Indeed, you can work with:

from django.db.models import Count

data = ['car', 'awd']

Vehicle.objects.filter(tags__slug__in=data).alias(ntags=Count('tags')).filter(
    ntags=len(set(data))
)