From ab148c02cedbac492f29930dcd5346e1af052635 Mon Sep 17 00:00:00 2001 From: Simon Charette Date: Sun, 9 Mar 2025 07:04:16 -0400 Subject: Fixed #33579 -- Specialized exception raised on forced update failures. Raising DatabaseError directly made it harder than it should to differentiate between IntegrityError when a forced update resulted in no affected rows. Introducing a specialized exception allows for callers to more easily silence, log, or turn them update failures into user facing exceptions (e.g. 404s). Thanks Mariusz for the review. --- tests/force_insert_update/tests.py | 11 +++++++++-- tests/update_only_fields/tests.py | 16 ++++++++++++++++ 2 files changed, 25 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/force_insert_update/tests.py b/tests/force_insert_update/tests.py index cc223cf3ea..619b8b413d 100644 --- a/tests/force_insert_update/tests.py +++ b/tests/force_insert_update/tests.py @@ -1,3 +1,4 @@ +from django.core.exceptions import ObjectNotUpdated from django.db import DatabaseError, IntegrityError, models, transaction from django.test import TestCase @@ -50,8 +51,14 @@ class ForceTests(TestCase): # the data isn't in the database already. obj = WithCustomPK(name=1, value=1) msg = "Forced update did not affect any rows." - with self.assertRaisesMessage(DatabaseError, msg): - with transaction.atomic(): + # Make sure backward compatibility with DatabaseError is preserved. + exceptions = [DatabaseError, ObjectNotUpdated, WithCustomPK.NotUpdated] + for exception in exceptions: + with ( + self.subTest(exception), + self.assertRaisesMessage(DatabaseError, msg), + transaction.atomic(), + ): obj.save(force_update=True) diff --git a/tests/update_only_fields/tests.py b/tests/update_only_fields/tests.py index 2c86995799..9595c767eb 100644 --- a/tests/update_only_fields/tests.py +++ b/tests/update_only_fields/tests.py @@ -1,3 +1,5 @@ +from django.core.exceptions import ObjectNotUpdated +from django.db import DatabaseError, transaction from django.db.models.signals import post_save, pre_save from django.test import TestCase @@ -292,3 +294,17 @@ class UpdateOnlyFieldsTests(TestCase): employee_boss = Employee.objects.create(name="Boss", gender="F") with self.assertRaisesMessage(ValueError, self.msg % "id"): employee_boss.save(update_fields=["id"]) + + def test_update_fields_not_updated(self): + obj = Person.objects.create(name="Sara", gender="F") + Person.objects.filter(pk=obj.pk).delete() + msg = "Save with update_fields did not affect any rows." + # Make sure backward compatibility with DatabaseError is preserved. + exceptions = [DatabaseError, ObjectNotUpdated, Person.NotUpdated] + for exception in exceptions: + with ( + self.subTest(exception), + self.assertRaisesMessage(DatabaseError, msg), + transaction.atomic(), + ): + obj.save(update_fields=["name"]) -- cgit v1.3