From 3031c512f0cf030dc8f99128bcb4fb9d4d6e285a Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Thu, 24 Jul 2025 15:18:47 -0400 Subject: [PATCH] [5.2.x] Fixed #36522 -- Added support for filtering composite pks using a tuple of expressions. Thanks Jacob Walls for the report, and Sarah Boyce and Mariusz Felisiak for reviews. Backport of 0a4999b422702c64e21f5a10a4d60300b7074401 from main. --- django/db/models/fields/tuple_lookups.py | 12 ++++++++++-- docs/releases/5.2.5.txt | 3 +++ tests/composite_pk/test_filter.py | 8 ++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/django/db/models/fields/tuple_lookups.py b/django/db/models/fields/tuple_lookups.py index 2af2e193d5..3b9ff85dff 100644 --- a/django/db/models/fields/tuple_lookups.py +++ b/django/db/models/fields/tuple_lookups.py @@ -92,7 +92,11 @@ class TupleLookupMixin: def process_rhs(self, compiler, connection): if self.rhs_is_direct_value(): args = [ - Value(val, output_field=col.output_field) + ( + val + if hasattr(val, "as_sql") + else Value(val, output_field=col.output_field) + ) for col, val in zip(self.lhs, self.rhs) ] return compiler.compile(Tuple(*args)) @@ -326,7 +330,11 @@ class TupleIn(TupleLookupMixin, In): result.append( Tuple( *[ - Value(val, output_field=col.output_field) + ( + val + if hasattr(val, "as_sql") + else Value(val, output_field=col.output_field) + ) for col, val in zip(lhs, vals) ] ) diff --git a/docs/releases/5.2.5.txt b/docs/releases/5.2.5.txt index a72a3fe078..7708563857 100644 --- a/docs/releases/5.2.5.txt +++ b/docs/releases/5.2.5.txt @@ -12,3 +12,6 @@ Bugfixes * Fixed a regression in Django 5.2.1 that prevented the usage of ``UNNEST`` PostgreSQL strategy of ``QuerySet.bulk_create()`` with foreign keys (:ticket:`36502`). + +* Fixed a crash in Django 5.2 when filtering against a composite primary key + using a tuple containing expressions (:ticket:`36522`). diff --git a/tests/composite_pk/test_filter.py b/tests/composite_pk/test_filter.py index 03037d4d82..ed6caf8034 100644 --- a/tests/composite_pk/test_filter.py +++ b/tests/composite_pk/test_filter.py @@ -9,6 +9,7 @@ from django.db.models import ( Q, Subquery, TextField, + Value, When, ) from django.db.models.functions import Cast @@ -549,6 +550,13 @@ class CompositePKFilterTests(TestCase): [self.tenant_1], ) + def test_filter_by_tuple_containing_expression(self): + pk_lookup = (self.comment_1.tenant.id, (Value(self.comment_1.id) + 1) - 1) + for lookup in ({"pk": pk_lookup}, {"pk__in": [pk_lookup]}): + with self.subTest(lookup=lookup): + qs = Comment.objects.filter(**lookup) + self.assertEqual(qs.get(), self.comment_1) + @skipUnlessDBFeature("supports_tuple_lookups") class CompositePKFilterTupleLookupFallbackTests(CompositePKFilterTests):