﻿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
37291	"Passing F(""pk"") to a tuple lookup can hang"	Jacob Walls		"This test provides `F(""pk"")` directly to the `TupleIn` lookup. It hangs due to a sanity check in the composite PK logic:

{{{#!py
diff --git a/tests/foreign_object/test_tuple_lookups.py b/tests/foreign_object/test_tuple_lookups.py
index 008f118994..fb1965fa6a 100644
--- a/tests/foreign_object/test_tuple_lookups.py
+++ b/tests/foreign_object/test_tuple_lookups.py
@@ -168,6 +168,12 @@ class TupleLookupsTests(TestCase):
             with self.subTest(customer=customer.id, query=str(qs.query)):
                 self.assertSequenceEqual(qs, contacts)
 
+    def test_tuple_in_subquery_f(self):
+        self.assertCountEqual(
+            Contact.objects.filter(TupleIn(F(""pk""), Contact.objects.values(""pk""))),
+            Contact.objects.all(),
+        )
+
     def test_tuple_in_rhs_must_be_collection_of_tuples_or_lists(self):
         test_cases = (
             (1, 2, 3),
}}}



That sanity check iterates over the left-hand side expression, which is apparently not safe if the left-hand side is an `F` object, as when `__iter__()` missing, Python falls back to `__getitem__()`, and `F.__getitem__()` can perpetually iterate an index:
{{{#!py
  /Users/jwalls/django/tests/foreign_object/test_tuple_lookups.py(173)test_tuple_in_subquery_f()
-> Contact.objects.filter(TupleIn(F(""pk""), Contact.objects.values(""pk""))),
  /Users/jwalls/django/django/db/models/lookups.py(35)__init__()
-> self.rhs = self.get_prep_lookup()
  /Users/jwalls/django/django/db/models/fields/tuple_lookups.py(302)get_prep_lookup()
-> self.check_rhs_is_tuple_or_list()
  /Users/jwalls/django/django/db/models/fields/tuple_lookups.py(63)check_rhs_is_tuple_or_list()
-> lhs_str = self.get_lhs_str()
  /Users/jwalls/django/django/db/models/fields/tuple_lookups.py(89)get_lhs_str()
-> names = "", "".join(repr(f.name) for f in self.lhs)
  /Users/jwalls/django/django/db/models/fields/tuple_lookups.py(89)<genexpr>()->""'pk'""
-> names = "", "".join(repr(f.name) for f in self.lhs)
> /Users/jwalls/django/django/db/models/expressions.py(898)__getitem__()
}}}
`F.__getitem__()`:
{{{#!py
    def __getitem__(self, subscript):
        return Sliced(self, subscript)
}}}"	Bug	new	Database layer (models, ORM)	5.2	Release blocker		compositeprimarykey		Unreviewed	0	0	0	0	0	0
