From 058747b77a3e6710301138b8ab3bc4256077fdfa Mon Sep 17 00:00:00 2001 From: David Wobrock Date: Sun, 27 Sep 2020 15:47:27 +0200 Subject: [PATCH] Fixed #31880 -- Made QuerySet.aggregate() raise FieldError when aggregating over aggregation aliases. --- django/db/models/query.py | 12 ++++++++++-- tests/aggregation/tests.py | 10 ++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/django/db/models/query.py b/django/db/models/query.py index 6c78fbc4b3..8d888447cd 100644 --- a/django/db/models/query.py +++ b/django/db/models/query.py @@ -17,7 +17,7 @@ from django.db import ( from django.db.models import AutoField, DateField, DateTimeField, sql from django.db.models.constants import LOOKUP_SEP from django.db.models.deletion import Collector -from django.db.models.expressions import Case, Expression, F, Value, When +from django.db.models.expressions import Case, Expression, F, Ref, Value, When from django.db.models.functions import Cast, Trunc from django.db.models.query_utils import FilteredRelation, Q from django.db.models.sql.constants import CURSOR, GET_ITERATOR_CHUNK_SIZE @@ -386,8 +386,16 @@ class QuerySet: query = self.query.chain() for (alias, aggregate_expr) in kwargs.items(): query.add_annotation(aggregate_expr, alias, is_summary=True) - if not query.annotations[alias].contains_aggregate: + annotation = query.annotations[alias] + if not annotation.contains_aggregate: raise TypeError("%s is not an aggregate expression" % alias) + for expr in annotation.get_source_expressions(): + if expr.contains_aggregate and isinstance(expr, Ref) and expr.refs in kwargs: + name = expr.refs + raise exceptions.FieldError( + "Cannot compute %s('%s'): '%s' is an aggregate" + % (annotation.name, name, name) + ) return query.get_aggregation(self.db, kwargs) def count(self): diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py index 1f6baa70ff..f8aeceb2d0 100644 --- a/tests/aggregation/tests.py +++ b/tests/aggregation/tests.py @@ -1004,6 +1004,16 @@ class AggregateTestCase(TestCase): self.assertEqual(author.sum_age, other_author.sum_age) + def test_aggregate_over_aggregate(self): + msg = "Cannot compute Avg('age'): 'age' is an aggregate" + with self.assertRaisesMessage(FieldError, msg): + Author.objects.annotate( + age_alias=F('age'), + ).aggregate( + age=Sum(F('age')), + avg_age=Avg(F('age')), + ) + def test_annotated_aggregate_over_annotated_aggregate(self): with self.assertRaisesMessage(FieldError, "Cannot compute Sum('id__max'): 'id__max' is an aggregate"): Book.objects.annotate(Max('id')).annotate(Sum('id__max'))