[1.5.x] Fixed #16436 -- defer + annotate + select_related crash

Correctly calculate the ``aggregate_start`` offset from loaded fields,
if any are deferred, instead of ``self.query.select`` which includes all
fields on the model.

Backpatch of 69f7db153d from master.
This commit is contained in:
Tai Lee 2013-05-30 08:16:09 +03:00 committed by Anssi Kääriäinen
parent f8393edb52
commit b495c24375
3 changed files with 37 additions and 2 deletions

View File

@ -775,7 +775,8 @@ class SQLCompiler(object):
for rows in self.execute_sql(MULTI): for rows in self.execute_sql(MULTI):
for row in rows: for row in rows:
if has_aggregate_select: if has_aggregate_select:
aggregate_start = len(self.query.extra_select) + len(self.query.select) loaded_fields = self.query.get_loaded_field_names().get(self.query.model, set()) or self.query.select
aggregate_start = len(self.query.extra_select) + len(loaded_fields)
aggregate_end = aggregate_start + len(self.query.aggregate_select) aggregate_end = aggregate_start + len(self.query.aggregate_select)
if resolve_columns: if resolve_columns:
if fields is None: if fields is None:

View File

@ -28,3 +28,22 @@ class BigChild(Primary):
class ChildProxy(Child): class ChildProxy(Child):
class Meta: class Meta:
proxy=True proxy=True
class Profile(models.Model):
profile1 = models.TextField(default='profile1')
class Location(models.Model):
location1 = models.TextField(default='location1')
class Item(models.Model):
pass
class Request(models.Model):
profile = models.ForeignKey(Profile, null=True, blank=True)
location = models.ForeignKey(Location)
items = models.ManyToManyField(Item)
request1 = models.TextField(default='request1')
request2 = models.TextField(default='request2')
request3 = models.TextField(default='request3')
request4 = models.TextField(default='request4')

View File

@ -1,9 +1,10 @@
from __future__ import absolute_import from __future__ import absolute_import
from django.db.models import Count
from django.db.models.query_utils import DeferredAttribute, InvalidQuery from django.db.models.query_utils import DeferredAttribute, InvalidQuery
from django.test import TestCase from django.test import TestCase
from .models import Secondary, Primary, Child, BigChild, ChildProxy from .models import Secondary, Primary, Child, BigChild, ChildProxy, Location, Request
class DeferTests(TestCase): class DeferTests(TestCase):
@ -183,3 +184,17 @@ class DeferTests(TestCase):
with self.assertNumQueries(0): with self.assertNumQueries(0):
bc_deferred.id bc_deferred.id
self.assertEqual(bc_deferred.pk, bc_deferred.id) self.assertEqual(bc_deferred.pk, bc_deferred.id)
class DeferAnnotateSelectRelatedTest(TestCase):
def test_defer_annotate_select_related(self):
location = Location.objects.create()
Request.objects.create(location=location)
self.assertIsInstance(list(Request.objects
.annotate(Count('items')).select_related('profile', 'location')
.only('profile', 'location')), list)
self.assertIsInstance(list(Request.objects
.annotate(Count('items')).select_related('profile', 'location')
.only('profile__profile1', 'location__location1')), list)
self.assertIsInstance(list(Request.objects
.annotate(Count('items')).select_related('profile', 'location')
.defer('request1', 'request2', 'request3', 'request4')), list)