From 7737305a4f5dc5006d92dac3a61523ad6c2a523a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= <akaariai@gmail.com>
Date: Mon, 19 Aug 2013 16:00:17 +0300
Subject: [PATCH] Fixed #12886 -- aggregation over sliced queryset

---
 django/db/models/query.py     | 5 ++---
 django/db/models/sql/query.py | 8 ++++----
 tests/aggregation/tests.py    | 8 ++++++++
 3 files changed, 14 insertions(+), 7 deletions(-)

diff --git a/django/db/models/query.py b/django/db/models/query.py
index 4069c04a14..836d394e9b 100644
--- a/django/db/models/query.py
+++ b/django/db/models/query.py
@@ -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):
         """
diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py
index bb7d071f38..64648c394e 100644
--- a/django/db/models/sql/query.py
+++ b/django/db/models/sql/query.py
@@ -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.
         """
@@ -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:
diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py
index 4d46cae766..339ccd63be 100644
--- a/tests/aggregation/tests.py
+++ b/tests/aggregation/tests.py
@@ -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)