Pārlūkot izejas kodu

Fixed #34421 -- Fixed QuerySet.update() on querysets in descending order by annotations.

hb6h057 2 gadi atpakaļ
vecāks
revīzija
2ffa815c73

+ 1 - 0
django/db/backends/mysql/features.py

@@ -108,6 +108,7 @@ class DatabaseFeatures(BaseDatabaseFeatures):
                 "update.tests.AdvancedTests."
                 "test_update_ordered_by_inline_m2m_annotation",
                 "update.tests.AdvancedTests.test_update_ordered_by_m2m_annotation",
+                "update.tests.AdvancedTests.test_update_ordered_by_m2m_annotation_desc",
             },
         }
         if self.connection.mysql_is_mariadb and (

+ 8 - 1
django/db/models/query.py

@@ -1190,11 +1190,18 @@ class QuerySet(AltersData):
         # Inline annotations in order_by(), if possible.
         new_order_by = []
         for col in query.order_by:
-            if annotation := query.annotations.get(col):
+            alias = col
+            descending = False
+            if isinstance(alias, str) and alias.startswith("-"):
+                alias = alias.removeprefix("-")
+                descending = True
+            if annotation := query.annotations.get(alias):
                 if getattr(annotation, "contains_aggregate", False):
                     raise exceptions.FieldError(
                         f"Cannot update when ordering by an aggregate: {annotation}"
                     )
+                if descending:
+                    annotation = annotation.desc()
                 new_order_by.append(annotation)
             else:
                 new_order_by.append(col)

+ 15 - 0
tests/update/tests.py

@@ -249,6 +249,13 @@ class AdvancedTests(TestCase):
         Bar.objects.annotate(abs_id=Abs("m2m_foo")).order_by("abs_id").update(x=3)
         self.assertEqual(Bar.objects.get().x, 3)
 
+    def test_update_ordered_by_m2m_annotation_desc(self):
+        foo = Foo.objects.create(target="test")
+        Bar.objects.create(foo=foo)
+
+        Bar.objects.annotate(abs_id=Abs("m2m_foo")).order_by("-abs_id").update(x=4)
+        self.assertEqual(Bar.objects.get().x, 4)
+
     def test_update_negated_f(self):
         DataPoint.objects.update(is_active=~F("is_active"))
         self.assertCountEqual(
@@ -309,6 +316,14 @@ class MySQLUpdateOrderByTest(TestCase):
         )
         self.assertEqual(updated, 2)
 
+    def test_order_by_update_on_unique_constraint_annotation_desc(self):
+        updated = (
+            UniqueNumber.objects.annotate(number_annotation=F("number"))
+            .order_by("-number_annotation")
+            .update(number=F("number") + 1)
+        )
+        self.assertEqual(updated, 2)
+
     def test_order_by_update_on_parent_unique_constraint(self):
         # Ordering by inherited fields is omitted because joined fields cannot
         # be used in the ORDER BY clause.