mirror of https://github.com/django/django.git
[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:
parent
f8393edb52
commit
b495c24375
|
@ -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:
|
||||||
|
|
|
@ -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')
|
||||||
|
|
|
@ -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)
|
||||||
|
|
Loading…
Reference in New Issue