Loading django/db/models/query.py +2 −3 Original line number Diff line number Diff line Loading @@ -313,14 +313,13 @@ class QuerySet(object): kwargs[arg.default_alias] = arg query = self.query.clone() force_subq = query.low_mark != 0 or query.high_mark is not None aggregate_names = [] for (alias, aggregate_expr) in kwargs.items(): query.add_aggregate(aggregate_expr, self.model, alias, is_summary=True) aggregate_names.append(alias) return query.get_aggregation(using=self.db) return query.get_aggregation(using=self.db, force_subq=force_subq) def count(self): """ Loading django/db/models/sql/query.py +4 −4 Original line number Diff line number Diff line Loading @@ -315,7 +315,7 @@ class Query(object): # Return value depends on the type of the field being processed. return self.convert_values(value, aggregate.field, connection) def get_aggregation(self, using): def get_aggregation(self, using, force_subq=False): """ Returns the dictionary with the values of the existing aggregations. """ Loading @@ -325,18 +325,18 @@ class Query(object): # If there is a group by clause, aggregating does not add useful # information but retrieves only the first row. Aggregate # over the subquery instead. if self.group_by is not None: if self.group_by is not None or force_subq: from django.db.models.sql.subqueries import AggregateQuery query = AggregateQuery(self.model) obj = self.clone() relabels = dict((t, 'subquery') for t in self.tables) # Remove any aggregates marked for reduction from the subquery # and move them to the outer AggregateQuery. for alias, aggregate in self.aggregate_select.items(): if aggregate.is_summary: query.aggregate_select[alias] = aggregate query.aggregate_select[alias] = aggregate.relabeled_clone(relabels) del obj.aggregate_select[alias] try: Loading tests/aggregation/tests.py +8 −0 Original line number Diff line number Diff line Loading @@ -617,3 +617,11 @@ class BaseAggregateTestCase(TestCase): # Check internal state self.assertIsNone(annotated_books.query.alias_map["aggregation_book"].join_type) self.assertIsNone(excluded_books.query.alias_map["aggregation_book"].join_type) def test_ticket12886(self): """ Check that aggregation over sliced queryset works correctly. """ qs = Book.objects.all().order_by('-rating')[0:3] vals = qs.aggregate(average_top3_rating=Avg('rating'))['average_top3_rating'] self.assertAlmostEqual(vals, 4.5, places=2) Loading
django/db/models/query.py +2 −3 Original line number Diff line number Diff line Loading @@ -313,14 +313,13 @@ class QuerySet(object): kwargs[arg.default_alias] = arg query = self.query.clone() force_subq = query.low_mark != 0 or query.high_mark is not None aggregate_names = [] for (alias, aggregate_expr) in kwargs.items(): query.add_aggregate(aggregate_expr, self.model, alias, is_summary=True) aggregate_names.append(alias) return query.get_aggregation(using=self.db) return query.get_aggregation(using=self.db, force_subq=force_subq) def count(self): """ Loading
django/db/models/sql/query.py +4 −4 Original line number Diff line number Diff line Loading @@ -315,7 +315,7 @@ class Query(object): # Return value depends on the type of the field being processed. return self.convert_values(value, aggregate.field, connection) def get_aggregation(self, using): def get_aggregation(self, using, force_subq=False): """ Returns the dictionary with the values of the existing aggregations. """ Loading @@ -325,18 +325,18 @@ class Query(object): # If there is a group by clause, aggregating does not add useful # information but retrieves only the first row. Aggregate # over the subquery instead. if self.group_by is not None: if self.group_by is not None or force_subq: from django.db.models.sql.subqueries import AggregateQuery query = AggregateQuery(self.model) obj = self.clone() relabels = dict((t, 'subquery') for t in self.tables) # Remove any aggregates marked for reduction from the subquery # and move them to the outer AggregateQuery. for alias, aggregate in self.aggregate_select.items(): if aggregate.is_summary: query.aggregate_select[alias] = aggregate query.aggregate_select[alias] = aggregate.relabeled_clone(relabels) del obj.aggregate_select[alias] try: Loading
tests/aggregation/tests.py +8 −0 Original line number Diff line number Diff line Loading @@ -617,3 +617,11 @@ class BaseAggregateTestCase(TestCase): # Check internal state self.assertIsNone(annotated_books.query.alias_map["aggregation_book"].join_type) self.assertIsNone(excluded_books.query.alias_map["aggregation_book"].join_type) def test_ticket12886(self): """ Check that aggregation over sliced queryset works correctly. """ qs = Book.objects.all().order_by('-rating')[0:3] vals = qs.aggregate(average_top3_rating=Avg('rating'))['average_top3_rating'] self.assertAlmostEqual(vals, 4.5, places=2)