[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 row in rows:
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)
if resolve_columns:
if fields is None:

View File

@ -28,3 +28,22 @@ class BigChild(Primary):
class ChildProxy(Child):
class Meta:
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 django.db.models import Count
from django.db.models.query_utils import DeferredAttribute, InvalidQuery
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):
@ -183,3 +184,17 @@ class DeferTests(TestCase):
with self.assertNumQueries(0):
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)