Loading django/contrib/postgres/aggregates/general.py +4 −3 Original line number Diff line number Diff line Loading @@ -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: Loading docs/ref/contrib/postgres/aggregates.txt +8 −1 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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 ================================== Loading docs/releases/1.11.txt +3 −1 Original line number Diff line number Diff line Loading @@ -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` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Loading tests/postgres_tests/test_aggregates.py +18 −0 Original line number Diff line number Diff line Loading @@ -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): Loading Loading
django/contrib/postgres/aggregates/general.py +4 −3 Original line number Diff line number Diff line Loading @@ -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: Loading
docs/ref/contrib/postgres/aggregates.txt +8 −1 Original line number Diff line number Diff line Loading @@ -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. Loading @@ -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 ================================== Loading
docs/releases/1.11.txt +3 −1 Original line number Diff line number Diff line Loading @@ -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` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Loading
tests/postgres_tests/test_aggregates.py +18 −0 Original line number Diff line number Diff line Loading @@ -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): Loading