ソースを参照

Fixed #31377 -- Disabled grouping by aliases on QuerySet.values()/values_list() when they collide with field names.

Regression in fb3f034f1c63160c0ff13c609acd01c18be12f80.

Thanks Holovashchenko Vadym for the report.
Hasan Ramezani 5 年 前
コミット
10866a10fe

+ 14 - 1
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)

+ 4 - 0
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`).

+ 1 - 0
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

+ 16 - 0
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,