From 5f24e7158e1d5a7e40fa0ae270639f6a171bb18e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?E=CC=81tienne=20Beaule=CC=81?= Date: Tue, 30 Jul 2019 20:08:55 +0200 Subject: [PATCH] Fixed #30665 -- Added support for distinct argument to Avg() and Sum(). --- django/db/models/aggregates.py | 2 ++ docs/ref/models/querysets.txt | 28 ++++++++++++++++++++++++++-- docs/releases/3.0.txt | 3 +++ tests/aggregation/tests.py | 10 ++++++++-- 4 files changed, 39 insertions(+), 4 deletions(-) diff --git a/django/db/models/aggregates.py b/django/db/models/aggregates.py index da4ff928aa0..8b10829eb8e 100644 --- a/django/db/models/aggregates.py +++ b/django/db/models/aggregates.py @@ -99,6 +99,7 @@ class Aggregate(Func): class Avg(FixDurationInputMixin, NumericOutputFieldMixin, Aggregate): function = 'AVG' name = 'Avg' + allow_distinct = True class Count(Aggregate): @@ -142,6 +143,7 @@ class StdDev(NumericOutputFieldMixin, Aggregate): class Sum(FixDurationInputMixin, Aggregate): function = 'SUM' name = 'Sum' + allow_distinct = True class Variance(NumericOutputFieldMixin, Aggregate): diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index f84097ae966..409d8f0f298 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -3378,7 +3378,7 @@ by the aggregate. ``Avg`` ~~~~~~~ -.. class:: Avg(expression, output_field=None, filter=None, **extra) +.. class:: Avg(expression, output_field=None, distinct=False, filter=None, **extra) Returns the mean value of the given expression, which must be numeric unless you specify a different ``output_field``. @@ -3387,6 +3387,18 @@ by the aggregate. * Return type: ``float`` if input is ``int``, otherwise same as input field, or ``output_field`` if supplied + Has one optional argument: + + .. attribute:: distinct + + If ``distinct=True``, ``Avg`` returns the mean value of unique values. + This is the SQL equivalent of ``AVG(DISTINCT )``. The default + value is ``False``. + + .. versionchanged:: 3.0 + + Support for ``distinct=True`` was added. + ``Count`` ~~~~~~~~~ @@ -3451,13 +3463,25 @@ by the aggregate. ``Sum`` ~~~~~~~ -.. class:: Sum(expression, output_field=None, filter=None, **extra) +.. class:: Sum(expression, output_field=None, distinct=False, filter=None, **extra) Computes the sum of all values of the given expression. * Default alias: ``__sum`` * Return type: same as input field, or ``output_field`` if supplied + Has one optional argument: + + .. attribute:: distinct + + If ``distinct=True``, ``Sum`` returns the sum of unique values. This is + the SQL equivalent of ``SUM(DISTINCT )``. The default value is + ``False``. + + .. versionchanged:: 3.0 + + Support for ``distinct=True`` was added. + ``Variance`` ~~~~~~~~~~~~ diff --git a/docs/releases/3.0.txt b/docs/releases/3.0.txt index 4bfebba73b5..da1578c0eb7 100644 --- a/docs/releases/3.0.txt +++ b/docs/releases/3.0.txt @@ -291,6 +291,9 @@ Models customize the get and set behavior by overriding their :py:ref:`descriptors `. +* :class:`~django.db.models.Avg` and :class:`~django.db.models.Sum` now support + the ``distinct`` argument. + Requests and Responses ~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py index 7e0436cdfc5..0f799c4bc3f 100644 --- a/tests/aggregation/tests.py +++ b/tests/aggregation/tests.py @@ -401,8 +401,14 @@ class AggregateTestCase(TestCase): self.assertEqual(aggs['distinct_ratings'], 4) def test_distinct_on_aggregate(self): - books = Book.objects.aggregate(ratings=Count('rating', distinct=True)) - self.assertEqual(books['ratings'], 4) + for aggregate, expected_result in ( + (Avg, 4.125), + (Count, 4), + (Sum, 16.5), + ): + with self.subTest(aggregate=aggregate.__name__): + books = Book.objects.aggregate(ratings=aggregate('rating', distinct=True)) + self.assertEqual(books['ratings'], expected_result) def test_non_grouped_annotation_not_in_group_by(self): """