summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHasan Ramezani <hasan.r67@gmail.com>2020-03-19 19:54:36 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-03-25 09:49:51 +0100
commit72652bcb1b29710d23c3e6f872046d4aedc58665 (patch)
tree0e789a048ed59e44547017f7e0e8e1a15e0ec48b
parent600d7d86931e314837591a227c834803f2b4994b (diff)
[3.0.x] 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. Backport of 10866a10fe9f0ad3ffdf6f93823aaf4716e6f27c from master
-rw-r--r--django/db/models/sql/query.py15
-rw-r--r--docs/releases/3.0.5.txt4
-rw-r--r--tests/aggregation/models.py1
-rw-r--r--tests/aggregation/tests.py16
4 files changed, 35 insertions, 1 deletions
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index fc1b916812..0e6828b0f1 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -1931,6 +1931,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():
@@ -1945,7 +1958,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 342507da01..8791221e6b 100644
--- a/tests/aggregation/tests.py
+++ b/tests/aggregation/tests.py
@@ -1190,6 +1190,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',