From fe9c7ded2996364f853c524b4421274717d89d5f Mon Sep 17 00:00:00 2001 From: Hasan Ramezani Date: Tue, 17 Nov 2020 23:31:58 +0100 Subject: [PATCH] Fixed #32200 -- Fixed grouping by ExpressionWrapper() with Q objects. Thanks Gordon Wrigley for the report. Regression in df32fd42b84cc6dbba173201f244491b0d154a63. --- django/db/models/expressions.py | 10 +++++++--- docs/releases/3.1.4.txt | 3 +++ tests/annotations/tests.py | 12 ++++++++++++ 3 files changed, 22 insertions(+), 3 deletions(-) diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 97045d2f49..48f295941d 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -915,9 +915,13 @@ class ExpressionWrapper(Expression): return [self.expression] def get_group_by_cols(self, alias=None): - expression = self.expression.copy() - expression.output_field = self.output_field - return expression.get_group_by_cols(alias=alias) + if isinstance(self.expression, Expression): + expression = self.expression.copy() + expression.output_field = self.output_field + return expression.get_group_by_cols(alias=alias) + # For non-expressions e.g. an SQL WHERE clause, the entire + # `expression` must be included in the GROUP BY clause. + return super().get_group_by_cols() def as_sql(self, compiler, connection): return compiler.compile(self.expression) diff --git a/docs/releases/3.1.4.txt b/docs/releases/3.1.4.txt index ca75d303b9..4d9e949791 100644 --- a/docs/releases/3.1.4.txt +++ b/docs/releases/3.1.4.txt @@ -21,3 +21,6 @@ Bugfixes * Fixed a regression in Django 3.1 that caused a crash of auto-reloader for certain invocations of ``runserver`` on Windows with Python 3.7 and below (:ticket:`32202`). + +* Fixed a regression in Django 3.1 that caused the incorrect grouping by a + ``Q`` object annotation (:ticket:`32200`). diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py index 5bd7d572df..f8346d801e 100644 --- a/tests/annotations/tests.py +++ b/tests/annotations/tests.py @@ -195,6 +195,18 @@ class NonAggregateAnnotationTestCase(TestCase): self.assertEqual(book.isnull_pubdate, False) self.assertEqual(book.rating_count, 1) + @skipUnlessDBFeature('supports_boolean_expr_in_select_clause') + def test_grouping_by_q_expression_annotation(self): + authors = Author.objects.annotate( + under_40=ExpressionWrapper(Q(age__lt=40), output_field=BooleanField()), + ).values('under_40').annotate( + count_id=Count('id'), + ).values('under_40', 'count_id') + self.assertCountEqual(authors, [ + {'under_40': False, 'count_id': 3}, + {'under_40': True, 'count_id': 6}, + ]) + def test_aggregate_over_annotation(self): agg = Author.objects.annotate(other_age=F('age')).aggregate(otherage_sum=Sum('other_age')) other_agg = Author.objects.aggregate(age_sum=Sum('age'))