Opened 14 years ago
Closed 14 years ago
#16409 closed Bug (fixed)
`defer()` and `only()` don't play nice with `annotate()`
| Reported by: | Tai Lee | Owned by: | nobody |
|---|---|---|---|
| Component: | GIS | Version: | dev |
| Severity: | Normal | Keywords: | annotate defer only count |
| Cc: | petr.gorodechnyj@… | Triage Stage: | Accepted |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
When adding defer() or only() to an existing query that used annotate() to cut out some fields, I started getting IndexError exceptions.
At first, I thought it was due to aggregate_start not being reduced by the number of deferred fields in QuerySet.iterator(), but when I looked at the SQL being generated I saw that ALL fields were being dropped from the query, except for the annotation. I suspect that even if this is fixed, aggregate_start will also need to be reduced by the number of deferred fields.
Here's the traceback:
>>> from django.db.models import Count
>>> from django.contrib.auth.models import *
>>> from django.contrib.admin.models import *
>>> User.objects.annotate(Count('logentry'))
[<User: manager>, <User: trak:1:10>, <User: admin>, <User: trak:3:5>, <User: trak:1:24>, <User: trak:3:9>, <User: trak:1:3>, <User: trak:4:11>, <User: trak:5:21>, <User: trak:1:14>, <User: trak:1:15>, <User: trak:1:2>, <User: trak:4:18>, <User: trak:4:19>, <User: trak:5:17>, <User: trak:3:6>, <User: trak:1:23>, <User: trak:3:16>, <User: trak:5:22>, <User: trak:4:12>, '...(remaining elements truncated)...']
>>> User.objects.annotate(Count('logentry')).defer('first_name')
Traceback (most recent call last):
File "<console>", line 1, in <module>
File "/Users/mrmachine/myproject/django/db/models/query.py", line 69, in __repr__
data = list(self[:REPR_OUTPUT_SIZE + 1])
File "/Users/mrmachine/myproject/django/db/models/query.py", line 84, in __len__
self._result_cache.extend(self._iter)
File "/Users/mrmachine/myproject/django/db/models/query.py", line 300, in iterator
setattr(obj, aggregate, row[i+aggregate_start])
IndexError: tuple index out of range
Here's the generated SQL:
>>> str(User.objects.annotate(Count('logentry')).query)
'SELECT "auth_user"."id", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."password", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."is_superuser", "auth_user"."last_login", "auth_user"."date_joined", COUNT("django_admin_log"."id") AS "logentry__count" FROM "auth_user" LEFT OUTER JOIN "django_admin_log" ON ("auth_user"."id" = "django_admin_log"."user_id") GROUP BY "auth_user"."id", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."password", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."is_superuser", "auth_user"."last_login", "auth_user"."date_joined"'
>>> str(User.objects.annotate(Count('logentry')).defer('first_name').query)
'SELECT COUNT("django_admin_log"."id") AS "logentry__count" FROM "auth_user" LEFT OUTER JOIN "django_admin_log" ON ("auth_user"."id" = "django_admin_log"."user_id") GROUP BY "auth_user"."id", "auth_user"."username", "auth_user"."first_name", "auth_user"."last_name", "auth_user"."email", "auth_user"."password", "auth_user"."is_staff", "auth_user"."is_active", "auth_user"."is_superuser", "auth_user"."last_login", "auth_user"."date_joined"'
Attachments (3)
Change History (12)
comment:1 by , 14 years ago
| Has patch: | set |
|---|---|
| Patch needs improvement: | set |
comment:2 by , 14 years ago
| Patch needs improvement: | unset |
|---|
Updated patch to include a fix. Hopefully good to go.
by , 14 years ago
| Attachment: | 16409-defer-only-annotate-r16510.diff added |
|---|
Failing test case and fix.
comment:3 by , 14 years ago
Updated tests to use assertIsInstance. The test checks that the queryset can be successfully iterated through without raising an exception by converting it to a list.
comment:6 by , 14 years ago
| Cc: | added |
|---|---|
| Resolution: | fixed |
| Status: | closed → reopened |
OK, you did that all right, but now do the same for the GeoQuerySet please. If I call GeoManager's defer(), only() or values() I get the same problem that was fixed in the patch above.
by , 14 years ago
| Attachment: | 16409.diff added |
|---|
comment:7 by , 14 years ago
| Triage Stage: | Unreviewed → Accepted |
|---|
Indeed — I just attached a failing test case.
comment:8 by , 14 years ago
| Component: | Database layer (models, ORM) → GIS |
|---|
Added a patch with a failing test case.
Creating test database for alias 'default'... Creating test database for alias 'other'... E ====================================================================== ERROR: test_defer (modeltests.defer.tests.DeferTests) ---------------------------------------------------------------------- Traceback (most recent call last): File "/Users/mrmachine/Subversion/django/tests/modeltests/defer/tests.py", line 141, in test_defer self.assertEqual(len(Secondary.objects.annotate(Count('primary')).defer('first')), 1) File "/Users/mrmachine/Subversion/django/django/db/models/query.py", line 82, in __len__ self._result_cache = list(self.iterator()) File "/Users/mrmachine/Subversion/django/django/db/models/query.py", line 300, in iterator setattr(obj, aggregate, row[i+aggregate_start]) IndexError: tuple index out of range ---------------------------------------------------------------------- Ran 1 test in 0.120s FAILED (errors=1) Destroying test database for alias 'default'... Destroying test database for alias 'other'...