Flimm
Flimm

Reputation: 150927

Always False Q object

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()

Upvotes: 16

Views: 3863

Answers (3)

Sam Mason
Sam Mason

Reputation: 16184

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?

Upvotes: 18

Alasdair
Alasdair

Reputation: 308979

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))
[]

Upvotes: 17

fwip
fwip

Reputation: 415

I don't have enough reputation to comment, but Sam Mason's answer (Q(pk__in=[])) has the advantage that it doesn't even perform a database query if used alone. Django (v1.10) seems smart enough to recognize that the condition is unsatisfiable, and returns an empty queryset without asking the database.

$ ./manage.py shell_plus

In [1]: from django.db import connection

In [2]: FooBar.objects.filter(Q(pk__in=[]))
Out[2]: <QuerySet []>

In [3]: connection.queries
Out[3]: []

Upvotes: 13

Related Questions