From c56c42b5c01ed774cfa8e044cd372a984608536c Mon Sep 17 00:00:00 2001 From: Jozef Knaperek Date: Fri, 11 Jul 2014 22:11:04 +0200 Subject: [PATCH] Fixed #22967 -- Made Model._do_update consistent Made _do_update behave more strictly according to its docs, including a corner case when specific concurent updates are executed and select_on_save is set. --- django/db/models/base.py | 10 ++++++++-- tests/basic/tests.py | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/django/db/models/base.py b/django/db/models/base.py index d416b65ccf..f2404d40a5 100644 --- a/django/db/models/base.py +++ b/django/db/models/base.py @@ -757,8 +757,14 @@ class Model(six.with_metaclass(ModelBase)): return update_fields is not None or filtered.exists() if self._meta.select_on_save and not forced_update: if filtered.exists(): - filtered._update(values) - return True + # It may happen that the object is deleted from the DB right after + # this check, causing the subsequent UPDATE to return zero matching + # rows. The same result can occur in some rare cases when the + # database returns zero despite the UPDATE being executed + # successfully (a row is matched and updated). In order to + # distinguish these two cases, the object's existence in the + # database is again checked for if the UPDATE query returns 0. + return filtered._update(values) > 0 or filtered.exists() else: return False return filtered._update(values) > 0 diff --git a/tests/basic/tests.py b/tests/basic/tests.py index 4f3e8a958f..a10e38f2a4 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -701,7 +701,7 @@ class SelectOnSaveTests(TestCase): try: Article._base_manager.__class__ = FakeManager asos = ArticleSelectOnSave.objects.create(pub_date=datetime.now()) - with self.assertNumQueries(2): + with self.assertNumQueries(3): asos.save() self.assertTrue(FakeQuerySet.called) # This is not wanted behavior, but this is how Django has always