Browse Source

Fixed #35744 -- Relabelled external aliases of combined queries.

Just like normal queries, combined queries' outer references might fully
resolve before their reference is assigned its final alias.

Refs #29338.

Thanks Antony_K for the report and example, and thanks Mariusz Felisiak
for the review.
Simon Charette 5 tháng trước cách đây
mục cha
commit
53ea4cce2f
2 tập tin đã thay đổi với 41 bổ sung1 xóa
  1. 10 0
      django/db/models/sql/query.py
  2. 31 1
      tests/queries/test_qs_combinators.py

+ 10 - 0
django/db/models/sql/query.py

@@ -1021,11 +1021,21 @@ class Query(BaseExpression):
                 if alias == old_alias:
                     table_aliases[pos] = new_alias
                     break
+
+        # 3. Rename the direct external aliases and the ones of combined
+        # queries (union, intersection, difference).
         self.external_aliases = {
             # Table is aliased or it's being changed and thus is aliased.
             change_map.get(alias, alias): (aliased or alias in change_map)
             for alias, aliased in self.external_aliases.items()
         }
+        for combined_query in self.combined_queries:
+            external_change_map = {
+                alias: aliased
+                for alias, aliased in change_map.items()
+                if alias in combined_query.external_aliases
+            }
+            combined_query.change_aliases(external_change_map)
 
     def bump_prefix(self, other_query, exclude=None):
         """

+ 31 - 1
tests/queries/test_qs_combinators.py

@@ -14,7 +14,16 @@ from django.db.models.functions import Mod
 from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature
 from django.test.utils import CaptureQueriesContext
 
-from .models import Author, Celebrity, ExtraInfo, Number, ReservedName
+from .models import (
+    Annotation,
+    Author,
+    Celebrity,
+    ExtraInfo,
+    Note,
+    Number,
+    ReservedName,
+    Tag,
+)
 
 
 @skipUnlessDBFeature("supports_select_union")
@@ -450,6 +459,27 @@ class QuerySetSetOperationTests(TestCase):
             [8, 1],
         )
 
+    @skipUnlessDBFeature("supports_select_intersection")
+    def test_intersection_in_nested_subquery(self):
+        tag = Tag.objects.create(name="tag")
+        note = Note.objects.create(tag=tag)
+        annotation = Annotation.objects.create(tag=tag)
+        tags = Tag.objects.order_by()
+        tags = tags.filter(id=OuterRef("tag_id")).intersection(
+            tags.filter(id=OuterRef(OuterRef("tag_id")))
+        )
+        qs = Note.objects.filter(
+            Exists(
+                Annotation.objects.filter(
+                    Exists(tags),
+                    notes__in=OuterRef("pk"),
+                )
+            )
+        )
+        self.assertIsNone(qs.first())
+        annotation.notes.add(note)
+        self.assertEqual(qs.first(), note)
+
     def test_union_in_subquery_related_outerref(self):
         e1 = ExtraInfo.objects.create(value=7, info="e3")
         e2 = ExtraInfo.objects.create(value=5, info="e2")