summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorAnssi Kääriäinen <akaariai@gmail.com>2012-11-29 12:10:31 +0200
committerAnssi Kääriäinen <akaariai@gmail.com>2013-03-14 11:01:47 +0200
commit6b4834952dcce0db5cbc1534635c00ff8573a6d8 (patch)
treefdf79be9ab8152086cf7401b949720ca1597c35e /tests
parent8a2f5300b21c3c20a9fc6829d9fcadb023cba4a8 (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.py31
-rw-r--r--tests/model_inheritance/tests.py2
-rw-r--r--tests/transactions_regress/models.py2
-rw-r--r--tests/transactions_regress/tests.py22
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):
"""