#10045 closed Cleanup/optimization (fixed)
Improve documentation of .annotate() / .filter() ordering quirks
Reported by: | Owned by: | ||
---|---|---|---|
Component: | Documentation | Version: | dev |
Severity: | Normal | Keywords: | |
Cc: | Triage Stage: | Accepted | |
Has patch: | yes | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description (last modified by )
The documentation surrounding the ordering of .annotate() and .filter() clauses needs improvement. The current documentation is confusing (and arguably incorrect), leading to invalid bug reports like the following:
---
I get something that looks like a missing join condition when doing
ModelA.objects.all().annotate(num_b=Count('modelb')).filter(modelb__somebool=True)[0].num_b
I get num_b = 2^i
for i objects of ModelB instead of num_b=i
as I would expect to.
Please see attached models.py file for the Model definition and test case.
django revision: 9756
---
Attachments (2)
Change History (13)
by , 16 years ago
comment:1 by , 16 years ago
Resolution: | → invalid |
---|---|
Status: | new → closed |
comment:2 by , 16 years ago
I did read the docs, but apparently didn't understand them: They say: "annotation in the first query will provide the total number of all books published by the publisher;" that would map to the total number of B objects in my example, wouldn't it? And that is not 4. Also: "In the first query, the annotation precedes the filter, so the filter has no effect on the annotation".
Remove the filter, and the expected result is returned:
>>> ModelA.objects.all().annotate(num_b=Count('modelb'))[0].num_b 2
Note that somebool is True on all ModelB objects, so the filter should really have no effect, right?
comment:3 by , 16 years ago
Component: | ORM aggregation → Documentation |
---|---|
Description: | modified (diff) |
Resolution: | invalid |
Status: | closed → reopened |
Summary: | bug in aggregations: .annotate() followed by .filter() on related field breaks → Improve documentation of .annotate() / .filter() ordering quirks |
Triage Stage: | Unreviewed → Accepted |
In this case, I am confirming that the query is returning the correct results. The order of filter() and annotate() is significant, and the result you have received is what should be expected.
However, I will concede that the documentation of this quirk could certainly be improved. I'll reopen this ticket, but I'll repurpose it to clarify the documentation of this feature since there isn't anything at a code level that requires fixing. If you (or anyone else) wants to take a swing at describing this quirk better, feel free to do so.
comment:4 by , 14 years ago
Severity: | → Normal |
---|---|
Type: | → Cleanup/optimization |
comment:5 by , 13 years ago
Easy pickings: | unset |
---|---|
UI/UX: | unset |
This seems more than documenting a "quirk". The very example given in the docs is broken.
by , 13 years ago
Attachment: | test_10045.tar.gz added |
---|
self-contained simple project - run manage.py test
comment:6 by , 13 years ago
(anon was me)
In the project test, I create two Publisher
s, and assign three Book
s to the first publisher, two above a rating of 3.0
. Running Publisher.objects.annotate(num_books=Count('book')).filter(book__rating__gt=3.0)[0].num_books
returns 6.
comment:7 by , 12 years ago
Status: | reopened → new |
---|
comment:8 by , 9 years ago
Has patch: | set |
---|
PR -- I could use some advice from ORM experts about how to explain the actual behavior. I'm not sure if it might be a bug that we can fix later or if we should simply say "don't do that."
I think you may have missed this section of the docs:
There is a difference between:
which evaluates as the SQL:
and the query:
which evaluates as the SQL:
Notice the order of the annotate() and filter() clauses. By putting the filter after the annotate, the annotation uses a separate join table. When the annotate follows the filter, the annotation uses the filter's join table.