Opened 11 years ago

Closed 11 years ago

Last modified 11 years ago

#19848 closed Bug (invalid)

ANDed Q filters spanning generic relations evaluate unexpectedly to empty querie sets

Reported by: gotoalanlu@… Owned by: nobody
Component: contrib.contenttypes Version: dev
Severity: Normal Keywords: generic genericrelations filter q and
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

See this StackOverflow question for some context. There is a answer for a workaround, but this feels a lot less natural than fixing the SQL—in order to do more complex filter queries involving OR and NOT, one would have to programmatically use De Morgan's laws to deconstruct the query into ANDed ANDless subqueries, IIRC.

Change History (6)

comment:1 by anonymous, 11 years ago

Summary: ANDed Q filters spanning generic relations evaluate unexpectedly to empty queriesANDed Q filters spanning generic relations evaluate unexpectedly to empty querie sets

comment:2 by anonymous, 11 years ago

Summary: ANDed Q filters spanning generic relations evaluate unexpectedly to empty querie setsANDed Q filters spanning generic relations evaluate unexpectedly to empty query sets

comment:3 by anonymous, 11 years ago

Needs documentation: set
Needs tests: set

comment:4 by Anssi Kääriäinen, 11 years ago

Needs documentation: unset
Needs tests: unset
Resolution: invalid
Status: newclosed
Summary: ANDed Q filters spanning generic relations evaluate unexpectedly to empty query setsANDed Q filters spanning generic relations evaluate unexpectedly to empty querie sets

Django is working as designed here. filters inside a single .filter() call target the same join. If you want multiple joins, then you will need to use multiple .filter() calls.

Really, this is not a bug. The API is defined that all the conditions inside single filter() target the same join, with multiple filter()s you get multiple joins. That you can workaround this by negated joins might contain a bug though...

comment:5 by Alan Lu <gotoalanlu@…>, 11 years ago

Apologies: my original post contained an error. I meant to refer to the distributive property of AND and OR. Therefore, in my original line of thought on the workaround, queries like

query = (Q(objs__name = "foo") & ~Q(objs__name = "bar")) | Q(objs__name = "baz")
ExampleModel.filter(query)

would instead be written as

query1 = Q(objs__name = "foo") | Q(objs__name = "baz")
query2 = ~Q(objs__name = "bar") | Q(objs__name = "baz")
ExampleModel.filter(query1).filter(query2)

though DeMorgan's laws could certainly still be useful when unwrapping deeply nested logical statements with NOTs on parenthetical groups.

The intent of the code is to query for instances of a model with either a relationship with an object "foo" and no relationship with an object "bar" or a relationship with an object "baz."

Because I was unaware of the way SQL handles these relationships, I misjudged the capacity of the system, so it appears that the workaround is natural. Having read about SQL joins, I am now convinced that Django is working as designed, even though I am a bit surprised.

comment:6 by Alan Lu <gotoalanlu@…>, 11 years ago

Just in case somebody in the future misses things like I do, here is another StackOverflow question which addresses why this is right and good for M2M/generic relationships. As it is now, the system is perfectly capable.

Note: See TracTickets for help on using tickets.
Back to Top