#32549: Add `Q.empty()` to check for nested empty Q objects like `Q(Q())`
-------------------------------------+-------------------------------------
     Reporter:  jonathan-golorry     |                    Owner:  nobody
         Type:                       |                   Status:  new
  Cleanup/optimization               |
    Component:  Database layer       |                  Version:  dev
  (models, ORM)                      |
     Severity:  Normal               |               Resolution:
     Keywords:  Q objects, nested,   |             Triage Stage:
  empty                              |  Unreviewed
    Has patch:  1                    |      Needs documentation:  0
  Needs tests:  0                    |  Patch needs improvement:  0
Easy pickings:  0                    |                    UI/UX:  0
-------------------------------------+-------------------------------------
Changes (by jonathan-golorry):

 * status:  closed => new
 * resolution:  wontfix =>


Old description:

> Empty Q objects (`Q()` or `~Q()`, etc) evaluate to `False`, but `Q(Q())`
> evaluates to `True`. This interferes with the shortcut on logical
> operations involving empty Q objects and can lead to bugs when trying to
> detect empty operations.
> {{{
> def q_any(iterable):
>     q = Q()
>     for element in iterable:
>         q |= element
>     if q:
>         return q
>     return Q(pk__in=[])
> Item.objects.filter(q_any())  # no items
> Item.objects.filter(q_any([Q()]))  # no items
> Item.objects.filter(q_any([Q(Q())]))  # all items
> }}}
>
> Patch https://github.com/django/django/pull/14127 removes empty Q objects
> from `args` during Q object initialization.
>
> This requires https://code.djangoproject.com/ticket/32548 in order to
> prevent a regression in logical operations between query expressions and
> empty Q objects.

New description:

 Empty Q objects (`Q()` or `~Q()`, etc) evaluate to `False`, but `Q(Q())`
 evaluates to `True`. This interferes with the shortcut on logical
 operations involving empty Q objects and can lead to bugs when trying to
 detect empty operations.
 {{{
 def q_any(iterable):
     q = Q()
     for element in iterable:
         q |= element
     if q:
         return q
     return Q(pk__in=[])
 Item.objects.filter(q_any())  # no items
 Item.objects.filter(q_any([Q()]))  # no items
 Item.objects.filter(q_any([Q(Q())]))  # all items
 }}}

 ~~Patch https://github.com/django/django/pull/14127 removes empty Q
 objects from `args` during Q object initialization.~~

 Patch https://github.com/django/django/pull/14130 adds `.empty()` for
 detecting nested empty Q objects and uses that instead of boolean
 evaluation for shortcutting logical operations between empty Q objects. So
 `Q(x=1) | Q(Q())` will now shortcut to `Q(x=1)` the same way `Q(x=1) |
 Q()` does. No simplifying is done during Q object initialization.

 This requires https://code.djangoproject.com/ticket/32548 in order to
 prevent a regression in logical operations between query expressions and
 empty Q objects.

--

Comment:

 Here is an alternate approach that adds a way to check for nested empty Q
 objects instead of making them impossible to create.

-- 
Ticket URL: <https://code.djangoproject.com/ticket/32549#comment:3>
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 django-updates+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/074.0f8410eddd348955e01fb04edf02243c%40djangoproject.com.

Reply via email to