Explorar o código

Fixed #34803 -- Fixed queryset crash when filtering againts deeply nested OuterRef annotations.

Thanks Pierre-Nicolas Rigal for the report.

Regression in c67ea79aa981ae82595d89f8018a41fcd842e7c9.
willzhao hai 1 ano
pai
achega
9cc0d7f7f8

+ 1 - 0
AUTHORS

@@ -1033,6 +1033,7 @@ answer newbie questions, and generally made Django that much better:
     Will Ayd <william.ayd@icloud.com>
     William Schwartz <wkschwartz@gmail.com>
     Will Hardy <django@willhardy.com.au>
+    Will Zhao <www.gnomeek@gmail.com>
     Wilson Miner <wminer@gmail.com>
     Wim Glenn <hey@wimglenn.com>
     wojtek

+ 1 - 0
django/db/models/expressions.py

@@ -885,6 +885,7 @@ class ResolvedOuterRef(F):
 
 class OuterRef(F):
     contains_aggregate = False
+    contains_over_clause = False
 
     def resolve_expression(self, *args, **kwargs):
         if isinstance(self.name, self.__class__):

+ 3 - 0
docs/releases/4.2.5.txt

@@ -21,3 +21,6 @@ Bugfixes
 * Fixed a regression in Django 4.2.2 that caused an unnecessary selection of a
   non-nullable ``ManyToManyField`` without a natural key during serialization
   (:ticket:`34779`).
+
+* Fixed a regression in Django 4.2 that caused a crash of a queryset when
+  filtering against deeply nested ``OuterRef()`` annotations (:ticket:`34803`).

+ 4 - 0
tests/expressions/models.py

@@ -8,6 +8,9 @@ from django.db import models
 
 class Manager(models.Model):
     name = models.CharField(max_length=50)
+    secretary = models.ForeignKey(
+        "Employee", models.CASCADE, null=True, related_name="managers"
+    )
 
 
 class Employee(models.Model):
@@ -15,6 +18,7 @@ class Employee(models.Model):
     lastname = models.CharField(max_length=50)
     salary = models.IntegerField(blank=True, null=True)
     manager = models.ForeignKey(Manager, models.CASCADE, null=True)
+    based_in_eu = models.BooleanField(default=False)
 
     def __str__(self):
         return "%s %s" % (self.firstname, self.lastname)

+ 30 - 0
tests/expressions/tests.py

@@ -870,6 +870,36 @@ class BasicExpressionsTests(TestCase):
         ).filter(ceo_company__isnull=False)
         self.assertEqual(qs.get().ceo_company, "Test GmbH")
 
+    def test_annotation_with_deeply_nested_outerref(self):
+        bob = Employee.objects.create(firstname="Bob", based_in_eu=True)
+        self.max.manager = Manager.objects.create(name="Rock", secretary=bob)
+        self.max.save()
+        qs = Employee.objects.filter(
+            Exists(
+                Manager.objects.filter(
+                    Exists(
+                        Employee.objects.filter(
+                            pk=OuterRef("secretary__pk"),
+                        )
+                        .annotate(
+                            secretary_based_in_eu=OuterRef(OuterRef("based_in_eu"))
+                        )
+                        .filter(
+                            Exists(
+                                Company.objects.filter(
+                                    # Inner OuterRef refers to an outer
+                                    # OuterRef (not ResolvedOuterRef).
+                                    based_in_eu=OuterRef("secretary_based_in_eu")
+                                )
+                            )
+                        )
+                    ),
+                    secretary__pk=OuterRef("pk"),
+                )
+            )
+        )
+        self.assertEqual(qs.get(), bob)
+
     def test_pickle_expression(self):
         expr = Value(1)
         expr.convert_value  # populate cached property