ソースを参照

Fixed #34227 -- Fixed QuerySet.select_related() with multi-level FilteredRelation.

朱穆穆 2 年 前
コミット
d3c93cdc59
2 ファイル変更28 行追加1 行削除
  1. 8 1
      django/db/models/sql/compiler.py
  2. 20 0
      tests/known_related_objects/tests.py

+ 8 - 1
django/db/models/sql/compiler.py

@@ -1274,6 +1274,9 @@ class SQLCompiler:
                 if from_obj:
                     final_field.remote_field.set_cached_value(from_obj, obj)
 
+            def local_setter_noop(obj, from_obj):
+                pass
+
             def remote_setter(name, obj, from_obj):
                 setattr(from_obj, name, obj)
 
@@ -1295,7 +1298,11 @@ class SQLCompiler:
                         "model": model,
                         "field": final_field,
                         "reverse": True,
-                        "local_setter": partial(local_setter, final_field),
+                        "local_setter": (
+                            partial(local_setter, final_field)
+                            if len(joins) <= 2
+                            else local_setter_noop
+                        ),
                         "remote_setter": partial(remote_setter, name),
                         "from_parent": from_parent,
                     }

+ 20 - 0
tests/known_related_objects/tests.py

@@ -164,3 +164,23 @@ class ExistingRelatedInstancesTests(TestCase):
             )
             self.assertIs(ps[0], ps[0].pool_1.poolstyle)
             self.assertIs(ps[0], ps[0].pool_2.another_style)
+
+    def test_multilevel_reverse_fk_cyclic_select_related(self):
+        with self.assertNumQueries(3):
+            p = list(
+                PoolStyle.objects.annotate(
+                    tournament_pool=FilteredRelation("pool__tournament__pool"),
+                ).select_related("tournament_pool", "tournament_pool__tournament")
+            )
+            self.assertEqual(p[0].tournament_pool.tournament, p[0].pool.tournament)
+
+    def test_multilevel_reverse_fk_select_related(self):
+        with self.assertNumQueries(2):
+            p = list(
+                Tournament.objects.filter(id=self.t2.id)
+                .annotate(
+                    style=FilteredRelation("pool__another_style"),
+                )
+                .select_related("style")
+            )
+            self.assertEqual(p[0].style.another_pool, self.p3)