diff options
| author | Matthijs Kooijman <matthijs@stdin.nl> | 2019-12-02 00:42:06 +0100 |
|---|---|---|
| committer | Mariusz Felisiak <felisiak.mariusz@gmail.com> | 2020-02-14 13:18:06 +0100 |
| commit | 98f23a8af0be7e87535426c5c83058e2682bfdf8 (patch) | |
| tree | 5ac7e0c05a222d176fec8ae13c578d33dbdb6f3f | |
| parent | b330b918e979ea39a21d47b61172d112caf432c3 (diff) | |
Fixed #26552 -- Deferred constraint checks when reloading the database with data for tests.
deserialize_db_from_string() loads the full serialized database
contents, which might contain forward references and cycles. That
caused IntegrityError because constraints were checked immediately.
Now, it loads data in a transaction with constraint checks deferred
until the end of the transaction.
| -rw-r--r-- | django/db/backends/base/creation.py | 13 | ||||
| -rw-r--r-- | tests/backends/base/test_creation.py | 28 | ||||
| -rw-r--r-- | tests/backends/models.py | 1 |
3 files changed, 40 insertions, 2 deletions
diff --git a/django/db/backends/base/creation.py b/django/db/backends/base/creation.py index c50fc90171..230954ddfc 100644 --- a/django/db/backends/base/creation.py +++ b/django/db/backends/base/creation.py @@ -6,6 +6,7 @@ from django.apps import apps from django.conf import settings from django.core import serializers from django.db import router +from django.db.transaction import atomic # The prefix to put on the default database name when creating # the test database. @@ -126,8 +127,16 @@ class BaseDatabaseCreation: the serialize_db_to_string() method. """ data = StringIO(data) - for obj in serializers.deserialize("json", data, using=self.connection.alias): - obj.save() + # Load data in a transaction to handle forward references and cycles. + with atomic(using=self.connection.alias): + # Disable constraint checks, because some databases (MySQL) doesn't + # support deferred checks. + with self.connection.constraint_checks_disabled(): + for obj in serializers.deserialize('json', data, using=self.connection.alias): + obj.save() + # Manually check for any invalid keys that might have been added, + # because constraint checks were disabled. + self.connection.check_constraints() def _get_database_display_str(self, verbosity, database_name): """ diff --git a/tests/backends/base/test_creation.py b/tests/backends/base/test_creation.py index b91466911a..f627f2e7c8 100644 --- a/tests/backends/base/test_creation.py +++ b/tests/backends/base/test_creation.py @@ -7,6 +7,8 @@ from django.db.backends.base.creation import ( ) from django.test import SimpleTestCase +from ..models import Object, ObjectReference + def get_connection_copy(): # Get a copy of the default connection. (Can't use django.db.connection @@ -73,3 +75,29 @@ class TestDbCreationTests(SimpleTestCase): finally: with mock.patch.object(creation, '_destroy_test_db'): creation.destroy_test_db(old_database_name, verbosity=0) + + +class TestDeserializeDbFromString(SimpleTestCase): + databases = {'default'} + + def test_circular_reference(self): + # deserialize_db_from_string() handles circular references. + data = """ + [ + { + "model": "backends.object", + "pk": 1, + "fields": {"obj_ref": 1, "related_objects": []} + }, + { + "model": "backends.objectreference", + "pk": 1, + "fields": {"obj": 1} + } + ] + """ + connection.creation.deserialize_db_from_string(data) + obj = Object.objects.get() + obj_ref = ObjectReference.objects.get() + self.assertEqual(obj.obj_ref, obj_ref) + self.assertEqual(obj_ref.obj, obj) diff --git a/tests/backends/models.py b/tests/backends/models.py index 1fa8d44e63..277a3a1203 100644 --- a/tests/backends/models.py +++ b/tests/backends/models.py @@ -89,6 +89,7 @@ class Item(models.Model): class Object(models.Model): related_objects = models.ManyToManyField("self", db_constraint=False, symmetrical=False) + obj_ref = models.ForeignKey('ObjectReference', models.CASCADE, null=True) def __str__(self): return str(self.id) |
