#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 [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/074.0f8410eddd348955e01fb04edf02243c%40djangoproject.com.

Reply via email to