From 52545e788d664040abf4f1a5d77cdfc61152ffca Mon Sep 17 00:00:00 2001 From: can Date: Wed, 10 Jul 2019 15:07:48 +0300 Subject: [PATCH] Fixed #28289 -- Fixed crash of RawSQL annotations on inherited model fields. --- django/db/models/expressions.py | 10 ++++++++++ tests/annotations/models.py | 1 + tests/annotations/tests.py | 22 ++++++++++++++++++++++ 3 files changed, 33 insertions(+) diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index b757426440..bef93d36a2 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -699,6 +699,16 @@ class RawSQL(Expression): def get_group_by_cols(self, alias=None): return [self] + def resolve_expression(self, query=None, allow_joins=True, reuse=None, summarize=False, for_save=False): + # Resolve parents fields used in raw SQL. + for parent in query.model._meta.get_parent_list(): + for parent_field in parent._meta.local_fields: + _, column_name = parent_field.get_attname_column() + if column_name.lower() in self.sql.lower(): + query.resolve_ref(parent_field.name, allow_joins, reuse, summarize) + break + return super().resolve_expression(query, allow_joins, reuse, summarize, for_save) + class Star(Expression): def __repr__(self): diff --git a/tests/annotations/models.py b/tests/annotations/models.py index 4aeda3c093..cd3d6ddb87 100644 --- a/tests/annotations/models.py +++ b/tests/annotations/models.py @@ -38,6 +38,7 @@ class Store(models.Model): books = models.ManyToManyField(Book) original_opening = models.DateTimeField() friday_night_closing = models.TimeField() + area = models.IntegerField(null=True, db_column='surface') def __str__(self): return self.name diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py index af13915312..c39e8d3fbe 100644 --- a/tests/annotations/tests.py +++ b/tests/annotations/tests.py @@ -405,6 +405,28 @@ class NonAggregateAnnotationTestCase(TestCase): lambda a: (a['age'], a['age_count']) ) + def test_raw_sql_with_inherited_field(self): + DepartmentStore.objects.create( + name='Angus & Robinson', + original_opening=datetime.date(2014, 3, 8), + friday_night_closing=datetime.time(21), + chain='Westfield', + area=123, + ) + tests = ( + ('name', 'Angus & Robinson'), + ('surface', 123), + ("case when name='Angus & Robinson' then chain else name end", 'Westfield'), + ) + for sql, expected_result in tests: + with self.subTest(sql=sql): + self.assertSequenceEqual( + DepartmentStore.objects.annotate( + annotation=RawSQL(sql, ()), + ).values_list('annotation', flat=True), + [expected_result], + ) + def test_annotate_exists(self): authors = Author.objects.annotate(c=Count('id')).filter(c__gt=1) self.assertFalse(authors.exists())