Refs #31051 -- Fixed reloading the database with circular related objects and natural keys for tests.
Made deserialize_db_from_string() do not sort dependencies. deserialize_db_from_string() doesn't use natural keys, so there is no need to sort dependencies in serialize_db_to_string(). Moreover, sorting models cause issues for circular dependencies.
This commit is contained in:
parent
12e6f573ad
commit
289d0ec6fd
|
@ -97,21 +97,21 @@ class BaseDatabaseCreation:
|
||||||
Designed only for test runner usage; will not handle large
|
Designed only for test runner usage; will not handle large
|
||||||
amounts of data.
|
amounts of data.
|
||||||
"""
|
"""
|
||||||
# Build list of all apps to serialize
|
# Build list of all models to serialize.
|
||||||
from django.db.migrations.loader import MigrationLoader
|
from django.db.migrations.loader import MigrationLoader
|
||||||
loader = MigrationLoader(self.connection)
|
loader = MigrationLoader(self.connection)
|
||||||
app_list = []
|
model_list = []
|
||||||
for app_config in apps.get_app_configs():
|
for app_config in apps.get_app_configs():
|
||||||
if (
|
if (
|
||||||
app_config.models_module is not None and
|
app_config.models_module is not None and
|
||||||
app_config.label in loader.migrated_apps and
|
app_config.label in loader.migrated_apps and
|
||||||
app_config.name not in settings.TEST_NON_SERIALIZED_APPS
|
app_config.name not in settings.TEST_NON_SERIALIZED_APPS
|
||||||
):
|
):
|
||||||
app_list.append((app_config, None))
|
model_list.extend(app_config.get_models())
|
||||||
|
|
||||||
# Make a function to iteratively return every object
|
# Make a function to iteratively return every object
|
||||||
def get_objects():
|
def get_objects():
|
||||||
for model in serializers.sort_dependencies(app_list):
|
for model in model_list:
|
||||||
if (model._meta.can_migrate(self.connection) and
|
if (model._meta.can_migrate(self.connection) and
|
||||||
router.allow_migrate_model(self.connection.alias, model)):
|
router.allow_migrate_model(self.connection.alias, model)):
|
||||||
queryset = model._default_manager.using(self.connection.alias).order_by(model._meta.pk.name)
|
queryset = model._default_manager.using(self.connection.alias).order_by(model._meta.pk.name)
|
||||||
|
|
|
@ -7,7 +7,9 @@ from django.db.backends.base.creation import (
|
||||||
)
|
)
|
||||||
from django.test import SimpleTestCase, TransactionTestCase
|
from django.test import SimpleTestCase, TransactionTestCase
|
||||||
|
|
||||||
from ..models import Object, ObjectReference, ObjectSelfReference
|
from ..models import (
|
||||||
|
CircularA, CircularB, Object, ObjectReference, ObjectSelfReference,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def get_connection_copy():
|
def get_connection_copy():
|
||||||
|
@ -123,3 +125,26 @@ class TestDeserializeDbFromString(TransactionTestCase):
|
||||||
obj_2 = ObjectSelfReference.objects.get(key='Y')
|
obj_2 = ObjectSelfReference.objects.get(key='Y')
|
||||||
self.assertEqual(obj_1.obj, obj_2)
|
self.assertEqual(obj_1.obj, obj_2)
|
||||||
self.assertEqual(obj_2.obj, obj_1)
|
self.assertEqual(obj_2.obj, obj_1)
|
||||||
|
|
||||||
|
def test_circular_reference_with_natural_key(self):
|
||||||
|
# serialize_db_to_string() and deserialize_db_from_string() handles
|
||||||
|
# circular references for models with natural keys.
|
||||||
|
obj_a = CircularA.objects.create(key='A')
|
||||||
|
obj_b = CircularB.objects.create(key='B', obj=obj_a)
|
||||||
|
obj_a.obj = obj_b
|
||||||
|
obj_a.save()
|
||||||
|
# Serialize objects.
|
||||||
|
with mock.patch('django.db.migrations.loader.MigrationLoader') as loader:
|
||||||
|
# serialize_db_to_string() serializes only migrated apps, so mark
|
||||||
|
# the backends app as migrated.
|
||||||
|
loader_instance = loader.return_value
|
||||||
|
loader_instance.migrated_apps = {'backends'}
|
||||||
|
data = connection.creation.serialize_db_to_string()
|
||||||
|
CircularA.objects.all().delete()
|
||||||
|
CircularB.objects.all().delete()
|
||||||
|
# Deserialize objects.
|
||||||
|
connection.creation.deserialize_db_from_string(data)
|
||||||
|
obj_a = CircularA.objects.get()
|
||||||
|
obj_b = CircularB.objects.get()
|
||||||
|
self.assertEqual(obj_a.obj, obj_b)
|
||||||
|
self.assertEqual(obj_b.obj, obj_a)
|
||||||
|
|
|
@ -107,6 +107,22 @@ class ObjectSelfReference(models.Model):
|
||||||
obj = models.ForeignKey('ObjectSelfReference', models.SET_NULL, null=True)
|
obj = models.ForeignKey('ObjectSelfReference', models.SET_NULL, null=True)
|
||||||
|
|
||||||
|
|
||||||
|
class CircularA(models.Model):
|
||||||
|
key = models.CharField(max_length=3, unique=True)
|
||||||
|
obj = models.ForeignKey('CircularB', models.SET_NULL, null=True)
|
||||||
|
|
||||||
|
def natural_key(self):
|
||||||
|
return (self.key,)
|
||||||
|
|
||||||
|
|
||||||
|
class CircularB(models.Model):
|
||||||
|
key = models.CharField(max_length=3, unique=True)
|
||||||
|
obj = models.ForeignKey('CircularA', models.SET_NULL, null=True)
|
||||||
|
|
||||||
|
def natural_key(self):
|
||||||
|
return (self.key,)
|
||||||
|
|
||||||
|
|
||||||
class RawData(models.Model):
|
class RawData(models.Model):
|
||||||
raw_data = models.BinaryField()
|
raw_data = models.BinaryField()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue