Python django.db.migrations.executor.MigrationExecutor() Examples
The following are 30
code examples of django.db.migrations.executor.MigrationExecutor().
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example.
You may also want to check out all available functions/classes of the module
django.db.migrations.executor
, or try the search function
.
Example #1
Source File: utils.py From karrot-backend with GNU Affero General Public License v3.0 | 7 votes |
def setUp(self): assert self.migrate_from and self.migrate_to, \ "TestCase '{}' must define migrate_from and migrate_to properties".format(type(self).__name__) executor = MigrationExecutor(connection) old_apps = executor.loader.project_state(self.migrate_from).apps # Reverse to the original migration executor.migrate(self.migrate_from) self.setUpBeforeMigration(old_apps) # Run the migration to test executor = MigrationExecutor(connection) executor.loader.build_graph() # reload. executor.migrate(self.migrate_to) self.apps = executor.loader.project_state(self.migrate_to).apps
Example #2
Source File: test_migrations.py From chain-api with MIT License | 6 votes |
def setUp(self): assert self.migrate_from and self.migrate_to, \ "TestCase '{}' must define migrate_from and migrate_to properties".format(type(self).__name__) self.migrate_from = [(self.app, self.migrate_from)] self.migrate_to = [(self.app, self.migrate_to)] executor = MigrationExecutor(connection) old_apps = executor.loader.project_state(self.migrate_from).apps # Reverse to the original migration executor.migrate(self.migrate_from) super(TestMigrations, self).setUp() self.setUpBeforeMigration(old_apps) # Run the migration to test executor = MigrationExecutor(connection) executor.loader.build_graph() # reload. executor.migrate(self.migrate_to) self.apps = executor.loader.project_state(self.migrate_to).apps
Example #3
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 6 votes |
def test_migrate_marks_replacement_applied_even_if_it_did_nothing(self): """ A new squash migration will be marked as applied even if all its replaced migrations were previously already applied (#24628). """ recorder = MigrationRecorder(connection) # Record all replaced migrations as applied recorder.record_applied("migrations", "0001_initial") recorder.record_applied("migrations", "0002_second") executor = MigrationExecutor(connection) executor.migrate([("migrations", "0001_squashed_0002")]) # Because 0001 and 0002 are both applied, even though this migrate run # didn't apply anything new, their squashed replacement should be # marked as applied. self.assertIn( ("migrations", "0001_squashed_0002"), recorder.applied_migrations(), ) # When the feature is False, the operation and the record won't be # performed in a transaction and the test will systematically pass.
Example #4
Source File: test_classes.py From zulip with Apache License 2.0 | 6 votes |
def setUp(self) -> None: assert self.migrate_from and self.migrate_to, \ f"TestCase '{type(self).__name__}' must define migrate_from and migrate_to properties" migrate_from: List[Tuple[str, str]] = [(self.app, self.migrate_from)] migrate_to: List[Tuple[str, str]] = [(self.app, self.migrate_to)] executor = MigrationExecutor(connection) old_apps = executor.loader.project_state(migrate_from).apps # Reverse to the original migration executor.migrate(migrate_from) self.setUpBeforeMigration(old_apps) # Run the migration to test executor = MigrationExecutor(connection) executor.loader.build_graph() # reload. executor.migrate(migrate_to) self.apps = executor.loader.project_state(migrate_to).apps
Example #5
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 6 votes |
def test_apply_all_replaced_marks_replacement_as_applied(self): """ Applying all replaced migrations marks replacement as applied (#24628). """ recorder = MigrationRecorder(connection) # Place the database in a state where the replaced migrations are # partially applied: 0001 is applied, 0002 is not. recorder.record_applied("migrations", "0001_initial") executor = MigrationExecutor(connection) # Use fake because we don't actually have the first migration # applied, so the second will fail. And there's no need to actually # create/modify tables here, we're just testing the # MigrationRecord, which works the same with or without fake. executor.migrate([("migrations", "0002_second")], fake=True) # Because we've now applied 0001 and 0002 both, their squashed # replacement should be marked as applied. self.assertIn( ("migrations", "0001_squashed_0002"), recorder.applied_migrations(), )
Example #6
Source File: conftest.py From normandy with Mozilla Public License 2.0 | 6 votes |
def migrations(transactional_db): """ This fixture returns a helper object to test Django data migrations. Based on: https://gist.github.com/bennylope/82a6088c02fefdd47e18f3c04ec167af """ class Migrator(object): def migrate(self, app, to): migration = [(app, to)] executor = MigrationExecutor(connection) executor.migrate(migration) return executor.loader.project_state(migration).apps def reset(self): call_command("migrate", no_input=True) return Migrator()
Example #7
Source File: django_migrations.py From django-exclusivebooleanfield with MIT License | 6 votes |
def apply_django_migration(migration_cls, migration_name='9999', app_label='testapp', executor=None): if executor is None: executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS]) migration = migration_cls(migration_name, app_label) key = (migration.app_label, migration.name) executor.loader.graph.add_node(key, migration) for parent in migration.dependencies: executor.loader.graph.add_dependency(migration, key, parent) try: executor.apply_migration(migration) except TypeError: state = executor.loader.project_state(parent) executor.apply_migration(state, migration) return executor
Example #8
Source File: sources.py From koku with GNU Affero General Public License v3.0 | 6 votes |
def check_migrations(self): """ Check the status of database migrations. The koku API server is responsible for running all database migrations. This method will return the state of the database and whether or not all migrations have been completed. Hat tip to the Stack Overflow contributor: https://stackoverflow.com/a/31847406 Returns: Boolean - True if database is available and migrations have completed. False otherwise. """ connection = connections[DEFAULT_DB_ALIAS] connection.prepare_database() executor = MigrationExecutor(connection) targets = executor.loader.graph.leaf_nodes() return not executor.migration_plan(targets)
Example #9
Source File: database.py From koku with GNU Affero General Public License v3.0 | 6 votes |
def check_migrations(): """ Check the status of database migrations. The koku API server is responsible for running all database migrations. This method will return the state of the database and whether or not all migrations have been completed. Hat tip to the Stack Overflow contributor: https://stackoverflow.com/a/31847406 Returns: Boolean - True if database is available and migrations have completed. False otherwise. """ try: connection = connections[DEFAULT_DB_ALIAS] connection.prepare_database() executor = MigrationExecutor(connection) targets = executor.loader.graph.leaf_nodes() return not executor.migration_plan(targets) except OperationalError: return False
Example #10
Source File: has_missing_migrations.py From django-react-boilerplate with MIT License | 6 votes |
def handle(self, *args, **options): changed = set() self.stdout.write("Checking...") for db in settings.DATABASES.keys(): try: executor = MigrationExecutor(connections[db]) except OperationalError: sys.exit("Unable to check migrations: cannot connect to database\n") autodetector = MigrationAutodetector( executor.loader.project_state(), ProjectState.from_apps(apps), ) changed.update(autodetector.changes(graph=executor.loader.graph).keys()) changed -= set(options["ignore"]) if changed: sys.exit( "Apps with model changes but no corresponding migration file: %(changed)s\n" % {"changed": list(changed)} ) else: sys.stdout.write("All migration files present\n")
Example #11
Source File: helper.py From byro with Apache License 2.0 | 6 votes |
def setUp(self): assert ( self.migrate_from and self.migrate_to ), "TestCase '{}' must define migrate_from and migrate_to properties".format( type(self).__name__ ) self.migrate_from = [(self.app, self.migrate_from)] self.migrate_to = [(self.app, self.migrate_to)] executor = MigrationExecutor(connection) old_apps = executor.loader.project_state(self.migrate_from).apps # Reverse to the original migration executor.migrate(self.migrate_from) if self.migrate_fixtures: self.load_fixtures(self.migrate_fixtures, apps=old_apps) self.setUpBeforeMigration(old_apps) # Run the migration to test executor = MigrationExecutor(connection) executor.loader.build_graph() # reload. executor.migrate(self.migrate_to) self.apps = executor.loader.project_state(self.migrate_to).apps
Example #12
Source File: fake_model.py From django-localized-fields with MIT License | 6 votes |
def get_fake_model(fields=None, model_base=LocalizedModel, meta_options={}): """Creates a fake model to use during unit tests.""" model = define_fake_model(fields, model_base, meta_options) class TestProject: def clone(self, *_args, **_kwargs): return self @property def apps(self): return self class TestMigration(migrations.Migration): operations = [HStoreExtension()] with connection.schema_editor() as schema_editor: migration_executor = MigrationExecutor(schema_editor.connection) migration_executor.apply_migration( TestProject(), TestMigration("eh", "postgres_extra") ) schema_editor.create_model(model) return model
Example #13
Source File: utils.py From django-rest-framework-simplejwt with MIT License | 6 votes |
def setUp(self): self.migrate_from = [self.migrate_from] self.migrate_to = [self.migrate_to] # Reverse to the original migration executor = MigrationExecutor(connection) executor.migrate(self.migrate_from) old_apps = executor.loader.project_state(self.migrate_from).apps self.setUpBeforeMigration(old_apps) # Run the migration to test executor.loader.build_graph() executor.migrate(self.migrate_to) self.apps = executor.loader.project_state(self.migrate_to).apps
Example #14
Source File: util.py From donation-tracker with Apache License 2.0 | 6 votes |
def setUp(self): assert ( self.migrate_from and self.migrate_to ), "TestCase '{}' must define migrate_from and migrate_to properties".format( type(self).__name__ ) self.migrate_from = [(self.app, self.migrate_from)] self.migrate_to = [(self.app, self.migrate_to)] executor = MigrationExecutor(connection) old_apps = executor.loader.project_state(self.migrate_from).apps # Reverse to the original migration executor.migrate(self.migrate_from) self.setUpBeforeMigration(old_apps) # Run the migration to test executor = MigrationExecutor(connection) executor.loader.build_graph() # reload. executor.migrate(self.migrate_to) self.apps = executor.loader.project_state(self.migrate_to).apps
Example #15
Source File: test_executor.py From django-sqlserver with MIT License | 6 votes |
def test_migrate_marks_replacement_applied_even_if_it_did_nothing(self): """ A new squash migration will be marked as applied even if all its replaced migrations were previously already applied (#24628). """ recorder = MigrationRecorder(connection) # Record all replaced migrations as applied recorder.record_applied("migrations", "0001_initial") recorder.record_applied("migrations", "0002_second") executor = MigrationExecutor(connection) executor.migrate([("migrations", "0001_squashed_0002")]) # Because 0001 and 0002 are both applied, even though this migrate run # didn't apply anything new, their squashed replacement should be # marked as applied. self.assertIn( ("migrations", "0001_squashed_0002"), recorder.applied_migrations(), )
Example #16
Source File: test_executor.py From django-sqlserver with MIT License | 6 votes |
def test_apply_all_replaced_marks_replacement_as_applied(self): """ Applying all replaced migrations marks replacement as applied (#24628). """ recorder = MigrationRecorder(connection) # Place the database in a state where the replaced migrations are # partially applied: 0001 is applied, 0002 is not. recorder.record_applied("migrations", "0001_initial") executor = MigrationExecutor(connection) # Use fake because we don't actually have the first migration # applied, so the second will fail. And there's no need to actually # create/modify tables here, we're just testing the # MigrationRecord, which works the same with or without fake. executor.migrate([("migrations", "0002_second")], fake=True) # Because we've now applied 0001 and 0002 both, their squashed # replacement should be marked as applied. self.assertIn( ("migrations", "0001_squashed_0002"), recorder.applied_migrations(), )
Example #17
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 6 votes |
def test_atomic_operation_in_non_atomic_migration(self): """ An atomic operation is properly rolled back inside a non-atomic migration. """ executor = MigrationExecutor(connection) with self.assertRaisesMessage(RuntimeError, "Abort migration"): executor.migrate([("migrations", "0001_initial")]) migrations_apps = executor.loader.project_state(("migrations", "0001_initial")).apps Editor = migrations_apps.get_model("migrations", "Editor") self.assertFalse(Editor.objects.exists()) # Record previous migration as successful. executor.migrate([("migrations", "0001_initial")], fake=True) # Rebuild the graph to reflect the new DB state. executor.loader.build_graph() # Migrating backwards is also atomic. with self.assertRaisesMessage(RuntimeError, "Abort migration"): executor.migrate([("migrations", None)]) self.assertFalse(Editor.objects.exists())
Example #18
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 6 votes |
def test_apply_all_replaced_marks_replacement_as_applied(self): """ Applying all replaced migrations marks replacement as applied (#24628). """ recorder = MigrationRecorder(connection) # Place the database in a state where the replaced migrations are # partially applied: 0001 is applied, 0002 is not. recorder.record_applied("migrations", "0001_initial") executor = MigrationExecutor(connection) # Use fake because we don't actually have the first migration # applied, so the second will fail. And there's no need to actually # create/modify tables here, we're just testing the # MigrationRecord, which works the same with or without fake. executor.migrate([("migrations", "0002_second")], fake=True) # Because we've now applied 0001 and 0002 both, their squashed # replacement should be marked as applied. self.assertIn( ("migrations", "0001_squashed_0002"), recorder.applied_migrations(), )
Example #19
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 6 votes |
def test_migrate_marks_replacement_applied_even_if_it_did_nothing(self): """ A new squash migration will be marked as applied even if all its replaced migrations were previously already applied (#24628). """ recorder = MigrationRecorder(connection) # Record all replaced migrations as applied recorder.record_applied("migrations", "0001_initial") recorder.record_applied("migrations", "0002_second") executor = MigrationExecutor(connection) executor.migrate([("migrations", "0001_squashed_0002")]) # Because 0001 and 0002 are both applied, even though this migrate run # didn't apply anything new, their squashed replacement should be # marked as applied. self.assertIn( ("migrations", "0001_squashed_0002"), recorder.applied_migrations(), )
Example #20
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 6 votes |
def test_atomic_operation_in_non_atomic_migration(self): """ An atomic operation is properly rolled back inside a non-atomic migration. """ executor = MigrationExecutor(connection) with self.assertRaisesMessage(RuntimeError, "Abort migration"): executor.migrate([("migrations", "0001_initial")]) migrations_apps = executor.loader.project_state(("migrations", "0001_initial")).apps Editor = migrations_apps.get_model("migrations", "Editor") self.assertFalse(Editor.objects.exists()) # Record previous migration as successful. executor.migrate([("migrations", "0001_initial")], fake=True) # Rebuild the graph to reflect the new DB state. executor.loader.build_graph() # Migrating backwards is also atomic. with self.assertRaisesMessage(RuntimeError, "Abort migration"): executor.migrate([("migrations", None)]) self.assertFalse(Editor.objects.exists())
Example #21
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 6 votes |
def test_unrelated_applied_migrations_mutate_state(self): """ #26647 - Unrelated applied migrations should be part of the final state in both directions. """ executor = MigrationExecutor(connection) executor.migrate([ ('mutate_state_b', '0002_add_field'), ]) # Migrate forward. executor.loader.build_graph() state = executor.migrate([ ('mutate_state_a', '0001_initial'), ]) self.assertIn('added', dict(state.models['mutate_state_b', 'b'].fields)) executor.loader.build_graph() # Migrate backward. state = executor.migrate([ ('mutate_state_a', None), ]) self.assertIn('added', dict(state.models['mutate_state_b', 'b'].fields)) executor.migrate([ ('mutate_state_b', None), ])
Example #22
Source File: test_middleware.py From django-request-profiler with MIT License | 6 votes |
def test_for_missing_migrations(self): """Checks if there're models changes which aren't reflected in migrations.""" migrations_loader = MigrationExecutor(connection).loader migrations_detector = MigrationAutodetector( from_state=migrations_loader.project_state(), to_state=ProjectState.from_apps(apps), ) if migrations_detector.changes(graph=migrations_loader.graph): self.fail( "Your models have changes that are not yet reflected " "in a migration. You should add them now." )
Example #23
Source File: migrations.py From django-postgres-extra with MIT License | 5 votes |
def apply_migration(operations, state=None, backwards: bool = False): """Executes the specified migration operations using the specified schema editor. Arguments: operations: The migration operations to execute. state: The state state to use during the migrations. backwards: Whether to apply the operations in reverse (backwards). """ state = state or migrations.state.ProjectState.from_apps(apps) class Migration(migrations.Migration): pass Migration.operations = operations migration = Migration("migration", "tests") executor = MigrationExecutor(connection) if not backwards: executor.apply_migration(state, migration) else: executor.unapply_migration(state, migration) return migration
Example #24
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 5 votes |
def test_alter_id_type_with_fk(self): try: executor = MigrationExecutor(connection) self.assertTableNotExists("author_app_author") self.assertTableNotExists("book_app_book") # Apply initial migrations executor.migrate([ ("author_app", "0001_initial"), ("book_app", "0001_initial"), ]) self.assertTableExists("author_app_author") self.assertTableExists("book_app_book") # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Apply PK type alteration executor.migrate([("author_app", "0002_alter_id")]) # Rebuild the graph to reflect the new DB state executor.loader.build_graph() finally: # We can't simply unapply the migrations here because there is no # implicit cast from VARCHAR to INT on the database level. with connection.schema_editor() as editor: editor.execute(editor.sql_delete_table % {"table": "book_app_book"}) editor.execute(editor.sql_delete_table % {"table": "author_app_author"}) self.assertTableNotExists("author_app_author") self.assertTableNotExists("book_app_book") executor.migrate([("author_app", None)], fake=True)
Example #25
Source File: test_migration.py From caluma with GNU General Public License v3.0 | 5 votes |
def test_migrate_to_form_question_natural_key_forward(transactional_db): executor = MigrationExecutor(connection) app = "caluma_form" migrate_from = [(app, "0023_auto_20190729_1448")] migrate_to = [(app, "0024_auto_20190919_1244")] executor.migrate(migrate_from) old_apps = executor.loader.project_state(migrate_from).apps # Create some old data. Can't use factories here Form = old_apps.get_model(app, "Form") Question = old_apps.get_model(app, "Question") FormQuestion = old_apps.get_model(app, "FormQuestion") form_1 = Form.objects.create(slug="form-1") question_1 = Question.objects.create(type="text", slug="question-1") form_question = FormQuestion.objects.create(form=form_1, question=question_1) assert isinstance(form_question.pk, UUID) # Migrate forwards. executor.loader.build_graph() # reload. executor.migrate(migrate_to) new_apps = executor.loader.project_state(migrate_to).apps # Test the new data. FormQuestion = new_apps.get_model(app, "FormQuestion") form_question = FormQuestion.objects.first() assert form_question.pk == "form-1.question-1"
Example #26
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 5 votes |
def test_unrelated_model_lookups_backwards(self): """ #24123 - All models of apps being unapplied which are unrelated to the first app being unapplied are part of the initial model state. """ try: executor = MigrationExecutor(connection) self.assertTableNotExists("lookuperror_a_a1") self.assertTableNotExists("lookuperror_b_b1") self.assertTableNotExists("lookuperror_c_c1") executor.migrate([ ("lookuperror_a", "0004_a4"), ("lookuperror_b", "0003_b3"), ("lookuperror_c", "0003_c3"), ]) self.assertTableExists("lookuperror_b_b3") self.assertTableExists("lookuperror_a_a4") self.assertTableExists("lookuperror_c_c3") # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Migrate backwards -- This led to a lookup LookupErrors because # lookuperror_b.B2 is not in the initial state (unrelated to app c) executor.migrate([("lookuperror_a", None)]) # Rebuild the graph to reflect the new DB state executor.loader.build_graph() finally: # Cleanup executor.migrate([ ("lookuperror_b", None), ("lookuperror_c", None) ]) self.assertTableNotExists("lookuperror_a_a1") self.assertTableNotExists("lookuperror_b_b1") self.assertTableNotExists("lookuperror_c_c1")
Example #27
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 5 votes |
def test_unrelated_model_lookups_forwards(self): """ #24123 - All models of apps already applied which are unrelated to the first app being applied are part of the initial model state. """ try: executor = MigrationExecutor(connection) self.assertTableNotExists("lookuperror_a_a1") self.assertTableNotExists("lookuperror_b_b1") self.assertTableNotExists("lookuperror_c_c1") executor.migrate([("lookuperror_b", "0003_b3")]) self.assertTableExists("lookuperror_b_b3") # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Migrate forwards -- This led to a lookup LookupErrors because # lookuperror_b.B2 is already applied executor.migrate([ ("lookuperror_a", "0004_a4"), ("lookuperror_c", "0003_c3"), ]) self.assertTableExists("lookuperror_a_a4") self.assertTableExists("lookuperror_c_c3") # Rebuild the graph to reflect the new DB state executor.loader.build_graph() finally: # Cleanup executor.migrate([ ("lookuperror_a", None), ("lookuperror_b", None), ("lookuperror_c", None), ]) self.assertTableNotExists("lookuperror_a_a1") self.assertTableNotExists("lookuperror_b_b1") self.assertTableNotExists("lookuperror_c_c1")
Example #28
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 5 votes |
def test_non_atomic_migration(self): """ Applying a non-atomic migration works as expected. """ executor = MigrationExecutor(connection) with self.assertRaisesMessage(RuntimeError, "Abort migration"): executor.migrate([("migrations", "0001_initial")]) self.assertTableExists("migrations_publisher") migrations_apps = executor.loader.project_state(("migrations", "0001_initial")).apps Publisher = migrations_apps.get_model("migrations", "Publisher") self.assertTrue(Publisher.objects.exists()) self.assertTableNotExists("migrations_book")
Example #29
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 5 votes |
def test_run_with_squashed(self): """ Tests running a squashed migration from zero (should ignore what it replaces) """ executor = MigrationExecutor(connection) # Check our leaf node is the squashed one leaves = [key for key in executor.loader.graph.leaf_nodes() if key[0] == "migrations"] self.assertEqual(leaves, [("migrations", "0001_squashed_0002")]) # Check the plan plan = executor.migration_plan([("migrations", "0001_squashed_0002")]) self.assertEqual( plan, [ (executor.loader.graph.nodes["migrations", "0001_squashed_0002"], False), ], ) # Were the tables there before? self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_book") # Alright, let's try running it executor.migrate([("migrations", "0001_squashed_0002")]) # Are the tables there now? self.assertTableExists("migrations_author") self.assertTableExists("migrations_book") # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Alright, let's undo what we did. Should also just use squashed. plan = executor.migration_plan([("migrations", None)]) self.assertEqual( plan, [ (executor.loader.graph.nodes["migrations", "0001_squashed_0002"], True), ], ) executor.migrate([("migrations", None)]) # Are the tables gone? self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_book")
Example #30
Source File: test_executor.py From djongo with GNU Affero General Public License v3.0 | 5 votes |
def test_custom_user(self): """ Regression test for #22325 - references to a custom user model defined in the same app are not resolved correctly. """ executor = MigrationExecutor(connection) self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") # Migrate forwards executor.migrate([("migrations", "0001_initial")]) self.assertTableExists("migrations_author") self.assertTableExists("migrations_tribble") # Make sure the soft-application detection works (#23093) # Change table_names to not return auth_user during this as # it wouldn't be there in a normal run, and ensure migrations.Author # exists in the global app registry temporarily. old_table_names = connection.introspection.table_names connection.introspection.table_names = lambda c: [x for x in old_table_names(c) if x != "auth_user"] migrations_apps = executor.loader.project_state(("migrations", "0001_initial")).apps global_apps.get_app_config("migrations").models["author"] = migrations_apps.get_model("migrations", "author") try: migration = executor.loader.get_migration("auth", "0001_initial") self.assertIs(executor.detect_soft_applied(None, migration)[0], True) finally: connection.introspection.table_names = old_table_names del global_apps.get_app_config("migrations").models["author"] # And migrate back to clean up the database executor.loader.build_graph() executor.migrate([("migrations", None)]) self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble")