From 7bc57a6d71dd4d00bb09cfa67be547591fd759ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anssi=20K=C3=A4=C3=A4ri=C3=A4inen?= Date: Mon, 19 Aug 2013 16:14:31 +0300 Subject: [PATCH] Fixed #11881 -- removed junk from aggregation subqueries There were clauses that weren't needed in the subqueries. These were ORDER BY, SELECT FOR UPDATE and related selections. --- django/db/models/sql/query.py | 10 +++++++++- tests/aggregation/tests.py | 17 +++++++++++++++++ 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/django/db/models/sql/query.py b/django/db/models/sql/query.py index 64648c394e7..1d19c5b9a8a 100644 --- a/django/db/models/sql/query.py +++ b/django/db/models/sql/query.py @@ -330,8 +330,16 @@ class Query(object): from django.db.models.sql.subqueries import AggregateQuery query = AggregateQuery(self.model) obj = self.clone() - relabels = dict((t, 'subquery') for t in self.tables) + if not force_subq: + # In forced subq case the ordering and limits will likely + # affect the results. + obj.clear_ordering(True) + obj.clear_limits() + obj.select_for_update = False + obj.select_related = False + obj.related_select_cols = [] + 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(): diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py index 339ccd63be4..7d2490a77c7 100644 --- a/tests/aggregation/tests.py +++ b/tests/aggregation/tests.py @@ -3,8 +3,10 @@ from __future__ import unicode_literals import datetime from decimal import Decimal +from django.db import connection from django.db.models import Avg, Sum, Count, Max, Min from django.test import TestCase, Approximate +from django.test.utils import CaptureQueriesContext from .models import Author, Publisher, Book, Store @@ -625,3 +627,18 @@ class BaseAggregateTestCase(TestCase): 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) + + def test_ticket11881(self): + """ + Check that subqueries do not needlessly contain ORDER BY, SELECT FOR UPDATE + or select_related() stuff. + """ + qs = Book.objects.all().select_for_update().order_by( + 'pk').select_related('publisher').annotate(max_pk=Max('pk')) + with CaptureQueriesContext(connection) as captured_queries: + qs.aggregate(avg_pk=Avg('max_pk')) + self.assertEqual(len(captured_queries), 1) + qstr = captured_queries[0]['sql'].lower() + self.assertNotIn('for update', qstr) + self.assertNotIn('order by', qstr) + self.assertEqual(qstr.count(' join '), 0)