diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 78c4f47b5b..9fe0c9a656 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -1927,6 +1927,19 @@ class Query(BaseExpression): primary key, and the query would be equivalent, the optimization will be made automatically. """ + # Column names from JOINs to check collisions with aliases. + if allow_aliases: + column_names = set() + seen_models = set() + for join in list(self.alias_map.values())[1:]: # Skip base table. + model = join.join_field.related_model + if model not in seen_models: + column_names.update({ + field.column + for field in model._meta.local_concrete_fields + }) + seen_models.add(model) + group_by = list(self.select) if self.annotation_select: for alias, annotation in self.annotation_select.items(): @@ -1940,7 +1953,7 @@ class Query(BaseExpression): warnings.warn(msg, category=RemovedInDjango40Warning) group_by_cols = annotation.get_group_by_cols() else: - if not allow_aliases: + if not allow_aliases or alias in column_names: alias = None group_by_cols = annotation.get_group_by_cols(alias=alias) group_by.extend(group_by_cols) diff --git a/docs/releases/3.0.5.txt b/docs/releases/3.0.5.txt index 43b7bfe08f..423ec809cd 100644 --- a/docs/releases/3.0.5.txt +++ b/docs/releases/3.0.5.txt @@ -11,3 +11,7 @@ Bugfixes * Added the ability to handle ``.po`` files containing different plural equations for the same language (:ticket:`30439`). + +* Fixed a regression in Django 3.0 where ``QuerySet.values()`` and + ``values_list()`` crashed if a queryset contained an aggregation and + ``Subquery()`` annotation that collides with a field name (:ticket:`31377`). diff --git a/tests/aggregation/models.py b/tests/aggregation/models.py index fd441fe51d..cfc261abcc 100644 --- a/tests/aggregation/models.py +++ b/tests/aggregation/models.py @@ -5,6 +5,7 @@ class Author(models.Model): name = models.CharField(max_length=100) age = models.IntegerField() friends = models.ManyToManyField('self', blank=True) + rating = models.FloatField(null=True) def __str__(self): return self.name diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py index 95b77ef87b..a8377c9a26 100644 --- a/tests/aggregation/tests.py +++ b/tests/aggregation/tests.py @@ -1191,6 +1191,22 @@ class AggregateTestCase(TestCase): }, ]) + def test_aggregation_subquery_annotation_values_collision(self): + books_rating_qs = Book.objects.filter( + publisher=OuterRef('pk'), + price=Decimal('29.69'), + ).values('rating') + publisher_qs = Publisher.objects.filter( + book__contact__age__gt=20, + name=self.p1.name, + ).annotate( + rating=Subquery(books_rating_qs), + contacts_count=Count('book__contact'), + ).values('rating').annotate(total_count=Count('rating')) + self.assertEqual(list(publisher_qs), [ + {'rating': 4.0, 'total_count': 2}, + ]) + @skipUnlessDBFeature('supports_subqueries_in_group_by') @skipIf( connection.vendor == 'mysql' and 'ONLY_FULL_GROUP_BY' in connection.sql_mode,