summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorMarkus Holtermann <info@markusholtermann.eu>2015-08-27 09:49:35 +1000
committerMarkus Holtermann <info@markusholtermann.eu>2015-09-19 14:54:53 +1000
commit5aa55038ca9ac44b440b56d1fc4e79c876e51393 (patch)
tree1f6a8fd00c0d796135daee79124fe6e520937332 /tests
parent186eb21dc159807dba83148f7c9c50d470745708 (diff)
Fixed #24743, #24745 -- Optimized migration plan handling
The change partly goes back to the old behavior for forwards migrations which should reduce the amount of memory consumption (#24745). However, by the way the current state computation is done (there is no `state_backwards` on a migration class) this change cannot be applied to backwards migrations. Hence rolling back migrations still requires the precomputation and storage of the intermediate migration states. This improvement also implies that Django does not handle mixed migration plans anymore. Mixed plans consist of a list of migrations where some are being applied and others are being unapplied. Thanks Andrew Godwin, Josh Smeaton and Tim Graham for the review as well as everybody involved on the ticket that kept me looking into the issue.
Diffstat (limited to 'tests')
-rw-r--r--tests/migrations/test_executor.py59
-rw-r--r--tests/migrations2/test_migrations_2_no_deps/0001_initial.py24
-rw-r--r--tests/migrations2/test_migrations_2_no_deps/__init__.py0
3 files changed, 83 insertions, 0 deletions
diff --git a/tests/migrations/test_executor.py b/tests/migrations/test_executor.py
index 12bdabb172..32cdc5e09c 100644
--- a/tests/migrations/test_executor.py
+++ b/tests/migrations/test_executor.py
@@ -1,5 +1,6 @@
from django.apps.registry import apps as global_apps
from django.db import connection
+from django.db.migrations.exceptions import InvalidMigrationPlan
from django.db.migrations.executor import MigrationExecutor
from django.db.migrations.graph import MigrationGraph
from django.db.migrations.recorder import MigrationRecorder
@@ -145,6 +146,64 @@ class ExecutorTests(MigrationTestBase):
executor.recorder.record_unapplied("migrations", "0002_second")
executor.recorder.record_unapplied("migrations", "0001_initial")
+ @override_settings(MIGRATION_MODULES={
+ "migrations": "migrations.test_migrations",
+ "migrations2": "migrations2.test_migrations_2_no_deps",
+ })
+ def test_mixed_plan_not_supported(self):
+ """
+ Although the MigrationExecutor interfaces allows for mixed migration
+ plans (combined forwards and backwards migrations) this is not
+ supported.
+ """
+ # Prepare for mixed plan
+ executor = MigrationExecutor(connection)
+ plan = executor.migration_plan([("migrations", "0002_second")])
+ self.assertEqual(
+ plan,
+ [
+ (executor.loader.graph.nodes["migrations", "0001_initial"], False),
+ (executor.loader.graph.nodes["migrations", "0002_second"], False),
+ ],
+ )
+ executor.migrate(None, plan)
+ # Rebuild the graph to reflect the new DB state
+ executor.loader.build_graph()
+ self.assertIn(('migrations', '0001_initial'), executor.loader.applied_migrations)
+ self.assertIn(('migrations', '0002_second'), executor.loader.applied_migrations)
+ self.assertNotIn(('migrations2', '0001_initial'), executor.loader.applied_migrations)
+
+ # Generate mixed plan
+ plan = executor.migration_plan([
+ ("migrations", None),
+ ("migrations2", "0001_initial"),
+ ])
+ msg = (
+ 'Migration plans with both forwards and backwards migrations are '
+ 'not supported. Please split your migration process into separate '
+ 'plans of only forwards OR backwards migrations.'
+ )
+ with self.assertRaisesMessage(InvalidMigrationPlan, msg) as cm:
+ executor.migrate(None, plan)
+ self.assertEqual(
+ cm.exception.args[1],
+ [
+ (executor.loader.graph.nodes["migrations", "0002_second"], True),
+ (executor.loader.graph.nodes["migrations", "0001_initial"], True),
+ (executor.loader.graph.nodes["migrations2", "0001_initial"], False),
+ ],
+ )
+ # Rebuild the graph to reflect the new DB state
+ executor.loader.build_graph()
+ executor.migrate([
+ ("migrations", None),
+ ("migrations2", None),
+ ])
+ # Are the tables gone?
+ self.assertTableNotExists("migrations_author")
+ self.assertTableNotExists("migrations_book")
+ self.assertTableNotExists("migrations2_otherauthor")
+
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"})
def test_soft_apply(self):
"""
diff --git a/tests/migrations2/test_migrations_2_no_deps/0001_initial.py b/tests/migrations2/test_migrations_2_no_deps/0001_initial.py
new file mode 100644
index 0000000000..2213706594
--- /dev/null
+++ b/tests/migrations2/test_migrations_2_no_deps/0001_initial.py
@@ -0,0 +1,24 @@
+# -*- coding: utf-8 -*-
+from __future__ import unicode_literals
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = []
+
+ operations = [
+
+ migrations.CreateModel(
+ "OtherAuthor",
+ [
+ ("id", models.AutoField(primary_key=True)),
+ ("name", models.CharField(max_length=255)),
+ ("slug", models.SlugField(null=True)),
+ ("age", models.IntegerField(default=0)),
+ ("silly_field", models.BooleanField(default=False)),
+ ],
+ ),
+
+ ]
diff --git a/tests/migrations2/test_migrations_2_no_deps/__init__.py b/tests/migrations2/test_migrations_2_no_deps/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/tests/migrations2/test_migrations_2_no_deps/__init__.py