summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorvarunkasyap <varunkasyap@hotmail.com>2026-02-02 13:50:16 +0530
committerJacob Walls <jacobtylerwalls@gmail.com>2026-02-10 16:47:44 -0500
commit3282d9f4edbe5d341a0fa2a8c62b435b3885ab64 (patch)
tree391e2c4601b4c25fbacad492f677d2cc383c77e9
parent977345f70df64a1d524e185e05b956e57c612110 (diff)
Fixed #36890 -- Supported StringAgg(distinct=True) on SQLite with the default delimiter.
-rw-r--r--django/db/models/aggregates.py18
-rw-r--r--docs/ref/models/querysets.txt9
-rw-r--r--docs/releases/6.1.txt3
-rw-r--r--tests/aggregation/tests.py13
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,
)
)