diff options
| author | varunkasyap <varunkasyap@hotmail.com> | 2026-02-02 13:50:16 +0530 |
|---|---|---|
| committer | Jacob Walls <jacobtylerwalls@gmail.com> | 2026-02-10 16:47:44 -0500 |
| commit | 3282d9f4edbe5d341a0fa2a8c62b435b3885ab64 (patch) | |
| tree | 391e2c4601b4c25fbacad492f677d2cc383c77e9 | |
| parent | 977345f70df64a1d524e185e05b956e57c612110 (diff) | |
Fixed #36890 -- Supported StringAgg(distinct=True) on SQLite with the default delimiter.
| -rw-r--r-- | django/db/models/aggregates.py | 18 | ||||
| -rw-r--r-- | docs/ref/models/querysets.txt | 9 | ||||
| -rw-r--r-- | docs/releases/6.1.txt | 3 | ||||
| -rw-r--r-- | tests/aggregation/tests.py | 13 |
4 files changed, 41 insertions, 2 deletions
diff --git a/django/db/models/aggregates.py b/django/db/models/aggregates.py index 1cf82416cb..d1139e8bcc 100644 --- a/django/db/models/aggregates.py +++ b/django/db/models/aggregates.py @@ -369,6 +369,24 @@ class StringAgg(Aggregate): return sql, (*params, *delimiter_params) def as_sqlite(self, compiler, connection, **extra_context): + if ( + self.distinct + and isinstance(self.delimiter.value, Value) + and self.delimiter.value.value == "," + ): + clone = self.copy() + source_expressions = clone.get_source_expressions() + clone.set_source_expressions( + source_expressions[:1] + source_expressions[2:] + ) + + return clone.as_sql( + compiler, + connection, + function="GROUP_CONCAT", + **extra_context, + ) + if connection.get_database_version() < (3, 44): return self.as_sql( compiler, diff --git a/docs/ref/models/querysets.txt b/docs/ref/models/querysets.txt index c819015b25..579c0de302 100644 --- a/docs/ref/models/querysets.txt +++ b/docs/ref/models/querysets.txt @@ -4175,7 +4175,14 @@ by the aggregate. .. attribute:: delimiter A ``Value`` or expression representing the string that should separate - each of the values. For example, ``Value(",")``. + each of the values. For example, ``Value(",")``. (On SQLite, the + literal delimiter ``Value(",")`` is the only delimiter compatible with + ``distinct=True``.) + + .. versionchanged:: 6.1 + + Support for using ``distinct=True`` with a delimiter of + ``Value(",")`` on SQLite was added. Query-related tools =================== diff --git a/docs/releases/6.1.txt b/docs/releases/6.1.txt index 0d35982543..af783136ad 100644 --- a/docs/releases/6.1.txt +++ b/docs/releases/6.1.txt @@ -284,6 +284,9 @@ Models * The :data:`~django.db.models.signals.m2m_changed` signal now receives a ``raw`` argument. +* :class:`~django.db.models.StringAgg` now supports ``distinct=True`` on SQLite + when using the default delimiter ``Value(",")`` only. + Pagination ~~~~~~~~~~ diff --git a/tests/aggregation/tests.py b/tests/aggregation/tests.py index bf6bf27031..0a975dcb52 100644 --- a/tests/aggregation/tests.py +++ b/tests/aggregation/tests.py @@ -3,6 +3,7 @@ import math import re from decimal import Decimal from itertools import chain +from unittest import skipUnless from django.core.exceptions import FieldError from django.db import NotSupportedError, connection @@ -579,6 +580,16 @@ class AggregateTestCase(TestCase): ) self.assertCountEqual(books["ratings"].split(","), ["3", "4", "4.5", "5"]) + @skipUnless(connection.vendor == "sqlite", "Special default case for SQLite.") + def test_distinct_on_stringagg_sqlite_special_case(self): + """ + Value(",") is the only delimiter usable on SQLite with distinct=True. + """ + books = Book.objects.aggregate( + ratings=StringAgg(Cast(F("rating"), CharField()), Value(","), distinct=True) + ) + self.assertCountEqual(books["ratings"].split(","), ["3.0", "4.0", "4.5", "5.0"]) + @skipIfDBFeature("supports_aggregate_distinct_multiple_argument") def test_raises_error_on_multiple_argument_distinct(self): message = ( @@ -589,7 +600,7 @@ class AggregateTestCase(TestCase): Book.objects.aggregate( ratings=StringAgg( Cast(F("rating"), CharField()), - Value(","), + Value(";"), distinct=True, ) ) |
