Fixed #12886 -- aggregation over sliced queryset
This commit is contained in:
parent
7d28bed13b
commit
7737305a4f
|
@ -313,14 +313,13 @@ class QuerySet(object):
|
||||||
kwargs[arg.default_alias] = arg
|
kwargs[arg.default_alias] = arg
|
||||||
|
|
||||||
query = self.query.clone()
|
query = self.query.clone()
|
||||||
|
force_subq = query.low_mark != 0 or query.high_mark is not None
|
||||||
aggregate_names = []
|
aggregate_names = []
|
||||||
for (alias, aggregate_expr) in kwargs.items():
|
for (alias, aggregate_expr) in kwargs.items():
|
||||||
query.add_aggregate(aggregate_expr, self.model, alias,
|
query.add_aggregate(aggregate_expr, self.model, alias,
|
||||||
is_summary=True)
|
is_summary=True)
|
||||||
aggregate_names.append(alias)
|
aggregate_names.append(alias)
|
||||||
|
return query.get_aggregation(using=self.db, force_subq=force_subq)
|
||||||
return query.get_aggregation(using=self.db)
|
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -315,7 +315,7 @@ class Query(object):
|
||||||
# Return value depends on the type of the field being processed.
|
# Return value depends on the type of the field being processed.
|
||||||
return self.convert_values(value, aggregate.field, connection)
|
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.
|
Returns the dictionary with the values of the existing aggregations.
|
||||||
"""
|
"""
|
||||||
|
@ -325,18 +325,18 @@ class Query(object):
|
||||||
# If there is a group by clause, aggregating does not add useful
|
# If there is a group by clause, aggregating does not add useful
|
||||||
# information but retrieves only the first row. Aggregate
|
# information but retrieves only the first row. Aggregate
|
||||||
# over the subquery instead.
|
# 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
|
from django.db.models.sql.subqueries import AggregateQuery
|
||||||
query = AggregateQuery(self.model)
|
query = AggregateQuery(self.model)
|
||||||
|
|
||||||
obj = self.clone()
|
obj = self.clone()
|
||||||
|
relabels = dict((t, 'subquery') for t in self.tables)
|
||||||
|
|
||||||
# Remove any aggregates marked for reduction from the subquery
|
# Remove any aggregates marked for reduction from the subquery
|
||||||
# and move them to the outer AggregateQuery.
|
# and move them to the outer AggregateQuery.
|
||||||
for alias, aggregate in self.aggregate_select.items():
|
for alias, aggregate in self.aggregate_select.items():
|
||||||
if aggregate.is_summary:
|
if aggregate.is_summary:
|
||||||
query.aggregate_select[alias] = aggregate
|
query.aggregate_select[alias] = aggregate.relabeled_clone(relabels)
|
||||||
del obj.aggregate_select[alias]
|
del obj.aggregate_select[alias]
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -617,3 +617,11 @@ class BaseAggregateTestCase(TestCase):
|
||||||
# Check internal state
|
# Check internal state
|
||||||
self.assertIsNone(annotated_books.query.alias_map["aggregation_book"].join_type)
|
self.assertIsNone(annotated_books.query.alias_map["aggregation_book"].join_type)
|
||||||
self.assertIsNone(excluded_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…
Reference in New Issue