From 2ffa815c734e12262a3cb8ce5664f297128ae47f Mon Sep 17 00:00:00 2001 From: hb6h057 Date: Fri, 17 Mar 2023 00:03:32 +0800 Subject: [PATCH] Fixed #34421 -- Fixed QuerySet.update() on querysets in descending order by annotations. --- django/db/backends/mysql/features.py | 1 + django/db/models/query.py | 9 ++++++++- tests/update/tests.py | 15 +++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/django/db/backends/mysql/features.py b/django/db/backends/mysql/features.py index c657860b5d..0b8d12d2fe 100644 --- a/django/db/backends/mysql/features.py +++ b/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 ( diff --git a/django/db/models/query.py b/django/db/models/query.py index 6ac2a4cb10..56ad4d5c20 100644 --- a/django/db/models/query.py +++ b/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) diff --git a/tests/update/tests.py b/tests/update/tests.py index e88eeda96d..079e00818a 100644 --- a/tests/update/tests.py +++ b/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.