Commit b495c243 authored by Tai Lee's avatar Tai Lee Committed by Anssi Kääriäinen
Browse files

[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 69f7db15 from master.
parent f8393edb
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -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:
+19 −0
Original line number Diff line number Diff line
@@ -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')
+16 −1
Original line number Diff line number Diff line
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)