diff options
| author | Anssi Kääriäinen <akaariai@gmail.com> | 2012-11-29 12:10:31 +0200 |
|---|---|---|
| committer | Anssi Kääriäinen <akaariai@gmail.com> | 2013-03-14 11:01:47 +0200 |
| commit | 6b4834952dcce0db5cbc1534635c00ff8573a6d8 (patch) | |
| tree | fdf79be9ab8152086cf7401b949720ca1597c35e /tests | |
| parent | 8a2f5300b21c3c20a9fc6829d9fcadb023cba4a8 (diff) | |
Fixed #16649 -- Refactored save_base logic
Model.save() will use UPDATE - if not updated - INSERT instead of
SELECT - if found UPDATE else INSERT. This should save a query when
updating, but will cost a little when inserting model with PK set.
Also fixed #17341 -- made sure .save() commits transactions only after
the whole model has been saved. This wasn't the case in model
inheritance situations.
The save_base implementation was refactored into multiple methods.
A typical chain for inherited save is:
save_base()
_save_parents(self)
for each parent:
_save_parents(parent)
_save_table(parent)
_save_table(self)
Diffstat (limited to 'tests')
| -rw-r--r-- | tests/basic/tests.py | 31 | ||||
| -rw-r--r-- | tests/model_inheritance/tests.py | 2 | ||||
| -rw-r--r-- | tests/transactions_regress/models.py | 2 | ||||
| -rw-r--r-- | tests/transactions_regress/tests.py | 22 |
4 files changed, 52 insertions, 5 deletions
diff --git a/tests/basic/tests.py b/tests/basic/tests.py index 2de87a225f..a8005baca7 100644 --- a/tests/basic/tests.py +++ b/tests/basic/tests.py @@ -1,11 +1,13 @@ from __future__ import absolute_import, unicode_literals from datetime import datetime +import threading from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned, FieldError +from django.db import connections, DEFAULT_DB_ALIAS from django.db.models.fields import Field, FieldDoesNotExist from django.db.models.query import QuerySet, EmptyQuerySet, ValuesListQuerySet -from django.test import TestCase, skipIfDBFeature, skipUnlessDBFeature +from django.test import TestCase, TransactionTestCase, skipIfDBFeature, skipUnlessDBFeature from django.utils import six from django.utils.translation import ugettext_lazy @@ -690,4 +692,29 @@ class ModelTest(TestCase): def test_invalid_qs_list(self): qs = Article.objects.order_by('invalid_column') self.assertRaises(FieldError, list, qs) - self.assertRaises(FieldError, list, qs)
\ No newline at end of file + self.assertRaises(FieldError, list, qs) + +class ConcurrentSaveTests(TransactionTestCase): + @skipUnlessDBFeature('test_db_allows_multiple_connections') + def test_concurrent_delete_with_save(self): + """ + Test fetching, deleting and finally saving an object - we should get + an insert in this case. + """ + a = Article.objects.create(headline='foo', pub_date=datetime.now()) + exceptions = [] + def deleter(): + try: + # Do not delete a directly - doing so alters its state. + Article.objects.filter(pk=a.pk).delete() + connections[DEFAULT_DB_ALIAS].commit_unless_managed() + except Exception as e: + exceptions.append(e) + finally: + connections[DEFAULT_DB_ALIAS].close() + self.assertEqual(len(exceptions), 0) + t = threading.Thread(target=deleter) + t.start() + t.join() + a.save() + self.assertEqual(Article.objects.get(pk=a.pk).headline, 'foo') diff --git a/tests/model_inheritance/tests.py b/tests/model_inheritance/tests.py index 62f521c07f..dc40d2d2e0 100644 --- a/tests/model_inheritance/tests.py +++ b/tests/model_inheritance/tests.py @@ -294,7 +294,7 @@ class ModelInheritanceTests(TestCase): rating=4, chef=c ) - with self.assertNumQueries(6): + with self.assertNumQueries(3): ir.save() def test_update_parent_filtering(self): diff --git a/tests/transactions_regress/models.py b/tests/transactions_regress/models.py index a4b576c3ca..e09e81d93d 100644 --- a/tests/transactions_regress/models.py +++ b/tests/transactions_regress/models.py @@ -4,6 +4,8 @@ from django.db import models class Mod(models.Model): fld = models.IntegerField() +class SubMod(Mod): + cnt = models.IntegerField(unique=True) class M2mA(models.Model): others = models.ManyToManyField('M2mB') diff --git a/tests/transactions_regress/tests.py b/tests/transactions_regress/tests.py index 142c09d3cf..e320f76169 100644 --- a/tests/transactions_regress/tests.py +++ b/tests/transactions_regress/tests.py @@ -1,6 +1,7 @@ from __future__ import absolute_import -from django.db import connection, connections, transaction, DEFAULT_DB_ALIAS, DatabaseError +from django.db import (connection, connections, transaction, DEFAULT_DB_ALIAS, DatabaseError, + IntegrityError) from django.db.transaction import commit_on_success, commit_manually, TransactionManagementError from django.test import TransactionTestCase, skipUnlessDBFeature from django.test.utils import override_settings @@ -8,8 +9,25 @@ from django.utils.unittest import skipIf, skipUnless from transactions.tests import IgnorePendingDeprecationWarningsMixin -from .models import Mod, M2mA, M2mB +from .models import Mod, M2mA, M2mB, SubMod +class ModelInheritanceTests(TransactionTestCase): + def test_save(self): + # First, create a SubMod, then try to save another with conflicting + # cnt field. The problem was that transactions were committed after + # every parent save when not in managed transaction. As the cnt + # conflict is in the second model, we can check if the first save + # was committed or not. + SubMod(fld=1, cnt=1).save() + # We should have committed the transaction for the above - assert this. + connection.rollback() + self.assertEqual(SubMod.objects.count(), 1) + try: + SubMod(fld=2, cnt=1).save() + except IntegrityError: + connection.rollback() + self.assertEqual(SubMod.objects.count(), 1) + self.assertEqual(Mod.objects.count(), 1) class TestTransactionClosing(IgnorePendingDeprecationWarningsMixin, TransactionTestCase): """ |
