summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorMarkus Holtermann <info@markusholtermann.eu>2015-02-12 12:48:28 +0100
committerMarkus Holtermann <info@markusholtermann.eu>2015-02-13 14:29:59 +0100
commitf287bec5833d75750fa6368bc2802741b7924533 (patch)
tree9ab7db3bc9a1834ead7366c0062b9fb39f457648 /tests
parentb4e1090ab28c0e731157a61a91a6875bad392307 (diff)
Fixed #24184 -- Prevented automatic soft-apply of migrations
Previously Django only checked for the table name in CreateModel operations in initial migrations and faked the migration automatically. This led to various errors and unexpected behavior. The newly introduced --fake-initial flag to the migrate command must be passed to get the same behavior again. With this change Django will bail out in with a "duplicate relation / table" error instead. Thanks Carl Meyer and Tim Graham for the documentation update, report and review.
Diffstat (limited to 'tests')
-rw-r--r--tests/migrations/test_commands.py61
-rw-r--r--tests/migrations/test_executor.py10
2 files changed, 69 insertions, 2 deletions
diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py
index 95defa61f8..f20a083fb6 100644
--- a/tests/migrations/test_commands.py
+++ b/tests/migrations/test_commands.py
@@ -8,7 +8,7 @@ import shutil
from django.apps import apps
from django.core.management import CommandError, call_command
-from django.db import connection, models
+from django.db import DatabaseError, connection, models
from django.db.migrations import questioner
from django.test import ignore_warnings, mock, override_settings
from django.utils import six
@@ -52,6 +52,65 @@ class MigrateTests(MigrationTestBase):
self.assertTableNotExists("migrations_tribble")
self.assertTableNotExists("migrations_book")
+ @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"})
+ def test_migrate_fake_initial(self):
+ """
+ #24184 - Tests that --fake-initial only works if all tables created in
+ the initial migration of an app exists
+ """
+ # Make sure no tables are created
+ self.assertTableNotExists("migrations_author")
+ self.assertTableNotExists("migrations_tribble")
+ # Run the migrations to 0001 only
+ call_command("migrate", "migrations", "0001", verbosity=0)
+ # Make sure the right tables exist
+ self.assertTableExists("migrations_author")
+ self.assertTableExists("migrations_tribble")
+ # Fake a roll-back
+ call_command("migrate", "migrations", "zero", fake=True, verbosity=0)
+ # Make sure the tables still exist
+ self.assertTableExists("migrations_author")
+ self.assertTableExists("migrations_tribble")
+ # Try to run initial migration
+ with self.assertRaises(DatabaseError):
+ call_command("migrate", "migrations", "0001", verbosity=0)
+ # Run initial migration with an explicit --fake-initial
+ out = six.StringIO()
+ with mock.patch('django.core.management.color.supports_color', lambda *args: False):
+ call_command("migrate", "migrations", "0001", fake_initial=True, stdout=out, verbosity=1)
+ self.assertIn(
+ "migrations.0001_initial... faked",
+ out.getvalue().lower()
+ )
+ # Run migrations all the way
+ call_command("migrate", verbosity=0)
+ # Make sure the right tables exist
+ self.assertTableExists("migrations_author")
+ self.assertTableNotExists("migrations_tribble")
+ self.assertTableExists("migrations_book")
+ # Fake a roll-back
+ call_command("migrate", "migrations", "zero", fake=True, verbosity=0)
+ # Make sure the tables still exist
+ self.assertTableExists("migrations_author")
+ self.assertTableNotExists("migrations_tribble")
+ self.assertTableExists("migrations_book")
+ # Try to run initial migration
+ with self.assertRaises(DatabaseError):
+ call_command("migrate", "migrations", verbosity=0)
+ # Run initial migration with an explicit --fake-initial
+ with self.assertRaises(DatabaseError):
+ # Fails because "migrations_tribble" does not exist but needs to in
+ # order to make --fake-initial work.
+ call_command("migrate", "migrations", fake_initial=True, verbosity=0)
+ # Fake a apply
+ call_command("migrate", "migrations", fake=True, verbosity=0)
+ # Unmigrate everything
+ call_command("migrate", "migrations", "zero", verbosity=0)
+ # Make sure it's all gone
+ self.assertTableNotExists("migrations_author")
+ self.assertTableNotExists("migrations_tribble")
+ self.assertTableNotExists("migrations_book")
+
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_conflict"})
def test_migrate_conflict_exit(self):
"""
diff --git a/tests/migrations/test_executor.py b/tests/migrations/test_executor.py
index e6482fb830..b88307ceea 100644
--- a/tests/migrations/test_executor.py
+++ b/tests/migrations/test_executor.py
@@ -2,6 +2,7 @@ from django.apps.registry import apps as global_apps
from django.db import connection
from django.db.migrations.executor import MigrationExecutor
from django.db.migrations.graph import MigrationGraph
+from django.db.utils import DatabaseError
from django.test import TestCase, modify_settings, override_settings
from .test_base import MigrationTestBase
@@ -186,7 +187,14 @@ class ExecutorTests(MigrationTestBase):
(executor.loader.graph.nodes["migrations", "0001_initial"], False),
],
)
- executor.migrate([("migrations", "0001_initial")])
+ # Applying the migration should raise a database level error
+ # because we haven't given the --fake-initial option
+ with self.assertRaises(DatabaseError):
+ executor.migrate([("migrations", "0001_initial")])
+ # Reset the faked state
+ state = {"faked": None}
+ # Allow faking of initial CreateModel operations
+ executor.migrate([("migrations", "0001_initial")], fake_initial=True)
self.assertEqual(state["faked"], True)
# And migrate back to clean up the database
executor.loader.build_graph()