#19848 closed Bug (invalid)
ANDed Q filters spanning generic relations evaluate unexpectedly to empty querie sets
Reported by: | 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 , 12 years ago
Summary: | ANDed Q filters spanning generic relations evaluate unexpectedly to empty queries → ANDed Q filters spanning generic relations evaluate unexpectedly to empty querie sets |
---|
comment:2 by , 12 years ago
Summary: | ANDed Q filters spanning generic relations evaluate unexpectedly to empty querie sets → ANDed Q filters spanning generic relations evaluate unexpectedly to empty query sets |
---|
comment:3 by , 12 years ago
Needs documentation: | set |
---|---|
Needs tests: | set |
comment:4 by , 12 years ago
Needs documentation: | unset |
---|---|
Needs tests: | unset |
Resolution: | → invalid |
Status: | new → closed |
Summary: | ANDed Q filters spanning generic relations evaluate unexpectedly to empty query sets → ANDed Q filters spanning generic relations evaluate unexpectedly to empty querie sets |
comment:5 by , 12 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 , 12 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.
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...