From e6285cac83b2aa255c9611a6536a92d10d890842 Mon Sep 17 00:00:00 2001 From: Mariusz Felisiak Date: Thu, 9 Jul 2020 11:55:03 +0200 Subject: [PATCH] [3.1.x] Fixed #31773 -- Fixed preserving output_field in ExpressionWrapper for combined expressions. Thanks Thodoris Sotiropoulos for the report and Simon Charette for the implementation idea. Regression in df32fd42b84cc6dbba173201f244491b0d154a63. Backport of 8a6df55f2dd5131282084a4edfd48f63fbf8c69a from master --- django/db/models/expressions.py | 7 +++---- tests/annotations/tests.py | 12 ++++++++++-- 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 8d7d72a52a..4c126edf96 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -857,9 +857,6 @@ class ExpressionWrapper(Expression): def __init__(self, expression, output_field): super().__init__(output_field=output_field) - if getattr(expression, '_output_field_or_none', True) is None: - expression = expression.copy() - expression.output_field = output_field self.expression = expression def set_source_expressions(self, exprs): @@ -869,7 +866,9 @@ class ExpressionWrapper(Expression): return [self.expression] def get_group_by_cols(self, alias=None): - return self.expression.get_group_by_cols(alias=alias) + expression = self.expression.copy() + expression.output_field = self.output_field + return expression.get_group_by_cols(alias=alias) def as_sql(self, compiler, connection): return self.expression.as_sql(compiler, connection) diff --git a/tests/annotations/tests.py b/tests/annotations/tests.py index 9f35074934..aa3682b86b 100644 --- a/tests/annotations/tests.py +++ b/tests/annotations/tests.py @@ -6,8 +6,8 @@ from django.core.exceptions import FieldDoesNotExist, FieldError from django.db import connection from django.db.models import ( BooleanField, Case, CharField, Count, DateTimeField, Exists, - ExpressionWrapper, F, Func, IntegerField, Max, NullBooleanField, OuterRef, - Q, Subquery, Sum, Value, When, + ExpressionWrapper, F, FloatField, Func, IntegerField, Max, + NullBooleanField, OuterRef, Q, Subquery, Sum, Value, When, ) from django.db.models.expressions import RawSQL from django.db.models.functions import Length, Lower @@ -178,6 +178,14 @@ class NonAggregateAnnotationTestCase(TestCase): self.assertEqual(book.combined, 12) self.assertEqual(book.rating_count, 1) + def test_combined_f_expression_annotation_with_aggregation(self): + book = Book.objects.filter(isbn='159059725').annotate( + combined=ExpressionWrapper(F('price') * F('pages'), output_field=FloatField()), + rating_count=Count('rating'), + ).first() + self.assertEqual(book.combined, 13410.0) + self.assertEqual(book.rating_count, 1) + 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'))