From d43f8478478c333ed8b7e499abef17a86363d61c Mon Sep 17 00:00:00 2001 From: Lex Berezhny Date: Tue, 31 Jan 2017 18:45:55 -0500 Subject: [PATCH] [1.11.x] Fixed #27800 -- Fixed QuerySet.annotate(Length(...)).distinct() crash. Backport of ac5f886c5610a6bca26dab10170b445d1e9df450 from master --- AUTHORS | 1 + django/db/models/sql/query.py | 7 ++++++- tests/annotations/tests.py | 7 ++++++- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/AUTHORS b/AUTHORS index dbc414f5a3..bf7c21be45 100644 --- a/AUTHORS +++ b/AUTHORS @@ -456,6 +456,7 @@ answer newbie questions, and generally made Django that much better: Leo Shklovskii Leo Soto lerouxb@gmail.com + Lex Berezhny Liang Feng limodou Loek van Gent diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 5f65786373..695e52cda7 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -1345,7 +1345,12 @@ class Query(object): "querying. If it is a GenericForeignKey, consider " "adding a GenericRelation." % name ) - model = field.model._meta.concrete_model + try: + model = field.model._meta.concrete_model + except AttributeError: + # QuerySet.annotate() may introduce fields that aren't + # attached to a model. + model = None else: # We didn't find the current field, so move position back # one step. diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py index b90286b9da..36e675402c 100644 --- a/tests/annotations/tests.py +++ b/tests/annotations/tests.py @@ -8,7 +8,7 @@ from django.db.models import ( BooleanField, CharField, Count, DateTimeField, ExpressionWrapper, F, Func, IntegerField, NullBooleanField, Q, Sum, Value, ) -from django.db.models.functions import Lower +from django.db.models.functions import Length, Lower from django.test import TestCase, skipUnlessDBFeature from django.utils import six @@ -208,6 +208,11 @@ class NonAggregateAnnotationTestCase(TestCase): ).distinct('test_alias') self.assertEqual(len(people2), 1) + lengths = Employee.objects.annotate( + name_len=Length('first_name'), + ).distinct('name_len').values_list('name_len', flat=True) + self.assertSequenceEqual(lengths, [3, 7, 8]) + def test_filter_annotation(self): books = Book.objects.annotate( is_book=Value(1, output_field=IntegerField())