﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
36149	Composite primary key subquery lookup prevent usage or specify fields and are not implemented for exact	Simon Charette	Simon Charette	"I started playing with tuple lookup support for composite primary key and I noticed two main problems.

First `pk__in=query` lookups completely disallow specifying which fields should be used  in the select clause of the right-hand-side and implicitly set them to the left-hand-side name which assumes the same set of left-hand-side field name is shared by the right-hand-side which is a bad assumption

{{{#!diff
diff --git a/tests/composite_pk/models/tenant.py b/tests/composite_pk/models/tenant.py
index 6286ed2354..c85869afa7 100644
--- a/tests/composite_pk/models/tenant.py
+++ b/tests/composite_pk/models/tenant.py
@@ -44,6 +44,7 @@ class Comment(models.Model):
         related_name=""comments"",
     )
     text = models.TextField(default="""", blank=True)
+    integer = models.IntegerField(default=0)


 class Post(models.Model):
diff --git a/tests/composite_pk/test_filter.py b/tests/composite_pk/test_filter.py
index fe942b9e5b..78383655a0 100644
--- a/tests/composite_pk/test_filter.py
+++ b/tests/composite_pk/test_filter.py
@@ -182,6 +182,30 @@ def test_filter_comments_by_pk_in(self):
                     Comment.objects.filter(pk__in=pks).order_by(""pk""), objs
                 )

+    def test_filter_comments_by_pk_in_subquery(self):
+        self.assertSequenceEqual(
+            Comment.objects.filter(
+                pk__in=Comment.objects.filter(pk=self.comment_1.pk),
+            ),
+            [self.comment_1],
+        )
+        self.assertSequenceEqual(
+            Comment.objects.filter(
+                pk__in=Comment.objects.filter(pk=self.comment_1.pk).values(
+                    ""tenant_id"", ""id""
+                ),
+            ),
+            [self.comment_1],
+        )
+        self.comment_2.integer = self.comment_1.id
+        self.comment_2.save()
+        self.assertSequenceEqual(
+            Comment.objects.filter(
+                pk__in=Comment.objects.values(""tenant_id"", ""integer""),
+            ),
+            [self.comment_1],
+        )
+
     def test_filter_comments_by_user_and_order_by_pk_asc(self):
         self.assertSequenceEqual(
             Comment.objects.filter(user=self.user_1).order_by(""pk""),
}}}

Allowing fields to be specified requires lifting a `QuerySet` [https://github.com/django/django/blob/8eca4077f60fa0705ecfd9437c9ceaeef7a3808b/django/db/models/query.py#L1967-L1970 resolving time constraint] which was entirely by-passable by passing `queryset.query` instead as the right-hand-side.

The tuple exact lookup should also allow querysets with a single element to be specified as right-hand-side which is currently disallowed

{{{#!diff
diff --git a/tests/composite_pk/test_filter.py b/tests/composite_pk/test_filter.py
index fe942b9e5b..78383655a0 100644
--- a/tests/composite_pk/test_filter.py
+++ b/tests/composite_pk/test_filter.py
@@ -450,6 +474,30 @@ def test_non_outer_ref_subquery(self):
         with self.assertRaisesMessage(ValueError, msg):
             Comment.objects.filter(pk=pk)

+    def test_filter_comments_by_pk_exact_subquery(self):
+        self.assertSequenceEqual(
+            Comment.objects.filter(
+                pk=Comment.objects.filter(pk=self.comment_1.pk)[:1],
+            ),
+            [self.comment_1],
+        )
+        self.assertSequenceEqual(
+            Comment.objects.filter(
+                pk__in=Comment.objects.filter(pk=self.comment_1.pk).values(
+                    ""tenant_id"", ""id""
+                )[:1],
+            ),
+            [self.comment_1],
+        )
+        self.comment_2.integer = self.comment_1.id
+        self.comment_2.save()
+        self.assertSequenceEqual(
+            Comment.objects.filter(
+                pk__in=Comment.objects.values(""tenant_id"", ""integer""),
+            )[:1],
+            [self.comment_1],
+        )
+
     def test_outer_ref_not_composite_pk(self):
         subquery = Comment.objects.filter(pk=OuterRef(""id"")).values(""id"")
         queryset = Comment.objects.filter(id=Subquery(subquery))
}}}

Lastly lifting the constraint that prevented subqueries returning more than one column from being used as a right-hand-side required adjusting the existing `exact` and `in` lookup logic to disallow left-hand-side and right-hand-side with mismatching number of fields. "	Bug	closed	Database layer (models, ORM)	5.2	Release blocker	fixed		Csirmaz Bendegúz	Ready for checkin	1	0	0	0	0	0
