mirror of
https://github.com/django/django.git
synced 2025-10-26 23:26:08 +00:00
[1.6.x] Fixed #20988 -- Added model meta option select_on_save
The option can be used to force pre 1.6 style SELECT on save behaviour.
This is needed in case the database returns zero updated rows even if
there is a matching row in the DB. One such case is PostgreSQL update
trigger that returns NULL.
Reviewed by Tim Graham.
Refs #16649
Backport of e973ee6a98 from master
Conflicts:
django/db/models/options.py
tests/basic/tests.py
This commit is contained in:
@@ -5,13 +5,14 @@ import threading
|
||||
|
||||
from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned
|
||||
from django.db import connections, DEFAULT_DB_ALIAS
|
||||
from django.db import DatabaseError
|
||||
from django.db.models.fields import Field, FieldDoesNotExist
|
||||
from django.db.models.query import QuerySet, EmptyQuerySet, ValuesListQuerySet
|
||||
from django.test import TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature
|
||||
from django.utils import six
|
||||
from django.utils.translation import ugettext_lazy
|
||||
|
||||
from .models import Article, SelfRef
|
||||
from .models import Article, SelfRef, ArticleSelectOnSave
|
||||
|
||||
|
||||
class ModelTest(TestCase):
|
||||
@@ -712,3 +713,61 @@ class ConcurrentSaveTests(TransactionTestCase):
|
||||
t.join()
|
||||
a.save()
|
||||
self.assertEqual(Article.objects.get(pk=a.pk).headline, 'foo')
|
||||
|
||||
|
||||
class SelectOnSaveTests(TestCase):
|
||||
def test_select_on_save(self):
|
||||
a1 = Article.objects.create(pub_date=datetime.now())
|
||||
with self.assertNumQueries(1):
|
||||
a1.save()
|
||||
asos = ArticleSelectOnSave.objects.create(pub_date=datetime.now())
|
||||
with self.assertNumQueries(2):
|
||||
asos.save()
|
||||
with self.assertNumQueries(1):
|
||||
asos.save(force_update=True)
|
||||
Article.objects.all().delete()
|
||||
with self.assertRaises(DatabaseError):
|
||||
with self.assertNumQueries(1):
|
||||
asos.save(force_update=True)
|
||||
|
||||
def test_select_on_save_lying_update(self):
|
||||
"""
|
||||
Test that select_on_save works correctly if the database
|
||||
doesn't return correct information about matched rows from
|
||||
UPDATE.
|
||||
"""
|
||||
# Change the manager to not return "row matched" for update().
|
||||
# We are going to change the Article's _base_manager class
|
||||
# dynamically. This is a bit of a hack, but it seems hard to
|
||||
# test this properly otherwise. Article's manager, because
|
||||
# proxy models use their parent model's _base_manager.
|
||||
|
||||
orig_class = Article._base_manager.__class__
|
||||
|
||||
class FakeQuerySet(QuerySet):
|
||||
# Make sure the _update method below is in fact called.
|
||||
called = False
|
||||
|
||||
def _update(self, *args, **kwargs):
|
||||
FakeQuerySet.called = True
|
||||
super(FakeQuerySet, self)._update(*args, **kwargs)
|
||||
return 0
|
||||
|
||||
class FakeManager(orig_class):
|
||||
def get_queryset(self):
|
||||
return FakeQuerySet(self.model)
|
||||
try:
|
||||
Article._base_manager.__class__ = FakeManager
|
||||
asos = ArticleSelectOnSave.objects.create(pub_date=datetime.now())
|
||||
with self.assertNumQueries(2):
|
||||
asos.save()
|
||||
self.assertTrue(FakeQuerySet.called)
|
||||
# This is not wanted behaviour, but this is how Django has always
|
||||
# behaved for databases that do not return correct information
|
||||
# about matched rows for UPDATE.
|
||||
with self.assertRaises(DatabaseError):
|
||||
asos.save(force_update=True)
|
||||
with self.assertRaises(DatabaseError):
|
||||
asos.save(update_fields=['pub_date'])
|
||||
finally:
|
||||
Article._base_manager.__class__ = orig_class
|
||||
|
||||
Reference in New Issue
Block a user