|
@@ -7,6 +7,11 @@ from django.db.utils import load_backend
|
|
|
from django.utils.encoding import force_bytes
|
|
|
from django.utils.functional import cached_property
|
|
|
from django.utils.six.moves import input
|
|
|
+from django.utils.six import StringIO
|
|
|
+from django.core.management.commands.dumpdata import sort_dependencies
|
|
|
+from django.db import router
|
|
|
+from django.apps import apps
|
|
|
+from django.core import serializers
|
|
|
|
|
|
from .utils import truncate_name
|
|
|
|
|
@@ -332,7 +337,7 @@ class BaseDatabaseCreation(object):
|
|
|
";",
|
|
|
]
|
|
|
|
|
|
- def create_test_db(self, verbosity=1, autoclobber=False, keepdb=False):
|
|
|
+ def create_test_db(self, verbosity=1, autoclobber=False, keepdb=False, serialize=True):
|
|
|
"""
|
|
|
Creates a test database, prompting the user for confirmation if the
|
|
|
database already exists. Returns the name of the test database created.
|
|
@@ -364,25 +369,31 @@ class BaseDatabaseCreation(object):
|
|
|
settings.DATABASES[self.connection.alias]["NAME"] = test_database_name
|
|
|
self.connection.settings_dict["NAME"] = test_database_name
|
|
|
|
|
|
- # Report migrate messages at one level lower than that requested.
|
|
|
+ # We report migrate messages at one level lower than that requested.
|
|
|
# This ensures we don't get flooded with messages during testing
|
|
|
- # (unless you really ask to be flooded)
|
|
|
- call_command('migrate',
|
|
|
+ # (unless you really ask to be flooded).
|
|
|
+ call_command(
|
|
|
+ 'migrate',
|
|
|
verbosity=max(verbosity - 1, 0),
|
|
|
interactive=False,
|
|
|
database=self.connection.alias,
|
|
|
- load_initial_data=False,
|
|
|
- test_database=True)
|
|
|
-
|
|
|
- # We need to then do a flush to ensure that any data installed by
|
|
|
- # custom SQL has been removed. The only test data should come from
|
|
|
- # test fixtures, or autogenerated from post_migrate triggers.
|
|
|
- # This has the side effect of loading initial data (which was
|
|
|
- # intentionally skipped in the syncdb).
|
|
|
- call_command('flush',
|
|
|
+ test_database=True,
|
|
|
+ )
|
|
|
+
|
|
|
+ # We then serialize the current state of the database into a string
|
|
|
+ # and store it on the connection. This slightly horrific process is so people
|
|
|
+ # who are testing on databases without transactions or who are using
|
|
|
+ # a TransactionTestCase still get a clean database on every test run.
|
|
|
+ if serialize:
|
|
|
+ self.connection._test_serialized_contents = self.serialize_db_to_string()
|
|
|
+
|
|
|
+ # Finally, we flush the database to clean
|
|
|
+ call_command(
|
|
|
+ 'flush',
|
|
|
verbosity=max(verbosity - 1, 0),
|
|
|
interactive=False,
|
|
|
- database=self.connection.alias)
|
|
|
+ database=self.connection.alias
|
|
|
+ )
|
|
|
|
|
|
call_command('createcachetable', database=self.connection.alias)
|
|
|
|
|
@@ -391,6 +402,44 @@ class BaseDatabaseCreation(object):
|
|
|
|
|
|
return test_database_name
|
|
|
|
|
|
+ def serialize_db_to_string(self):
|
|
|
+ """
|
|
|
+ Serializes all data in the database into a JSON string.
|
|
|
+ Designed only for test runner usage; will not handle large
|
|
|
+ amounts of data.
|
|
|
+ """
|
|
|
+ # Build list of all apps to serialize
|
|
|
+ from django.db.migrations.loader import MigrationLoader
|
|
|
+ loader = MigrationLoader(self.connection)
|
|
|
+ app_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))
|
|
|
+ # Make a function to iteratively return every object
|
|
|
+ def get_objects():
|
|
|
+ for model in sort_dependencies(app_list):
|
|
|
+ if not model._meta.proxy and router.allow_migrate(self.connection.alias, model):
|
|
|
+ queryset = model._default_manager.using(self.connection.alias).order_by(model._meta.pk.name)
|
|
|
+ for obj in queryset.iterator():
|
|
|
+ yield obj
|
|
|
+ # Serialise to a string
|
|
|
+ out = StringIO()
|
|
|
+ serializers.serialize("json", get_objects(), indent=None, stream=out)
|
|
|
+ return out.getvalue()
|
|
|
+
|
|
|
+ def deserialize_db_from_string(self, data):
|
|
|
+ """
|
|
|
+ Reloads the database with data from a string generated by
|
|
|
+ the serialize_db_to_string method.
|
|
|
+ """
|
|
|
+ data = StringIO(data)
|
|
|
+ for obj in serializers.deserialize("json", data, using=self.connection.alias):
|
|
|
+ obj.save()
|
|
|
+
|
|
|
def _get_test_db_name(self):
|
|
|
"""
|
|
|
Internal implementation - returns the name of the test DB that will be
|