15

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()
Community
  • 1
  • 1
Flimm
  • 136,138
  • 45
  • 251
  • 267
  • Do you mean `.exclude(Q())`?.. – Sayse Mar 09 '16 at 14:11
  • @Sayse I can see how that would work. However, I want a Q object that always is False, so that I can use it later to combine with other Q objects with AND and OR logic. – Flimm Mar 09 '16 at 14:15
  • It might help if you could show a [mcve] of exactly what it is you're looking for – Sayse Mar 09 '16 at 14:19
  • Why would you want a query that always evaluates to False instead of using `False` itself? – dnaranjo Mar 09 '16 at 14:23
  • @Sayse: I already have in the question post. I want a Q object XXXX such that `Foobar.objects.filter(XXX)` always returns a queryset that gives zero results. – Flimm Mar 09 '16 at 14:29
  • I'm not sure I fully understand the question... I can't see how this `false` object will be any different than ignoring this parameter altogether and constructing a specific query. Although, Alasdairs answer appears to provide a valid solution – Sayse Mar 09 '16 at 14:37
  • @Flimm This behaviour doesn't seem [natural](https://wiki.python.org/moin/TOOWTDI). At some point you have to decide whether to add the filter or not and then you can just return your response. – dnaranjo Mar 09 '16 at 14:39
  • 1
    It's pretty common pattern in my experience. Outside of ORM-land, I've seen `1=0` and `1=1` used for this purpose in MySQL queries. It seems silly that I can't do this simply in Django. – Flimm Mar 09 '16 at 14:41

3 Answers3

17

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))
[]
Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • 5
    It should be noted that "it doesn't work as you might expect when OR'd with an empty `Q()`" not because of limitations of the `Q(pk=None)` approach, but simply because `Q()` is not a "True Q object". It's "an empty Q object". It doesn't add any new information to the system. It doesn't change anything. `Q()|[anything]` is equivalent to `[anything]`. – Ludwik Trammer Mar 09 '16 at 14:34
  • 1
    @LudwikTrammer that's a good explanation of why it doesn't work. So I think a possible solution might be to use `Q(pk__isnull=False)` instead of `Q()` for the 'True Q object'. – Alasdair Mar 09 '16 at 14:42
  • 2
    This answer probably shouldn't be accepted over the `Q(pk__in=[])` answers. The Django optimizer (correctly) does not recognize `Q(pk__isnull=True)` or `Q(pk=None)` as expressions that always return empty. The `pk` field can be overridden, so it isn't a safe assumption. This results in messier queries than the other answers. – Jonathan Richards Jan 14 '20 at 12:33
15

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?

Sam Mason
  • 15,216
  • 1
  • 41
  • 60
12

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]: []
fwip
  • 395
  • 3
  • 6
  • When it is just a comment, dont misuse the answer button. So maybe you want to improve a bit more ... to turn this into a real answer. – GhostCat Sep 14 '17 at 17:47
  • Okay, but Sam answered the question first, I just had an addendum to his answer. I don't really know what you mean by "improve a bit more" - my reputation? My knowledge? I just want to add a helpful tidbit so other people can benefit. – fwip Sep 14 '17 at 20:08
  • 2
    It's nice to note that the optimizer will correctly simplify complex expressions. Queries for `( Q(pk__in=[]) & Q(foo="bar") ) | Q(hello="world")` will simplify the condition to `WHERE "hello" = world`. It also works with tilde `~` negations. – Jonathan Richards Jan 14 '20 at 11:52