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.
This commit is contained in:
parent
b330b918e9
commit
98f23a8af0
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue