summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthijs Kooijman <matthijs@stdin.nl>2019-12-02 01:16:32 +0100
committerMariusz Felisiak <felisiak.mariusz@gmail.com>2020-04-02 11:35:33 +0200
commit289d0ec6fd4c7e3d68b8a8e4f833e719e3dbe205 (patch)
treeaad7446a8cdd42e2c140ad782e8d02e0e8d8ec58
parent12e6f573adbf825c74042f15b70600341220674b (diff)
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.
-rw-r--r--django/db/backends/base/creation.py8
-rw-r--r--tests/backends/base/test_creation.py27
-rw-r--r--tests/backends/models.py16
3 files changed, 46 insertions, 5 deletions
diff --git a/django/db/backends/base/creation.py b/django/db/backends/base/creation.py
index 230954ddfc..a19ec48419 100644
--- a/django/db/backends/base/creation.py
+++ b/django/db/backends/base/creation.py
@@ -97,21 +97,21 @@ class BaseDatabaseCreation:
Designed only for test runner usage; will not handle large
amounts of data.
"""
- # Build list of all apps to serialize
+ # Build list of all models to serialize.
from django.db.migrations.loader import MigrationLoader
loader = MigrationLoader(self.connection)
- app_list = []
+ model_list = []
for app_config in apps.get_app_configs():
if (
app_config.models_module is not None and
app_config.label in loader.migrated_apps and
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
def get_objects():
- for model in serializers.sort_dependencies(app_list):
+ for model in model_list:
if (model._meta.can_migrate(self.connection) and
router.allow_migrate_model(self.connection.alias, model)):
queryset = model._default_manager.using(self.connection.alias).order_by(model._meta.pk.name)
diff --git a/tests/backends/base/test_creation.py b/tests/backends/base/test_creation.py
index 19a3915021..01215a9a5b 100644
--- a/tests/backends/base/test_creation.py
+++ b/tests/backends/base/test_creation.py
@@ -7,7 +7,9 @@ from django.db.backends.base.creation import (
)
from django.test import SimpleTestCase, TransactionTestCase
-from ..models import Object, ObjectReference, ObjectSelfReference
+from ..models import (
+ CircularA, CircularB, Object, ObjectReference, ObjectSelfReference,
+)
def get_connection_copy():
@@ -123,3 +125,26 @@ class TestDeserializeDbFromString(TransactionTestCase):
obj_2 = ObjectSelfReference.objects.get(key='Y')
self.assertEqual(obj_1.obj, obj_2)
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)
diff --git a/tests/backends/models.py b/tests/backends/models.py
index 87809b84a8..096fdb57cc 100644
--- a/tests/backends/models.py
+++ b/tests/backends/models.py
@@ -107,6 +107,22 @@ class ObjectSelfReference(models.Model):
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):
raw_data = models.BinaryField()