diff --git a/django/db/models/expressions.py b/django/db/models/expressions.py index 0eb70b057d..5d23c1572f 100644 --- a/django/db/models/expressions.py +++ b/django/db/models/expressions.py @@ -1669,7 +1669,7 @@ class Window(SQLiteNumericMixin, Expression): if not connection.features.supports_over_clause: raise NotSupportedError("This backend does not support window expressions.") expr_sql, params = compiler.compile(self.source_expression) - window_sql, window_params = [], [] + window_sql, window_params = [], () if self.partition_by is not None: sql_expr, sql_params = self.partition_by.as_sql( @@ -1678,24 +1678,23 @@ class Window(SQLiteNumericMixin, Expression): template="PARTITION BY %(expressions)s", ) window_sql.append(sql_expr) - window_params.extend(sql_params) + window_params += tuple(sql_params) if self.order_by is not None: order_sql, order_params = compiler.compile(self.order_by) window_sql.append(order_sql) - window_params.extend(order_params) + window_params += tuple(order_params) if self.frame: frame_sql, frame_params = compiler.compile(self.frame) window_sql.append(frame_sql) - window_params.extend(frame_params) + window_params += tuple(frame_params) - params.extend(window_params) template = template or self.template return ( template % {"expression": expr_sql, "window": " ".join(window_sql).strip()}, - params, + (*params, *window_params), ) def as_sqlite(self, compiler, connection): diff --git a/docs/releases/4.1.1.txt b/docs/releases/4.1.1.txt index 3e11782eb2..7339390ffa 100644 --- a/docs/releases/4.1.1.txt +++ b/docs/releases/4.1.1.txt @@ -22,3 +22,7 @@ Bugfixes * Fixed a regression in Django 4.1 that caused an incorrect redirection to the admin changelist view when using *"Save and continue editing"* and *"Save and add another"* options (:ticket:`33893`). + +* Fixed a regression in Django 4.1 that caused a crash of + :class:`~django.db.models.expressions.Window` expressions with + :class:`~django.contrib.postgres.aggregates.ArrayAgg` (:ticket:`33898`). diff --git a/tests/postgres_tests/test_aggregates.py b/tests/postgres_tests/test_aggregates.py index c3df490fcf..c2f38e42b6 100644 --- a/tests/postgres_tests/test_aggregates.py +++ b/tests/postgres_tests/test_aggregates.py @@ -8,6 +8,7 @@ from django.db.models import ( Q, Subquery, Value, + Window, ) from django.db.models.fields.json import KeyTextTransform, KeyTransform from django.db.models.functions import Cast, Concat, Substr @@ -669,6 +670,22 @@ class TestGeneralAggregate(PostgreSQLTestCase): inner_qs.values_list("integer_field", flat=True), ) + def test_window(self): + self.assertCountEqual( + AggregateTestModel.objects.annotate( + integers=Window( + expression=ArrayAgg("char_field"), + partition_by=F("integer_field"), + ) + ).values("integers", "char_field"), + [ + {"integers": ["Foo1", "Foo3"], "char_field": "Foo1"}, + {"integers": ["Foo1", "Foo3"], "char_field": "Foo3"}, + {"integers": ["Foo2"], "char_field": "Foo2"}, + {"integers": ["Foo4"], "char_field": "Foo4"}, + ], + ) + class TestAggregateDistinct(PostgreSQLTestCase): @classmethod