[Django] #23589: Django 1.7 filter Q m2m bug

18 views
Skip to first unread message

Django

unread,
Oct 3, 2014, 2:16:15 AM10/3/14
to django-...@googlegroups.com
#23589: Django 1.7 filter Q m2m bug
----------------------------------------------+--------------------
Reporter: Grafumbly | Owner: nobody
Type: Bug | Status: new
Component: Database layer (models, ORM) | Version: 1.7
Severity: Normal | Keywords:
Triage Stage: Unreviewed | Has patch: 0
Easy pickings: 0 | UI/UX: 0
----------------------------------------------+--------------------
The behavior of Q objects has changed in an undocumented and breaking way
for m2m relationships in Django 1.7 from the behavior in 1.6.7, returning
different query results depending on the Django version.

In the case where you want to select a primary object that has related
objects of one type, but none of another, you could use a Q like this:
`q = Q(Q(relatedobject__someparam=True),
~Q(relatedobject__someparam=False))`
`results = PrimaryObject.objects.filter(q)`

Using this Q in a filter in 1.6.7 would give you all primary objects that
have relatedobjects with someparam=True and omit any primary objects that
also had a relatedobject with someparam=False. In 1.7 these objects are
returned despite the ~Q(relatedobject__someparam=False).

This gist provides a sample to illustrate the problem:
https://gist.github.com/scottsexton/375f7869839a98593695

--
Ticket URL: <https://code.djangoproject.com/ticket/23589>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

Django

unread,
Oct 3, 2014, 7:47:55 AM10/3/14
to django-...@googlegroups.com
#23589: Django 1.7 filter Q m2m bug
-------------------------------------+-------------------------------------

Reporter: Grafumbly | Owner: nobody
Type: Bug | Status: new
Component: Database layer | Version: 1.7
(models, ORM) | Resolution:
Severity: Normal | Triage Stage:
Keywords: | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by timgraham):

* needs_better_patch: => 0
* needs_tests: => 0
* needs_docs: => 0


Old description:

> The behavior of Q objects has changed in an undocumented and breaking way
> for m2m relationships in Django 1.7 from the behavior in 1.6.7, returning
> different query results depending on the Django version.
>
> In the case where you want to select a primary object that has related
> objects of one type, but none of another, you could use a Q like this:
> `q = Q(Q(relatedobject__someparam=True),
> ~Q(relatedobject__someparam=False))`
> `results = PrimaryObject.objects.filter(q)`
>
> Using this Q in a filter in 1.6.7 would give you all primary objects that
> have relatedobjects with someparam=True and omit any primary objects that
> also had a relatedobject with someparam=False. In 1.7 these objects are
> returned despite the ~Q(relatedobject__someparam=False).
>
> This gist provides a sample to illustrate the problem:
> https://gist.github.com/scottsexton/375f7869839a98593695

New description:

The behavior of Q objects has changed in an undocumented and breaking way
for m2m relationships in Django 1.7 from the behavior in 1.6.7, returning
different query results depending on the Django version.

In the case where you want to select a primary object that has related
objects of one type, but none of another, you could use a Q like this:
`q = Q(Q(relatedobject__someparam=True),
~Q(relatedobject__someparam=False))`
`results = PrimaryObject.objects.filter(q)`

Using this Q in a filter in 1.6.7 would give you all primary objects that
have relatedobjects with someparam=True and omit any primary objects that
also had a relatedobject with someparam=False. In 1.7 these objects are

returned despite the `~Q(relatedobject__someparam=False)`.

This gist provides a sample to illustrate the problem:
https://gist.github.com/scottsexton/375f7869839a98593695

--

Comment:

It would be helpful if you could create a test for Django's test suite
that demonstrates the issue (ideally reusing existing models if you can
find appropriate ones) and bisect to the commit that introduced the
regression.

--
Ticket URL: <https://code.djangoproject.com/ticket/23589#comment:1>

Django

unread,
Oct 7, 2014, 9:39:01 AM10/7/14
to django-...@googlegroups.com
#23589: Django 1.7 filter Q m2m bug
-------------------------------------+-------------------------------------
Reporter: Grafumbly | Owner: nobody
Type: Bug | Status: closed

Component: Database layer | Version: 1.7
(models, ORM) | Resolution: invalid

Severity: Normal | Triage Stage:
Keywords: | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------
Changes (by akaariai):

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


Comment:

The interpretation of the query is that PrimaryObject must have a *single*
relatedobject row for which (someparam=True AND NOT someparam=False).
Obviously if someparam=True, then someparam is also not False for that
row. The 1.7 results seem correct to me. (See
https://docs.djangoproject.com/en/1.7/topics/db/queries/#spanning-multi-
valued-relationships for how Django handles filters to multivalued
relationships).

I believe you will get correct results by using
`.filter(Q(relatedobject__someparam=True)).filter(~Q(relatedobject__someparam=False))`.

--
Ticket URL: <https://code.djangoproject.com/ticket/23589#comment:2>

Django

unread,
Oct 7, 2014, 12:43:35 PM10/7/14
to django-...@googlegroups.com
#23589: Django 1.7 filter Q m2m bug
-------------------------------------+-------------------------------------
Reporter: Grafumbly | Owner: nobody

Type: Bug | Status: closed
Component: Database layer | Version: 1.7
(models, ORM) | Resolution: invalid
Severity: Normal | Triage Stage:
Keywords: | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by Grafumbly):

The documentation hasn't changed but the behavior has. If this is working
as intended in 1.7, is it a bug in 1.6 or does the documentation just need
to be updated?

--
Ticket URL: <https://code.djangoproject.com/ticket/23589#comment:3>

Django

unread,
Oct 8, 2014, 2:46:10 AM10/8/14
to django-...@googlegroups.com
#23589: Django 1.7 filter Q m2m bug
-------------------------------------+-------------------------------------
Reporter: Grafumbly | Owner: nobody

Type: Bug | Status: closed
Component: Database layer | Version: 1.7
(models, ORM) | Resolution: invalid
Severity: Normal | Triage Stage:
Keywords: | Unreviewed
Has patch: 0 | Needs documentation: 0
Needs tests: 0 | Patch needs improvement: 0

Easy pickings: 0 | UI/UX: 0
-------------------------------------+-------------------------------------

Comment (by akaariai):

Yes, this was a bug in 1.6. This is an unfortunate bug, as you might have
gotten correct results. That is, the query worked, but didn't match the
documentation. The query produced results for
`.filter(Q(relatedobject__someparam=True)).filter(~Q(relatedobject__someparam=False))`.

--
Ticket URL: <https://code.djangoproject.com/ticket/23589#comment:4>

Reply all
Reply to author
Forward
0 new messages