Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Always False Q object

Tags:

django

In Django ORM, how does one go about creating a Q object that is always False?

This is similar to the question about always True Q objects, but the other way round.

Note that this doesn't work:

Foobar.objects.filter(~Q()) # returns a queryset which gives all objects

Why do I want a Q object instead of the simple False value? So that I can combine it with other Q values, like this for example:

condition = always_true_q_object
if something_or_other:
    condition = condition | foobar_that_returns_a_q_object()
if something_or_other2:
    condition = condition | foobar_that_returns_a_q_object2()
like image 912
Flimm Avatar asked Mar 09 '16 14:03

Flimm


2 Answers

Note: Sam's answer is better. I've left this answer here instead of deleting it so that you can see the 'more hacky' answer that Sam is referring to

Original answer:

What about:

Q(pk__isnull=True)

or

Q(pk=None)

It seems hacky, but it appears to work. For example:

>>> FooBar.objects.filter(Q(x=10)|Q(pk__isnull=True))
[<FooBar: FooBar object>, ...]
>>> FooBar.objects.filter(Q(x=10)&Q(pk__isnull=True))
[]

However, note that it doesn't work as you might expect when OR'd with an empty Q().

>>> FooBar.objects.filter(Q()|Q(pk__isnull=True))
[]

The solution to this might be to use Q(pk__isnull=False) as the 'always True Q'.

>>> FooBar.objects.filter(Q(pk__isnull=False)|Q(pk__isnull=True))
[<FooBar: FooBar object>, ...]
>>> FooBar.objects.filter(Q(pk__isnull=False)&Q(pk__isnull=True))
[]
like image 131
Alasdair Avatar answered Sep 20 '22 11:09

Alasdair


Using Q(pk__in=[]) seems to be a good way to represent this idiom.

As indicated by @fwip and comments below: Django's ORM nicely recognises this case, knowing this always evaluates to FALSE. For example:

FooBar.objects.filter(Q(pk__in=[]))

correctly returns an empty QuerySet without involving any round trip to the database. While:

FooBar.objects.filter(
  (Q(pk__in=[]) & Q(foo="bar")) |
  Q(hello="world")
)

is optimised down to:

FooBar.objects.filter(
  Q(hello="world")
)

i.e. it recognises that Q(pk__in=[]) is always FALSE, hence the AND condition can never be TRUE, so is removed.

To see what queries are actually sent to the database, see: How can I see the raw SQL queries Django is running?

like image 45
Sam Mason Avatar answered Sep 19 '22 11:09

Sam Mason