From 3a0fe942ddf56ddcf4b958147f3914fe2788db30 Mon Sep 17 00:00:00 2001 From: Anssi Kääriäinen Date: Wed, 4 Mar 2015 14:56:20 +0200 Subject: [1.8.x] Fixed #24171 -- Fixed failure with complex aggregate query and expressions The query used a construct of qs.annotate().values().aggregate() where the first annotate used an F-object reference and the values() and aggregate() calls referenced that F-object. Also made sure the inner query's select clause is as simple as possible, and made sure .values().distinct().aggreate() works correctly. Backport of fb146193c49e4c683dc8da39d9b7c479375fdb57 from master --- tests/aggregation_regress/tests.py | 12 +++++++++++- tests/expressions/models.py | 1 + tests/expressions/tests.py | 17 +++++++++++++---- 3 files changed, 25 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/aggregation_regress/tests.py b/tests/aggregation_regress/tests.py index 58a226c95c..bc6a55875d 100644 --- a/tests/aggregation_regress/tests.py +++ b/tests/aggregation_regress/tests.py @@ -7,7 +7,9 @@ from operator import attrgetter from django.contrib.contenttypes.models import ContentType from django.core.exceptions import FieldError -from django.db.models import F, Q, Avg, Count, Max, StdDev, Sum, Variance +from django.db.models import ( + F, Q, Avg, Count, Max, StdDev, Sum, Value, Variance, +) from django.test import TestCase, skipUnlessDBFeature from django.test.utils import Approximate from django.utils import six @@ -1129,6 +1131,14 @@ class AggregationTests(TestCase): 'select__avg': Approximate(1.666, places=2), }) + def test_annotate_distinct_aggregate(self): + # There are three books with rating of 4.0 and two of the books have + # the same price. Hence, the distinct removes one rating of 4.0 + # from the results. + vals1 = Book.objects.values('rating', 'price').distinct().aggregate(result=Sum('rating')) + vals2 = Book.objects.aggregate(result=Sum('rating') - Value(4.0)) + self.assertEqual(vals1, vals2) + class JoinPromotionTests(TestCase): def test_ticket_21150(self): diff --git a/tests/expressions/models.py b/tests/expressions/models.py index 69de52c308..1c1e924079 100644 --- a/tests/expressions/models.py +++ b/tests/expressions/models.py @@ -12,6 +12,7 @@ from django.utils.encoding import python_2_unicode_compatible class Employee(models.Model): firstname = models.CharField(max_length=50) lastname = models.CharField(max_length=50) + salary = models.IntegerField(blank=True, null=True) def __str__(self): return '%s %s' % (self.firstname, self.lastname) diff --git a/tests/expressions/tests.py b/tests/expressions/tests.py index 7233fa0d08..59fd063103 100644 --- a/tests/expressions/tests.py +++ b/tests/expressions/tests.py @@ -5,7 +5,7 @@ import uuid from copy import deepcopy from django.core.exceptions import FieldError -from django.db import DatabaseError, connection, transaction +from django.db import DatabaseError, connection, models, transaction from django.db.models import TimeField, UUIDField from django.db.models.aggregates import ( Avg, Count, Max, Min, StdDev, Sum, Variance, @@ -30,15 +30,15 @@ class BasicExpressionsTests(TestCase): def setUpTestData(cls): Company.objects.create( name="Example Inc.", num_employees=2300, num_chairs=5, - ceo=Employee.objects.create(firstname="Joe", lastname="Smith") + ceo=Employee.objects.create(firstname="Joe", lastname="Smith", salary=10) ) Company.objects.create( name="Foobar Ltd.", num_employees=3, num_chairs=4, - ceo=Employee.objects.create(firstname="Frank", lastname="Meyer") + ceo=Employee.objects.create(firstname="Frank", lastname="Meyer", salary=20) ) Company.objects.create( name="Test GmbH", num_employees=32, num_chairs=1, - ceo=Employee.objects.create(firstname="Max", lastname="Mustermann") + ceo=Employee.objects.create(firstname="Max", lastname="Mustermann", salary=30) ) def setUp(self): @@ -48,6 +48,15 @@ class BasicExpressionsTests(TestCase): "name", "num_employees", "num_chairs" ) + def test_annotate_values_aggregate(self): + companies = Company.objects.annotate( + salaries=F('ceo__salary'), + ).values('num_employees', 'salaries').aggregate( + result=Sum(F('salaries') + F('num_employees'), + output_field=models.IntegerField()), + ) + self.assertEqual(companies['result'], 2395) + def test_filter_inter_attribute(self): # We can filter on attribute relationships on same model obj, e.g. # find companies where the number of employees is greater -- cgit v1.3