#34603: ~Q() incorrectly interpreted as full rather than empty
-------------------------------------+-------------------------------------
Reporter: Anders Kaseorg | Owner: nobody
Type: Bug | Status: new
Component: Database layer | Version: 4.2
(models, ORM) |
Severity: Normal | Resolution:
Keywords: | Triage Stage:
| Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0
Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by Anders Kaseorg):
* status: closed => new
* resolution: invalid =>
Comment:
That isn’t how logic works.
The expected meaning of `.filter(~Q(k_1=v_1, k_2=v_2, …, k_n=v_n))` is to
exclude those rows `r` such that for all `1 ≤ i ≤ n`, row `r` satisfies
`k_i=v_i`. If we plug in `n = 0`, then ''every'' row `r`
[https://en.wikipedia.org/wiki/Vacuous_truth vacuously] satisfies the
condition that for all `1 ≤ i ≤ n`, row `r` satisfies `k_i=v_i`, so every
row should be excluded.
----
Here’s an example. Consider this 3-column table with 8 rows:
||= **a** =||= **b** =||= **c** =||
|| 0 || 0 || 0 ||
|| 0 || 0 || 1 ||
|| 0 || 1 || 0 ||
|| 0 || 1 || 1 ||
|| 1 || 0 || 0 ||
|| 1 || 0 || 1 ||
|| 1 || 1 || 0 ||
|| 1 || 1 || 1 ||
Then `.filter(~Q(a=1, b=1, c=1))` gives 7 rows—those that don’t match `1 1
1`:
||= **a** =||= **b** =||= **c** =||
|| 0 || 0 || 0 ||
|| 0 || 0 || 1 ||
|| 0 || 1 || 0 ||
|| 0 || 1 || 1 ||
|| 1 || 0 || 0 ||
|| 1 || 0 || 1 ||
|| 1 || 1 || 0 ||
`.filter(~Q(a=1, b=1))` gives 6 rows—those that don’t match `1 1 *`:
||= **a** =||= **b** =||= **c** =||
|| 0 || 0 || 0 ||
|| 0 || 0 || 1 ||
|| 0 || 1 || 0 ||
|| 0 || 1 || 1 ||
|| 1 || 0 || 0 ||
|| 1 || 0 || 1 ||
`.filter(~Q(a=1))` gives 4 rows—those that don’t match `1 * *`:
||= **a** =||= **b** =||= **c** =||
|| 0 || 0 || 0 ||
|| 0 || 0 || 1 ||
|| 0 || 1 || 0 ||
|| 0 || 1 || 1 ||
and we should expect `.filter(~Q())` to give 0 rows—those that don’t match
`* * *`:
||= **a** =||= **b** =||= **c** =||
----
Another explanation comes from the invariant that the meaning of `Q(…)`
should be unchanged if we add a criterion that matches every row. For
example, in a table with an `id` column of nonnegative integers,
`.filter(~Q(k_1=v_1, k_2=v_2))` is equivalent to `.filter(~Q(k_1=v_1,
k_2=v_2, id__gte=0))`. So, similarly, `.filter(~Q())` should be equivalent
to `.filter(~Q(id__gte=0))`. Yet Django gives
{{{
>>> Client.objects.filter(~Q()).count()
12
>>> Client.objects.filter(~Q(id__gte=0)).count()
0
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/34603#comment:2>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/010701886be54886-bb5a02e6-73a8-467a-b986-2f8948855a36-000000%40eu-central-1.amazonses.com.