summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRustam Kashapov <hardtechnik91@gmail.com>2016-05-15 12:53:16 +0300
committerTim Graham <timograham@gmail.com>2016-06-02 13:48:35 -0400
commitdf8412d2e5c95fe8c8238ebde8e0dbb68fe2ec1d (patch)
tree58495dcfbed508cc58456b44e32bf97cbbeb46a1
parent149ace94dfc10504a0e69462c7737f5ce05340a4 (diff)
Fixed #26617 -- Added distinct argument to contrib.postgres's StringAgg.
-rw-r--r--django/contrib/postgres/aggregates/general.py7
-rw-r--r--docs/ref/contrib/postgres/aggregates.txt9
-rw-r--r--docs/releases/1.11.txt4
-rw-r--r--tests/postgres_tests/test_aggregates.py18
4 files changed, 33 insertions, 5 deletions
diff --git a/django/contrib/postgres/aggregates/general.py b/django/contrib/postgres/aggregates/general.py
index 1dda69c449..6ff6727bd4 100644
--- a/django/contrib/postgres/aggregates/general.py
+++ b/django/contrib/postgres/aggregates/general.py
@@ -32,10 +32,11 @@ class BoolOr(Aggregate):
class StringAgg(Aggregate):
function = 'STRING_AGG'
- template = "%(function)s(%(expressions)s, '%(delimiter)s')"
+ template = "%(function)s(%(distinct)s%(expressions)s, '%(delimiter)s')"
- def __init__(self, expression, delimiter, **extra):
- super(StringAgg, self).__init__(expression, delimiter=delimiter, **extra)
+ def __init__(self, expression, delimiter, distinct=False, **extra):
+ distinct = 'DISTINCT ' if distinct else ''
+ super(StringAgg, self).__init__(expression, delimiter=delimiter, distinct=distinct, **extra)
def convert_value(self, value, expression, connection, context):
if not value:
diff --git a/docs/ref/contrib/postgres/aggregates.txt b/docs/ref/contrib/postgres/aggregates.txt
index 943b0b9373..f9b7be0fd3 100644
--- a/docs/ref/contrib/postgres/aggregates.txt
+++ b/docs/ref/contrib/postgres/aggregates.txt
@@ -61,7 +61,7 @@ General-purpose aggregation functions
``StringAgg``
-------------
-.. class:: StringAgg(expression, delimiter)
+.. class:: StringAgg(expression, delimiter, distinct=False)
Returns the input values concatenated into a string, separated by
the ``delimiter`` string.
@@ -70,6 +70,13 @@ General-purpose aggregation functions
Required argument. Needs to be a string.
+ .. attribute:: distinct
+
+ .. versionadded:: 1.11
+
+ An optional boolean argument that determines if concatenated values
+ will be distinct. Defaults to ``False``.
+
Aggregate functions for statistics
==================================
diff --git a/docs/releases/1.11.txt b/docs/releases/1.11.txt
index a2a427e7a4..9c162b41c4 100644
--- a/docs/releases/1.11.txt
+++ b/docs/releases/1.11.txt
@@ -81,7 +81,9 @@ Minor features
:mod:`django.contrib.postgres`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-* ...
+* The new ``distinct`` argument for
+ :class:`~django.contrib.postgres.aggregates.StringAgg` determines if
+ concatenated values will be distinct.
:mod:`django.contrib.redirects`
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
diff --git a/tests/postgres_tests/test_aggregates.py b/tests/postgres_tests/test_aggregates.py
index 07e4ea0101..f6f48fdd61 100644
--- a/tests/postgres_tests/test_aggregates.py
+++ b/tests/postgres_tests/test_aggregates.py
@@ -111,6 +111,24 @@ class TestGeneralAggregate(PostgreSQLTestCase):
self.assertEqual(values, {'stringagg': ''})
+class TestStringAggregateDistinct(PostgreSQLTestCase):
+ @classmethod
+ def setUpTestData(cls):
+ AggregateTestModel.objects.create(char_field='Foo')
+ AggregateTestModel.objects.create(char_field='Foo')
+ AggregateTestModel.objects.create(char_field='Bar')
+
+ def test_string_agg_distinct_false(self):
+ values = AggregateTestModel.objects.aggregate(stringagg=StringAgg('char_field', delimiter=' ', distinct=False))
+ self.assertEqual(values['stringagg'].count('Foo'), 2)
+ self.assertEqual(values['stringagg'].count('Bar'), 1)
+
+ def test_string_agg_distinct_true(self):
+ values = AggregateTestModel.objects.aggregate(stringagg=StringAgg('char_field', delimiter=' ', distinct=True))
+ self.assertEqual(values['stringagg'].count('Foo'), 1)
+ self.assertEqual(values['stringagg'].count('Bar'), 1)
+
+
class TestStatisticsAggregate(PostgreSQLTestCase):
@classmethod
def setUpTestData(cls):