def check_migrations(self): """ Checks to see if the set of migrations on disk matches the migrations in the database. Prints a warning if they don't match. """ try: executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS]) except ImproperlyConfigured: # No databases are configured (or the dummy one) return except MigrationSchemaMissing: self.stdout.write( self.style.NOTICE( "\nNot checking migrations as it is not possible to access/create the django_migrations table." ) ) return plan = executor.migration_plan(executor.loader.graph.leaf_nodes()) if plan: apps_waiting_migration = sorted(set(migration.app_label for migration, backwards in plan)) self.stdout.write( self.style.NOTICE( "\nYou have %(unpplied_migration_count)s unapplied migration(s). " "Your project may not work properly until you apply the " "migrations for app(s): %(apps_waiting_migration)s." % { "unpplied_migration_count": len(plan), "apps_waiting_migration": ", ".join(apps_waiting_migration), } ) ) self.stdout.write(self.style.NOTICE("Run 'python manage.py migrate' to apply them.\n"))
def check_migrations(self): """ Print a warning if the set of migrations on disk don't match the migrations in the database. """ from django.db.migrations.executor import MigrationExecutor try: executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS]) except ImproperlyConfigured: # No databases are configured (or the dummy one) return plan = executor.migration_plan(executor.loader.graph.leaf_nodes()) if plan: apps_waiting_migration = sorted({migration.app_label for migration, backwards in plan}) self.stdout.write( self.style.NOTICE( "\nYou have %(unpplied_migration_count)s unapplied migration(s). " "Your project may not work properly until you apply the " "migrations for app(s): %(apps_waiting_migration)s." % { "unpplied_migration_count": len(plan), "apps_waiting_migration": ", ".join(apps_waiting_migration), } ) ) self.stdout.write(self.style.NOTICE("Run 'python manage.py migrate' to apply them.\n"))
def _migrate_from_old_targets(self, tmp_path): if "sqlite3" in self.databases['default']['ENGINE']: try: delete_path(self.databases['default']['NAME']) except Exception: self.clear(False) setup_from_none() self.read() else: self.clear(False) from django.db import DEFAULT_DB_ALIAS, connections from django.apps import apps from django.utils.module_loading import module_has_submodule from django.db.migrations.executor import MigrationExecutor from django.core.management.sql import emit_pre_migrate_signal, emit_post_migrate_signal for app_config in apps.get_app_configs(): if module_has_submodule(app_config.module, "management"): import_module('.management', app_config.name) connection = connections[DEFAULT_DB_ALIAS] connection.prepare_database() executor = MigrationExecutor(connection) emit_pre_migrate_signal(0, None, connection.alias) executor.migrate(executor.loader.graph.leaf_nodes()) emit_post_migrate_signal(0, None, connection.alias) self.clear(True, ['django_migrations'])
def handle(self, *args, **options): # Get the database we're operating from db = options.get('database') connection = connections[db] # Load up an executor to get all the migration data executor = MigrationExecutor(connection) # Resolve command-line arguments into a migration if len(args) != 2: raise CommandError("Wrong number of arguments (expecting 'sqlmigrate app_label migrationname')") else: app_label, migration_name = args if app_label not in executor.loader.migrated_apps: raise CommandError("App '%s' does not have migrations" % app_label) try: migration = executor.loader.get_migration_by_prefix(app_label, migration_name) except AmbiguityError: raise CommandError("More than one migration matches '%s' in app '%s'. Please be more specific." % ( migration_name, app_label)) except KeyError: raise CommandError("Cannot find a migration matching '%s' from app '%s'. Is it in INSTALLED_APPS?" % ( migration_name, app_label)) targets = [(app_label, migration.name)] # Make a plan that represents just the requested migrations and show SQL # for it plan = [(executor.loader.graph.nodes[targets[0]], options.get("backwards", False))] sql_statements = executor.collect_sql(plan) for statement in sql_statements: self.stdout.write(statement)
def handle(self, addrport, **kwargs): # In auto-reload mode, migrations will be re-made and the server will be reloaded (fixtures are not reloaded) for app in apps.get_app_configs(): if app.name in settings.SCAPL_INSTALLED_APPS: call_command('makemigrations', app.label, interactive=False) call_command('migrate') try: executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS]) executor.migration_plan(executor.loader.graph.leaf_nodes()) except Exception as e: logger.error("Something failed with migrations execution") for l in str(e).split(): logger.error(l) return if os.environ.get('RUN_MAIN') != 'true' and kwargs.get('fixtures'): dir = settings.SMUGGLER_FIXTURE_DIR for fn in sorted([f for f in os.listdir(dir) if f.endswith('.json')]): fixture = os.path.join(dir, fn) print("{}, ".format(fixture)), try: call_command('loaddata', fixture, ignorenonexistent=True) except Exception as e: print("No fixture loaded") if isinstance(e, TypeError): logger.warning(" +---> Please refer to 'BUGFIX' in the 'data' folder for this kind of error") else: with open(fixture) as f: if f.read().strip(" \n[]") == "": logger.warning(" +---> This fixture is emtpy") call_command('runserver', addrport)
def check_migrations(self): """ Checks to see if the set of migrations on disk matches the migrations in the database. Prints a warning if they don't match. """ try: executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS]) except ImproperlyConfigured: # No databases are configured (or the dummy one) return except MigrationSchemaMissing: self.stdout.write( self.style.NOTICE( "\nNot checking migrations as it is not possible to access/create the django_migrations table." ) ) return plan = executor.migration_plan(executor.loader.graph.leaf_nodes()) if plan: self.stdout.write( self.style.NOTICE( "\nYou have unapplied migrations; your app may not work properly until they are applied." ) ) self.stdout.write(self.style.NOTICE("Run 'python manage.py migrate' to apply them.\n"))
def __call__(self): # Run the migration to test executor = MigrationExecutor(connection) executor.loader.build_graph() # reload. executor.migrate(self._to) self.apps = executor.loader.project_state(self._to).apps
def test_backwards_nothing_to_do(self): """ If the current state satisfies the given target, do nothing. a: 1 <--- 2 b: \- 1 c: \- 1 If a1 is applied already and a2 is not, and we're asked to migrate to a1, don't apply or unapply b1 or c1, regardless of their current state. """ a1_impl = FakeMigration('a1') a1 = ('a', '1') a2_impl = FakeMigration('a2') a2 = ('a', '2') b1_impl = FakeMigration('b1') b1 = ('b', '1') c1_impl = FakeMigration('c1') c1 = ('c', '1') graph = MigrationGraph() graph.add_node(a1, a1_impl) graph.add_node(a2, a2_impl) graph.add_node(b1, b1_impl) graph.add_node(c1, c1_impl) graph.add_dependency(None, a2, a1) graph.add_dependency(None, b1, a1) graph.add_dependency(None, c1, a1) executor = MigrationExecutor(None) executor.loader = FakeLoader(graph, {a1, b1}) plan = executor.migration_plan({a1}) self.assertEqual(plan, [])
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")
def __init__(self, _from, _to): self._to = _to executor = MigrationExecutor(connection) self.apps = executor.loader.project_state(_from).apps # Reverse to the original migration executor.migrate(_from)
def main(): if len(sys.argv) == 1: print("Usage: ./run.py run") return if sys.argv[1] in ('makemigrations', 'runserver',): execute_from_command_line(sys.argv) elif sys.argv[1] == 'run': from django.db.migrations.executor import MigrationExecutor connection = connections[DEFAULT_DB_ALIAS] connection.prepare_database() executor = MigrationExecutor(connection) plan = executor.migration_plan(executor.loader.graph.leaf_nodes()) if len(plan) > 0: print('Synchronizing database schemas...') call_command('migrate') print() print('Starting application...') loop = asyncio.get_event_loop() ika = Server() ika.register_services() try: loop.run_until_complete(ika.connect()) except KeyboardInterrupt: ika.disconnect('Manually interrupted by console access') except: ika.disconnect('Exception has occured in the main loop') logger.exception('Exception has occured in the main loop') finally: loop.close()
def handle(self, *args, **options): # Get the database we're operating from connection = connections[options['database']] # Load up an executor to get all the migration data executor = MigrationExecutor(connection) # Resolve command-line arguments into a migration app_label, migration_name = options['app_label'], options['migration_name'] if app_label not in executor.loader.migrated_apps: raise CommandError("App '%s' does not have migrations" % app_label) try: migration = executor.loader.get_migration_by_prefix(app_label, migration_name) except AmbiguityError: raise CommandError("More than one migration matches '%s' in app '%s'. Please be more specific." % ( migration_name, app_label)) except KeyError: raise CommandError("Cannot find a migration matching '%s' from app '%s'. Is it in INSTALLED_APPS?" % ( migration_name, app_label)) targets = [(app_label, migration.name)] # Show begin/end around output only for atomic migrations self.output_transaction = migration.atomic # Make a plan that represents just the requested migrations and show SQL # for it plan = [(executor.loader.graph.nodes[targets[0]], options['backwards'])] sql_statements = executor.collect_sql(plan) return '\n'.join(sql_statements)
def test_empty_plan(self): """ Tests that re-planning a full migration of a fully-migrated set doesn't perform spurious unmigrations and remigrations. There was previously a bug where the executor just always performed the backwards plan for applied migrations - which even for the most recent migration in an app, might include other, dependent apps, and these were being unmigrated. """ # Make the initial plan, check it # We use 'sessions' here as the second app as it's always present # in INSTALLED_APPS, so we can happily assign it test migrations. executor = MigrationExecutor(connection) plan = executor.migration_plan([("migrations", "0002_second"), ("sessions", "0001_initial")]) self.assertEqual( plan, [ (executor.loader.graph.nodes["migrations", "0001_initial"], False), (executor.loader.graph.nodes["migrations", "0002_second"], False), (executor.loader.graph.nodes["sessions", "0001_initial"], False), ], ) # Fake-apply all migrations executor.migrate([("migrations", "0002_second"), ("sessions", "0001_initial")], fake=True) # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Now plan a second time and make sure it's empty plan = executor.migration_plan([("migrations", "0002_second"), ("sessions", "0001_initial")]) self.assertEqual(plan, []) # Erase all the fake records executor.recorder.flush()
def test_minimize_rollbacks(self): """ Minimize unnecessary rollbacks in connected apps. When you say "./manage.py migrate appA 0001", rather than migrating to just after appA-0001 in the linearized migration plan (which could roll back migrations in other apps that depend on appA 0001, but don't need to be rolled back since we're not rolling back appA 0001), we migrate to just before appA-0002. """ a1_impl = FakeMigration('a1') a1 = ('a', '1') a2_impl = FakeMigration('a2') a2 = ('a', '2') b1_impl = FakeMigration('b1') b1 = ('b', '1') graph = MigrationGraph() graph.add_node(a1, a1_impl) graph.add_node(a2, a2_impl) graph.add_node(b1, b1_impl) graph.add_dependency(None, b1, a1) graph.add_dependency(None, a2, a1) executor = MigrationExecutor(None) executor.loader = FakeLoader(graph, {a1, b1, a2}) plan = executor.migration_plan({a1}) self.assertEqual(plan, [(a2_impl, True)])
def check_migrations(self): """ Checks to see if the set of migrations on disk matches the migrations in the database. Prints a warning if they don't match. """ executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS]) plan = executor.migration_plan(executor.loader.graph.leaf_nodes()) if plan and self.show_startup_messages: self.stdout.write(self.style.NOTICE("\nYou have unapplied migrations; your app may not work properly until they are applied.")) self.stdout.write(self.style.NOTICE("Run 'python manage.py migrate' to apply them.\n"))
def get_database_info(database=DEFAULT_DB_ALIAS): connection = connections[database] connection.prepare_database() executor = MigrationExecutor(connection) targets = executor.loader.graph.leaf_nodes() is_synchronized = False if executor.migration_plan(targets) else True return { 'is_database_synchronized': is_synchronized, 'postgres_version': str(parse_pg_version(connection.pg_version)), }
def test_soft_apply(self): """ Tests detection of initial migrations already having been applied. """ state = {"faked": None} def fake_storer(phase, migration, fake): state["faked"] = fake executor = MigrationExecutor(connection, progress_callback=fake_storer) executor.recorder.flush() # Were the tables there before? self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") # Run it normally executor.migrate([("migrations", "0001_initial")]) # Are the tables there now? self.assertTableExists("migrations_author") self.assertTableExists("migrations_tribble") # We shouldn't have faked that one self.assertEqual(state["faked"], False) # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Fake-reverse that executor.migrate([("migrations", None)], fake=True) # Are the tables still there? self.assertTableExists("migrations_author") self.assertTableExists("migrations_tribble") # Make sure that was faked self.assertEqual(state["faked"], True) # Finally, migrate forwards; this should fake-apply our initial migration executor.migrate([("migrations", "0001_initial")]) self.assertEqual(state["faked"], True) # And migrate back to clean up the database executor.migrate([("migrations", None)]) self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble")
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())
def test_migrations_applied_and_recorded_atomically(self): """Migrations are applied and recorded atomically.""" executor = MigrationExecutor(connection) with mock.patch('django.db.migrations.executor.MigrationExecutor.record_migration') as record_migration: record_migration.side_effect = RuntimeError('Recording migration failed.') with self.assertRaisesMessage(RuntimeError, 'Recording migration failed.'): executor.migrate([('migrations', '0001_initial')]) # The migration isn't recorded as applied since it failed. migration_recorder = MigrationRecorder(connection) self.assertFalse(migration_recorder.migration_qs.filter(app='migrations', name='0001_initial').exists()) self.assertTableNotExists('migrations_author')
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.' )
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")
def handle(self, *args, **options): connection = connections[DEFAULT_DB_ALIAS] connection.prepare_database() executor = MigrationExecutor(connection) targets = executor.loader.graph.leaf_nodes() unapplied_migrations = executor.migration_plan(targets) if unapplied_migrations: self.stdout.write('The following migrations are unapplied:', self.style.ERROR) for migration in unapplied_migrations: self.stdout.write(' {}.{}'.format(migration[0].app_label, migration[0].name), self.style.MIGRATE_LABEL) sys.exit(1) self.stdout.write('All migrations have been applied. Have a nice day!', self.style.SUCCESS)
def test_minimize_rollbacks_branchy(self): r""" Minimize rollbacks when target has multiple in-app children. a: 1 <---- 3 <--\ \ \- 2 <--- 4 \ \ b: \- 1 <--- 2 """ a1_impl = FakeMigration('a1') a1 = ('a', '1') a2_impl = FakeMigration('a2') a2 = ('a', '2') a3_impl = FakeMigration('a3') a3 = ('a', '3') a4_impl = FakeMigration('a4') a4 = ('a', '4') b1_impl = FakeMigration('b1') b1 = ('b', '1') b2_impl = FakeMigration('b2') b2 = ('b', '2') graph = MigrationGraph() graph.add_node(a1, a1_impl) graph.add_node(a2, a2_impl) graph.add_node(a3, a3_impl) graph.add_node(a4, a4_impl) graph.add_node(b1, b1_impl) graph.add_node(b2, b2_impl) graph.add_dependency(None, a2, a1) graph.add_dependency(None, a3, a1) graph.add_dependency(None, a4, a2) graph.add_dependency(None, a4, a3) graph.add_dependency(None, b2, b1) graph.add_dependency(None, b1, a1) graph.add_dependency(None, b2, a2) executor = MigrationExecutor(None) executor.loader = FakeLoader(graph, { a1: a1_impl, b1: b1_impl, a2: a2_impl, b2: b2_impl, a3: a3_impl, a4: a4_impl, }) plan = executor.migration_plan({a1}) should_be_rolled_back = [b2_impl, a4_impl, a2_impl, a3_impl] exp = [(m, True) for m in should_be_rolled_back] self.assertEqual(plan, exp)
def migrate(self, targets): """ Migrate to a new state. MigrationExecutors can not be reloaded, as they cache the state of the migrations when created. Attempting to reuse one might make some migrations not run, as it thinks they have already been run. """ executor = MigrationExecutor(connection) executor.migrate(targets) # Cant load state for apps in the initial empty state state_nodes = [node for node in targets if node[1] is not None] return executor.loader.project_state(state_nodes).apps
def test_backwards_deps(self): """ #23474 - Migrating backwards shouldn't cause the wrong migrations to be unapplied. Migration dependencies (x -> y === y depends on x): m.0001 -+-> m.0002 +-> m2.0001 1) Migrate m2 to 0001, causing { m.0001, m2.0002 } to be applied. 2) Migrate m to 0001. m.0001 has already been applied, so this should be a noop. """ executor = MigrationExecutor(connection) executor.migrate([("migrations2", "0001_initial")]) try: self.assertTableExists("migrations2_example") # Rebuild the graph to reflect the new DB state executor.loader.build_graph() self.assertEqual( executor.migration_plan([("migrations", "0001_initial")]), [], ) executor.migrate([("migrations", "0001_initial")]) self.assertTableExists("migrations2_example") finally: # And migrate back to clean up the database executor.loader.build_graph() executor.migrate([("migrations", None)]) self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble")
def setUp(self): executor = MigrationExecutor(connection) # Migrate to start_migration (the migration before the one you want to test) executor.migrate([(self.app_name, self.start_migration)]) # Rebuild graph. Done between invocations of migrate() executor.loader.build_graph() # Run the migration you want to test executor.migrate([(self.app_name, self.dest_migration)]) # This application can now be used to get the latest models for testing self.django_application = executor.loader.project_state( [(self.app_name, self.dest_migration)] ).apps
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")
def __init__(self, migrate_from, migrate_to): self.migrate_from = migrate_from self.migrate_to = migrate_to self.executor = MigrationExecutor(connection) self.executor.loader.build_graph() # reload. self.executor.migrate(self.migrate_from)
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())
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") # And migrate back to clean up the database executor.loader.build_graph() executor.migrate([("migrations", None)]) self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble")
def test_migrate_to_form_question_natural_key_reverse(transactional_db): executor = MigrationExecutor(connection) app = "caluma_form" migrate_from = [(app, "0024_auto_20190919_1244")] migrate_to = [(app, "0023_auto_20190729_1448")] 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") FormQuestion.objects.create(form=form_1, question=question_1) # Migrate backwards. executor.loader.build_graph() # reload. with pytest.raises(DataError): executor.migrate(migrate_to)
def setUpClass(cls): # Capture current state before adding any apps, so we know where to restore to executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS]) cls._original_state = executor.loader.graph.leaf_nodes() super().setUpClass()
def get_pending_migrations(): connection = connections[DEFAULT_DB_ALIAS] connection.prepare_database() executor = MigrationExecutor(connection) targets = executor.loader.graph.leaf_nodes() return executor.migration_plan(targets)
def tearDown(self): executor = MigrationExecutor(connection) executor.loader.build_graph() executor.migrate(executor.loader.graph.leaf_nodes())
def setUp(self): super(MigrationTestCase, self).setUp() self.executor = MigrationExecutor(connection) self.executor.migrate(self.migrate_from)
class Migrator(object): """ Class to manage your migrations and app state. It is designed to be used inside the tests to ensure that migrations are working as intended: both data and schema migrations. This class can be but probably should not be used directly. Because we have utility test framework integrations for ``unitest`` and ``pytest``. Use them for better experience. """ def __init__( self, database: Optional[str] = None, ) -> None: """That's where we initialize all required internals.""" if database is None: database = DEFAULT_DB_ALIAS self._database: str = database self._executor = MigrationExecutor(connections[self._database]) def apply_initial_migration(self, targets: MigrationSpec) -> ProjectState: """Reverse back to the original migration.""" targets = normalize(targets) style = no_style() # start from clean database state sql.drop_models_tables(self._database, style) sql.flush_django_migrations_table(self._database, style) # prepare as broad plan as possible based on full plan self._executor.loader.build_graph() # reload full_plan = self._executor.migration_plan( self._executor.loader.graph.leaf_nodes(), clean_start=True, ) plan = truncate_plan(targets, full_plan) # apply all migrations from generated plan on clean database # (only forward, so any unexpected migration won't be applied) # to restore database state before tested migration return self._migrate(targets, plan=plan) def apply_tested_migration(self, targets: MigrationSpec) -> ProjectState: """Apply the next migration.""" self._executor.loader.build_graph() # reload return self._migrate(normalize(targets)) def reset(self) -> None: """Reset the state to the most recent one.""" call_command('migrate', verbosity=0, database=self._database) def _migrate( self, migration_targets: MigrationSpec, plan: Optional[MigrationPlan] = None, ) -> ProjectState: with _mute_migrate_signals(): return self._executor.migrate(migration_targets, plan=plan)
def handle(self, *args, **options): self.verbosity = options['verbosity'] self.interactive = options['interactive'] # Import the 'management' module within each installed app, to register # dispatcher events. for app_config in apps.get_app_configs(): if module_has_submodule(app_config.module, "management"): import_module('.management', app_config.name) db_routers = [ import_string(router)() for router in conf.settings.DATABASE_ROUTERS ] for connection in connections.all(): # Hook for backends needing any database preparation connection.prepare_database() # Work out which apps have migrations and which do not executor = MigrationExecutor(connection, self.migration_progress_callback) # Raise an error if any migrations are applied before their dependencies. executor.loader.check_consistent_history(connection) # Before anything else, see if there's conflicting apps and drop out # hard if there are any conflicts = executor.loader.detect_conflicts() if conflicts: name_str = "; ".join("%s in %s" % (", ".join(names), app) for app, names in conflicts.items()) raise CommandError( "Conflicting migrations detected; multiple leaf nodes in the " "migration graph: (%s).\nTo fix them run " "'python manage.py makemigrations --merge'" % name_str) # If they supplied command line arguments, work out what they mean. targets, target_app_labels_only = self._get_targets( connection, executor, db_routers, options) plan = executor.migration_plan(targets) run_syncdb = options[ 'run_syncdb'] and executor.loader.unmigrated_apps # Print some useful info if self.verbosity >= 1: self.stdout.write( self.style.MIGRATE_HEADING("Operations to perform:")) if run_syncdb: self.stdout.write( self.style.MIGRATE_LABEL( " Synchronize unmigrated apps: ") + (", ".join(sorted(executor.loader.unmigrated_apps)))) if target_app_labels_only: self.stdout.write( self.style.MIGRATE_LABEL(" Apply all migrations: ") + (", ".join(sorted({a for a, n in targets})) or "(none)")) else: if targets[0][1] is None: self.stdout.write( self.style.MIGRATE_LABEL( " Unapply all migrations: ") + "%s" % (targets[0][0], )) else: self.stdout.write( self.style.MIGRATE_LABEL( " Target specific migration: ") + "%s, from %s" % (targets[0][1], targets[0][0])) pre_migrate_state = executor._create_project_state( with_applied_migrations=True) pre_migrate_apps = pre_migrate_state.apps emit_pre_migrate_signal( self.verbosity, self.interactive, connection.alias, apps=pre_migrate_apps, plan=plan, ) # Run the syncdb phase. if run_syncdb: self._syncdb_phase(connection, executor) # Migrate! self._migrate(plan, connection, executor, pre_migrate_state, targets, options)
'level': 'DEBUG', 'class': 'logging.StreamHandler', }, }, 'loggers': { # We override only this one to avoid logspam # while running tests. Django warnings are # stil shown. 'binder': { 'handlers': ['console'], 'level': 'ERROR', }, } } }) setup() # Do the dance to ensure the models are synched to the DB. # This saves us from having to include migrations from django.core.management.commands.migrate import Command as MigrationCommand from django.db import connections from django.db.migrations.executor import MigrationExecutor # This is oh so hacky.... cmd = MigrationCommand() cmd.verbosity = 0 connection = connections['default'] executor = MigrationExecutor(connection) cmd.sync_apps(connection, executor.loader.unmigrated_apps)
def test_soft_apply(self): """ Tests detection of initial migrations already having been applied. """ state = {"faked": None} def fake_storer(phase, migration=None, fake=None): state["faked"] = fake executor = MigrationExecutor(connection, progress_callback=fake_storer) # Were the tables there before? self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble") # Run it normally self.assertEqual( executor.migration_plan([("migrations", "0001_initial")]), [ (executor.loader.graph.nodes["migrations", "0001_initial"], False), ], ) executor.migrate([("migrations", "0001_initial")]) # Are the tables there now? self.assertTableExists("migrations_author") self.assertTableExists("migrations_tribble") # We shouldn't have faked that one self.assertIs(state["faked"], False) # Rebuild the graph to reflect the new DB state executor.loader.build_graph() # Fake-reverse that executor.migrate([("migrations", None)], fake=True) # Are the tables still there? self.assertTableExists("migrations_author") self.assertTableExists("migrations_tribble") # Make sure that was faked self.assertIs(state["faked"], True) # Finally, migrate forwards; this should fake-apply our initial migration executor.loader.build_graph() self.assertEqual( executor.migration_plan([("migrations", "0001_initial")]), [ (executor.loader.graph.nodes["migrations", "0001_initial"], False), ], ) # 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.assertIs(state["faked"], True) # And migrate back to clean up the database executor.loader.build_graph() executor.migrate([("migrations", None)]) self.assertTableNotExists("migrations_author") self.assertTableNotExists("migrations_tribble")
def handle(self, *args, **options): self.verbosity = options['verbosity'] self.interactive = options['interactive'] # Import the 'management' module within each installed app, to register # dispatcher events. for app_config in apps.get_app_configs(): if module_has_submodule(app_config.module, "management"): import_module('.management', app_config.name) # Get the database we're operating from db = options['database'] connection = connections[db] # Hook for backends needing any database preparation connection.prepare_database() # Work out which apps have migrations and which do not executor = MigrationExecutor(connection, self.migration_progress_callback) # Raise an error if any migrations are applied before their dependencies. executor.loader.check_consistent_history(connection) # Before anything else, see if there's conflicting apps and drop out # hard if there are any conflicts = executor.loader.detect_conflicts() if conflicts: name_str = "; ".join("%s in %s" % (", ".join(names), app) for app, names in conflicts.items()) raise CommandError( "Conflicting migrations detected; multiple leaf nodes in the " "migration graph: (%s).\nTo fix them run " "'python manage.py makemigrations --merge'" % name_str) # If they supplied command line arguments, work out what they mean. target_app_labels_only = True if options['app_label'] and options['migration_name']: app_label, migration_name = options['app_label'], options[ 'migration_name'] if app_label not in executor.loader.migrated_apps: raise CommandError("App '%s' does not have migrations." % app_label) if migration_name == "zero": targets = [(app_label, None)] else: try: migration = executor.loader.get_migration_by_prefix( app_label, migration_name) except AmbiguityError: raise CommandError( "More than one migration matches '%s' in app '%s'. " "Please be more specific." % (migration_name, app_label)) except KeyError: raise CommandError( "Cannot find a migration matching '%s' from app '%s'." % (migration_name, app_label)) targets = [(app_label, migration.name)] target_app_labels_only = False elif options['app_label']: app_label = options['app_label'] if app_label not in executor.loader.migrated_apps: raise CommandError("App '%s' does not have migrations." % app_label) targets = [ key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label ] else: targets = executor.loader.graph.leaf_nodes() plan = executor.migration_plan(targets) run_syncdb = options['run_syncdb'] and executor.loader.unmigrated_apps # Print some useful info if self.verbosity >= 1: self.stdout.write( self.style.MIGRATE_HEADING("Operations to perform:")) if run_syncdb: self.stdout.write( self.style.MIGRATE_LABEL(" Synchronize unmigrated apps: ") + (", ".join(sorted(executor.loader.unmigrated_apps)))) if target_app_labels_only: self.stdout.write( self.style.MIGRATE_LABEL(" Apply all migrations: ") + (", ".join(sorted(set(a for a, n in targets))) or "(none)")) else: if targets[0][1] is None: self.stdout.write( self.style.MIGRATE_LABEL(" Unapply all migrations: ") + "%s" % (targets[0][0], )) else: self.stdout.write( self.style.MIGRATE_LABEL( " Target specific migration: ") + "%s, from %s" % (targets[0][1], targets[0][0])) emit_pre_migrate_signal(self.verbosity, self.interactive, connection.alias) # Run the syncdb phase. if run_syncdb: if self.verbosity >= 1: self.stdout.write( self.style.MIGRATE_HEADING( "Synchronizing apps without migrations:")) self.sync_apps(connection, executor.loader.unmigrated_apps) # Migrate! if self.verbosity >= 1: self.stdout.write( self.style.MIGRATE_HEADING("Running migrations:")) if not plan: executor.check_replacements() if self.verbosity >= 1: self.stdout.write(" No migrations to apply.") # If there's changes that aren't in migrations yet, tell them how to fix it. autodetector = MigrationAutodetector( executor.loader.project_state(), ProjectState.from_apps(apps), ) changes = autodetector.changes(graph=executor.loader.graph) if changes: self.stdout.write( self.style.NOTICE( " Your models have changes that are not yet reflected " "in a migration, and so won't be applied.")) self.stdout.write( self.style.NOTICE( " Run 'manage.py makemigrations' to make new " "migrations, and then re-run 'manage.py migrate' to " "apply them.")) else: fake = options['fake'] fake_initial = options['fake_initial'] executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial) # Send the post_migrate signal, so individual apps can do whatever they need # to do at this point. emit_post_migrate_signal(self.verbosity, self.interactive, connection.alias)
def test_migrate_to_flat_answers(transactional_db): executor = MigrationExecutor(connection) app = "form" migrate_from = [(app, "0017_auto_20190619_1320")] migrate_to = [(app, "0019_remove_answer_value_document")] executor.migrate(migrate_from) old_apps = executor.loader.project_state(migrate_from).apps # Create some old data. Can't use factories here Document = old_apps.get_model(app, "Document") Form = old_apps.get_model(app, "Form") Answer = old_apps.get_model(app, "Answer") Question = old_apps.get_model(app, "Question") FormQuestion = old_apps.get_model(app, "FormQuestion") main_form = Form.objects.create(slug="main-form") sub_form = Form.objects.create(slug="sub-form") main_form_question = Question.objects.create(type="form", sub_form=sub_form, slug="main-form-question") FormQuestion.objects.create(form=main_form, question=main_form_question) main_text_question = Question.objects.create(type="text", slug="main-text-question") FormQuestion.objects.create(form=main_form, question=main_text_question) sub_text_question = Question.objects.create(type="text", slug="sub_1_question_1") FormQuestion.objects.create(form=sub_form, question=sub_text_question) # we need to set a temporary family, because the signals are not available main_document = Document.objects.create(form=main_form, family=uuid4()) # then we set the correct family main_document.family = main_document.pk main_document.save() sub_document = Document.objects.create(form=sub_form, family=main_document.pk) sub_answer = Answer.objects.create(value="lorem ipsum", question=sub_text_question, document=sub_document) Answer.objects.create(value_document=sub_document, question=main_form_question, document=main_document) text_answer = Answer.objects.create(value="dolor sit", question=main_text_question, document=main_document) assert not sub_answer.document == main_document assert text_answer.document == main_document # Migrate forwards. executor.loader.build_graph() # reload. executor.migrate(migrate_to) new_apps = executor.loader.project_state(migrate_to).apps # Test the new data. Answer = new_apps.get_model(app, "Answer") sub_answer = Answer.objects.get(value="lorem ipsum") text_answer = Answer.objects.get(value="dolor sit") assert Answer.objects.filter(question__type="form").count() == 0 assert sub_answer.document.pk == main_document.pk assert text_answer.document.pk == main_document.pk
class BaseCommentMigrationTest(TransactionTestCase): """ Test specific migrations Make sure that `self.migrate_from` and `self.migrate_to` are defined. """ @property def app(self): return apps.get_containing_app_config(type(self).__module__).name migrate_from = None migrate_to = None def setUp(self): super().setUp() assert self.migrate_to and self.migrate_from, \ f'TestCase {type(self).__name__} must define migrate_to and migrate_from properties' self.migrate_from = [(self.app, self.migrate_from)] self.migrate_to = [(self.app, self.migrate_to)] self.executor = MigrationExecutor(connection) self.old_apps = self.executor.loader.project_state( self.migrate_from).apps self._create_data() # revert to the original migration self.executor.migrate(self.migrate_from) # ensure return to the latest migration, even if the test fails self.addCleanup(self.force_migrate) self.setUpBeforeMigration() self.executor.loader.build_graph() self.executor.migrate(self.migrate_to) self.new_apps = self.executor.loader.project_state( self.migrate_to).apps def setUpBeforeMigration(self): pass @property def new_model(self): return self.new_apps.get_model(self.app, 'Comment') @property def old_model(self): return self.old_apps.get_model(self.app, 'Comment') def force_migrate(self, migrate_to=None): self.executor.loader.build_graph() # reload. if migrate_to is None: # get latest migration of current app migrate_to = [ key for key in self.executor.loader.graph.leaf_nodes() if key[0] == self.app ] self.executor.migrate(migrate_to) def _create_data(self): self.user = User.objects.create_user(username="******") self.post = Post.objects.create(author=self.user, title="post 3", body="third post body") content_type = ContentType.objects.get(model='post') self.ct_object = content_type.get_object_for_this_type(id=self.post.id)
def handle(self, *args, **options): self.verbosity = int(options.get('verbosity')) self.interactive = options.get('interactive') self.show_traceback = options.get('traceback') self.load_initial_data = options.get('load_initial_data') self.test_database = options.get('test_database', False) # Import the 'management' module within each installed app, to register # dispatcher events. for app_config in apps.get_app_configs(): if module_has_submodule(app_config.module, "management"): import_module('.management', app_config.name) # Get the database we're operating from db = options.get('database') connection = connections[db] # If they asked for a migration listing, quit main execution flow and show it if options.get("list", False): return self.show_migration_list(connection, args) # Work out which apps have migrations and which do not executor = MigrationExecutor(connection, self.migration_progress_callback) # Before anything else, see if there's conflicting apps and drop out # hard if there are any conflicts = executor.loader.detect_conflicts() if conflicts: name_str = "; ".join("%s in %s" % (", ".join(names), app) for app, names in conflicts.items()) raise CommandError( "Conflicting migrations detected (%s).\nTo fix them run 'python manage.py makemigrations --merge'" % name_str) # If they supplied command line arguments, work out what they mean. run_syncdb = False target_app_labels_only = True if len(args) > 2: raise CommandError( "Too many command-line arguments (expecting 'app_label' or 'app_label migrationname')" ) elif len(args) == 2: app_label, migration_name = args if app_label not in executor.loader.migrated_apps: raise CommandError( "App '%s' does not have migrations (you cannot selectively sync unmigrated apps)" % app_label) if migration_name == "zero": targets = [(app_label, None)] else: try: migration = executor.loader.get_migration_by_prefix( app_label, migration_name) except AmbiguityError: raise CommandError( "More than one migration matches '%s' in app '%s'. Please be more specific." % (app_label, migration_name)) except KeyError: raise CommandError( "Cannot find a migration matching '%s' from app '%s'." % (app_label, migration_name)) targets = [(app_label, migration.name)] target_app_labels_only = False elif len(args) == 1: app_label = args[0] if app_label not in executor.loader.migrated_apps: raise CommandError( "App '%s' does not have migrations (you cannot selectively sync unmigrated apps)" % app_label) targets = [ key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label ] else: targets = executor.loader.graph.leaf_nodes() run_syncdb = True plan = executor.migration_plan(targets) # Print some useful info if self.verbosity >= 1: self.stdout.write( self.style.MIGRATE_HEADING("Operations to perform:")) if run_syncdb: self.stdout.write( self.style.MIGRATE_LABEL(" Synchronize unmigrated apps: ") + (", ".join(executor.loader.unmigrated_apps) or "(none)")) if target_app_labels_only: self.stdout.write( self.style.MIGRATE_LABEL(" Apply all migrations: ") + (", ".join(set(a for a, n in targets)) or "(none)")) else: if targets[0][1] is None: self.stdout.write( self.style.MIGRATE_LABEL(" Unapply all migrations: ") + "%s" % (targets[0][0], )) else: self.stdout.write( self.style.MIGRATE_LABEL( " Target specific migration: ") + "%s, from %s" % (targets[0][1], targets[0][0])) # Run the syncdb phase. # If you ever manage to get rid of this, I owe you many, many drinks. # Note that pre_migrate is called from inside here, as it needs # the list of models about to be installed. if run_syncdb: if self.verbosity >= 1: self.stdout.write( self.style.MIGRATE_HEADING( "Synchronizing apps without migrations:")) created_models = self.sync_apps(connection, executor.loader.unmigrated_apps) else: created_models = [] # Migrate! if self.verbosity >= 1: self.stdout.write( self.style.MIGRATE_HEADING("Running migrations:")) if not plan: if self.verbosity >= 1: self.stdout.write(" No migrations needed.") # If there's changes that aren't in migrations yet, tell them how to fix it. autodetector = MigrationAutodetector( executor.loader.project_state(), ProjectState.from_apps(apps), ) changes = autodetector.changes(graph=executor.loader.graph) if changes: self.stdout.write( self.style.NOTICE( " Your models have changes that are not yet reflected in a migration, and so won't be applied." )) self.stdout.write( self.style.NOTICE( " Run 'manage.py makemigrations' to make new migrations, and then re-run 'manage.py migrate' to apply them." )) else: executor.migrate(targets, plan, fake=options.get("fake", False)) # Send the post_migrate signal, so individual apps can do whatever they need # to do at this point. emit_post_migrate_signal(created_models, self.verbosity, self.interactive, connection.alias)
Finding duplicate file chunks. This needs to be done in a memory-safe, so it could take several minutes. When the initial database query operation finishes you will start seeing additional output. This script is incremental. You can stop it at any time and restart it later. DO NOT RUN MULTIPLE INSTANCES OF THIS SCRIPT AT THE SAME TIME. """) from django.db.migrations.executor import MigrationExecutor from django.db import connections, DEFAULT_DB_ALIAS connection = connections[DEFAULT_DB_ALIAS] connection.prepare_database() applied_migrations = set(migration_name for _, migration_name in MigrationExecutor( connection).loader.applied_migrations) if "0025_auto_20200106_2153.py" in applied_migrations: print("\nYour chunk paths are already unique.\n") exit(0) DEBUG = False if DEBUG: print("\nRUNNING IN DEBUG MODE, NO DESTRUCTIVE ACTIONS WILL BE TAKEN.\n") def run(): duplicate_chunks, duplicate_media = get_duplicates() for media_file in duplicate_media: remove_all_but_one_chunk(media_file)
def test_dynamic_option_unique_together(transactional_db): executor = MigrationExecutor(connection) app = "caluma_form" migrate_from = [(app, "0028_auto_20200210_0929")] migrate_to = [(app, "0029_dynamic_option_unique_together")] executor.migrate(migrate_from) old_apps = executor.loader.project_state(migrate_from).apps # Create some old data. Can't use factories here Document = old_apps.get_model(app, "Document") Form = old_apps.get_model(app, "Form") Question = old_apps.get_model(app, "Question") DynamicOption = old_apps.get_model(app, "DynamicOption") HistoricalDynamicOption = old_apps.get_model("caluma_form", "HistoricalDynamicOption") form = Form.objects.create(slug="main-form") question = Question.objects.create(type="text", slug="main-form-question") # we need to set a temporary family, because the signals are not available document = Document.objects.create(form=form, family=uuid4()) # then we set the correct family document.family = document.pk document.save() d_option_1 = DynamicOption.objects.create(document=document, question=question, slug="foo") d_option_2 = DynamicOption.objects.create(document=document, question=question, slug="foo") # again no signals HistoricalDynamicOption.objects.create( document=document, question=question, history_type="+", created_at=d_option_1.created_at, modified_at=d_option_1.modified_at, history_date=d_option_1.modified_at, slug="foo", id=d_option_1.pk, ) HistoricalDynamicOption.objects.create( document=document, question=question, history_type="+", created_at=d_option_2.created_at, modified_at=d_option_2.modified_at, history_date=d_option_2.modified_at, slug="foo", id=d_option_2.pk, ) # Migrate forwards. executor.loader.build_graph() # reload. executor.migrate(migrate_to) new_apps = executor.loader.project_state(migrate_to).apps # Test the new data. DynamicOption = new_apps.get_model(app, "DynamicOption") HistoricalDynamicOption = new_apps.get_model("caluma_form", "HistoricalDynamicOption") Document = new_apps.get_model(app, "Document") Question = new_apps.get_model(app, "Question") document = Document.objects.get(pk=document.pk) question = Question.objects.get(pk=question.pk) assert DynamicOption.objects.get(document=document, question=question, slug="foo") assert (HistoricalDynamicOption.objects.filter(document=document, question=question, slug="foo").count() == 1)
def prevent_tests_migrate(db): from django.db import connections from django.db.migrations.executor import MigrationExecutor ma = MigrationExecutor(connections[db]).loader.migrated_apps return dict(zip(ma, ['{a}.notmigrations'.format(a=a) for a in ma]))
def process_request(self, request): executor = MigrationExecutor(connection) plan = executor.migration_plan(executor.loader.graph.leaf_nodes()) if bool(plan) and \ getattr(resolve(request.path), 'url_name', '') != 'migrations_notran': return redirect(reverse("ui:migrations_notran"))
def handle(self, *args, **options): database = options['database'] if not options['skip_checks']: self.check(databases=[database]) self.verbosity = options['verbosity'] self.interactive = options['interactive'] # Import the 'management' module within each installed BLOG, to register # dispatcher events. for app_config in apps.get_app_configs(): if module_has_submodule(app_config.module, "management"): import_module('.management', app_config.name) # Get the database we're operating from connection = connections[database] # Hook for backends needing any database preparation connection.prepare_database() # Work out which apps have migrations and which do not executor = MigrationExecutor(connection, self.migration_progress_callback) # Raise an error if any migrations are applied before their dependencies. executor.loader.check_consistent_history(connection) # Before anything else, see if there's conflicting apps and drop out # hard if there are any conflicts = executor.loader.detect_conflicts() if conflicts: name_str = "; ".join( "%s in %s" % (", ".join(names), app) for app, names in conflicts.items() ) raise CommandError( "Conflicting migrations detected; multiple leaf nodes in the " "migration graph: (%s).\nTo fix them run " "'python manage.py makemigrations --merge'" % name_str ) # If they supplied command line arguments, work out what they mean. run_syncdb = options['run_syncdb'] target_app_labels_only = True if options['app_label']: # Validate app_label. app_label = options['app_label'] try: apps.get_app_config(app_label) except LookupError as err: raise CommandError(str(err)) if run_syncdb: if app_label in executor.loader.migrated_apps: raise CommandError("Can't use run_syncdb with BLOG '%s' as it has migrations." % app_label) elif app_label not in executor.loader.migrated_apps: raise CommandError("App '%s' does not have migrations." % app_label) if options['app_label'] and options['migration_name']: migration_name = options['migration_name'] if migration_name == "zero": targets = [(app_label, None)] else: try: migration = executor.loader.get_migration_by_prefix(app_label, migration_name) except AmbiguityError: raise CommandError( "More than one migration matches '%s' in BLOG '%s'. " "Please be more specific." % (migration_name, app_label) ) except KeyError: raise CommandError("Cannot find a migration matching '%s' from BLOG '%s'." % ( migration_name, app_label)) targets = [(app_label, migration.name)] target_app_labels_only = False elif options['app_label']: targets = [key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label] else: targets = executor.loader.graph.leaf_nodes() plan = executor.migration_plan(targets) exit_dry = plan and options['check_unapplied'] if options['plan']: self.stdout.write('Planned operations:', self.style.MIGRATE_LABEL) if not plan: self.stdout.write(' No planned migration operations.') for migration, backwards in plan: self.stdout.write(str(migration), self.style.MIGRATE_HEADING) for operation in migration.operations: message, is_error = self.describe_operation(operation, backwards) style = self.style.WARNING if is_error else None self.stdout.write(' ' + message, style) if exit_dry: sys.exit(1) return if exit_dry: sys.exit(1) # At this point, ignore run_syncdb if there aren't any apps to sync. run_syncdb = options['run_syncdb'] and executor.loader.unmigrated_apps # Print some useful info if self.verbosity >= 1: self.stdout.write(self.style.MIGRATE_HEADING("Operations to perform:")) if run_syncdb: if options['app_label']: self.stdout.write( self.style.MIGRATE_LABEL(" Synchronize unmigrated BLOG: %s" % app_label) ) else: self.stdout.write( self.style.MIGRATE_LABEL(" Synchronize unmigrated apps: ") + (", ".join(sorted(executor.loader.unmigrated_apps))) ) if target_app_labels_only: self.stdout.write( self.style.MIGRATE_LABEL(" Apply all migrations: ") + (", ".join(sorted({a for a, n in targets})) or "(none)") ) else: if targets[0][1] is None: self.stdout.write( self.style.MIGRATE_LABEL(' Unapply all migrations: ') + str(targets[0][0]) ) else: self.stdout.write(self.style.MIGRATE_LABEL( " Target specific migration: ") + "%s, from %s" % (targets[0][1], targets[0][0]) ) pre_migrate_state = executor._create_project_state(with_applied_migrations=True) pre_migrate_apps = pre_migrate_state.apps emit_pre_migrate_signal( self.verbosity, self.interactive, connection.alias, apps=pre_migrate_apps, plan=plan, ) # Run the syncdb phase. if run_syncdb: if self.verbosity >= 1: self.stdout.write(self.style.MIGRATE_HEADING("Synchronizing apps without migrations:")) if options['app_label']: self.sync_apps(connection, [app_label]) else: self.sync_apps(connection, executor.loader.unmigrated_apps) # Migrate! if self.verbosity >= 1: self.stdout.write(self.style.MIGRATE_HEADING("Running migrations:")) if not plan: if self.verbosity >= 1: self.stdout.write(" No migrations to apply.") # If there's changes that aren't in migrations yet, tell them how to fix it. autodetector = MigrationAutodetector( executor.loader.project_state(), ProjectState.from_apps(apps), ) changes = autodetector.changes(graph=executor.loader.graph) if changes: self.stdout.write(self.style.NOTICE( " Your models in BLOG(s): %s have changes that are not " "yet reflected in a migration, and so won't be " "applied." % ", ".join(repr(app) for app in sorted(changes)) )) self.stdout.write(self.style.NOTICE( " Run 'manage.py makemigrations' to make new " "migrations, and then re-run 'manage.py migrate' to " "apply them." )) fake = False fake_initial = False else: fake = options['fake'] fake_initial = options['fake_initial'] post_migrate_state = executor.migrate( targets, plan=plan, state=pre_migrate_state.clone(), fake=fake, fake_initial=fake_initial, ) # post_migrate signals have access to all models. Ensure that all models # are reloaded in case any are delayed. post_migrate_state.clear_delayed_apps_cache() post_migrate_apps = post_migrate_state.apps # Re-render models of real apps to include relationships now that # we've got a final state. This wouldn't be necessary if real apps # models were rendered with relationships in the first place. with post_migrate_apps.bulk_update(): model_keys = [] for model_state in post_migrate_apps.real_models: model_key = model_state.app_label, model_state.name_lower model_keys.append(model_key) post_migrate_apps.unregister_model(*model_key) post_migrate_apps.render_multiple([ ModelState.from_model(apps.get_model(*model)) for model in model_keys ]) # Send the post_migrate signal, so individual apps can do whatever they need # to do at this point. emit_post_migrate_signal( self.verbosity, self.interactive, connection.alias, apps=post_migrate_apps, plan=plan, )
def handle(self, **options): self.verbosity = options.get('verbosity') self.interactive = options.get('interactive') app_label, migration_name = options['app_label'], options[ 'migration_name'] # Load the current graph state, check the app and migration they asked for exists executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS]) if app_label not in executor.loader.migrated_apps: raise CommandError( "App '%s' does not have migrations (so squashmigrations on it makes no sense)" % app_label) try: migration = executor.loader.get_migration_by_prefix( app_label, migration_name) except AmbiguityError: raise CommandError( "More than one migration matches '%s' in app '%s'. Please be more specific." % (migration_name, app_label)) except KeyError: raise CommandError( "Cannot find a migration matching '%s' from app '%s'." % (migration_name, app_label)) # Work out the list of predecessor migrations migrations_to_squash = [ executor.loader.get_migration(al, mn) for al, mn in executor.loader.graph.forwards_plan(( migration.app_label, migration.name)) if al == migration.app_label ] # Tell them what we're doing and optionally ask if we should proceed if self.verbosity > 0 or self.interactive: self.stdout.write( self.style.MIGRATE_HEADING( "Will squash the following migrations:")) for migration in migrations_to_squash: self.stdout.write(" - %s" % migration.name) if self.interactive: answer = None while not answer or answer not in "yn": answer = six.moves.input("Do you wish to proceed? [yN] ") if not answer: answer = "n" break else: answer = answer[0].lower() if answer != "y": return # Load the operations from all those migrations and concat together, # along with collecting external dependencies and detecting # double-squashing operations = [] dependencies = set() for smigration in migrations_to_squash: if smigration.replaces: raise CommandError( "You cannot squash squashed migrations! Please transition it to a normal migration first: https://docs.djangoproject.com/en/1.7/topics/migrations/#squashing-migrations" ) operations.extend(smigration.operations) for dependency in smigration.dependencies: if isinstance(dependency, SwappableTuple): if settings.AUTH_USER_MODEL == dependency.setting: dependencies.add(("__setting__", "AUTH_USER_MODEL")) else: dependencies.add(dependency) elif dependency[0] != smigration.app_label: dependencies.add(dependency) if self.verbosity > 0: self.stdout.write(self.style.MIGRATE_HEADING("Optimizing...")) optimizer = MigrationOptimizer() new_operations = optimizer.optimize(operations, migration.app_label) if self.verbosity > 0: if len(new_operations) == len(operations): self.stdout.write(" No optimizations possible.") else: self.stdout.write( " Optimized from %s operations to %s operations." % (len(operations), len(new_operations))) # Work out the value of replaces (any squashed ones we're re-squashing) # need to feed their replaces into ours replaces = [] for migration in migrations_to_squash: if migration.replaces: replaces.extend(migration.replaces) else: replaces.append((migration.app_label, migration.name)) # Make a new migration with those operations subclass = type( "Migration", (migrations.Migration, ), { "dependencies": dependencies, "operations": new_operations, "replaces": replaces, }) new_migration = subclass("0001_squashed_%s" % migration.name, app_label) # Write out the new migration file writer = MigrationWriter(new_migration) with open(writer.path, "wb") as fh: fh.write(writer.as_string()) if self.verbosity > 0: self.stdout.write( self.style.MIGRATE_HEADING( "Created new squashed migration %s" % writer.path)) self.stdout.write( " You should commit this migration but leave the old ones in place;" ) self.stdout.write( " the new migration will be used for new installs. Once you are sure" ) self.stdout.write( " all instances of the codebase have applied the migrations you squashed," ) self.stdout.write(" you can delete them.") if writer.needs_manual_porting: self.stdout.write( self.style.MIGRATE_HEADING("Manual porting required")) self.stdout.write( " Your migrations contained functions that must be manually copied over," ) self.stdout.write( " as we could not safely copy their implementation.") self.stdout.write( " See the comment at the top of the squashed migration for details." )
def handle(self, *args, **options): self.branch = options['branch'] self.verbosity = options['verbosity'] self.interactive = options['interactive'] # Import the 'management' module within each installed app, to register # dispatcher events. for app_config in apps.get_app_configs(): if module_has_submodule(app_config.module, "management"): import_module('.management', app_config.name) # Get the database we're operating from db = options['database'] connection = connections[db] # Hook for backends needing any database preparation connection.prepare_database() # Work out which apps have migrations and which do not executor = MigrationExecutor(connection, self.migration_progress_callback) # Raise an error if any migrations are applied before their dependencies. executor.loader.check_consistent_history(connection) targets = [] migrations = {} migration_paths = self.get_diff_migrations() # Group diff migrations by app_label for p in migration_paths: app_label = p.parts[0] if app_label not in migrations: # Validate app_label. try: apps.get_app_config(app_label) except LookupError as err: raise CommandError(str(err)) migrations[app_label] = [] migrations[app_label].append(p.stem) # Get last applied migration for each app for app_label, migration_names in migrations.items(): mr = MigrationRecorder.Migration.objects.filter(app=app_label) # Skip if already unapplied if not mr.filter(name__in=migration_names).count(): continue mr = mr.exclude( name__in=migration_names).order_by('applied').last() if mr: targets.append((app_label, mr.name)) if not targets: if self.verbosity >= 1: self.stdout.write(" No migrations to unapply.") return plan = executor.migration_plan(targets) if options['plan']: self.stdout.write('Planned operations:', self.style.MIGRATE_LABEL) if not plan: self.stdout.write(' No planned migration operations.') for migration, backwards in plan: self.stdout.write(str(migration), self.style.MIGRATE_HEADING) for operation in migration.operations: message, is_error = self.describe_operation( operation, backwards) style = self.style.WARNING if is_error else None self.stdout.write(' ' + message, style) return # Print some useful info if self.verbosity >= 1: self.stdout.write( self.style.MIGRATE_HEADING("Operations to perform:")) for app_label, migration_name in targets: self.stdout.write( self.style.MIGRATE_LABEL(" Target specific migration: ") + "%s, from %s" % (app_label, migration_name)) # noinspection PyProtectedMember pre_migrate_state = executor._create_project_state( with_applied_migrations=True) pre_migrate_apps = pre_migrate_state.apps emit_pre_migrate_signal( self.verbosity, self.interactive, connection.alias, apps=pre_migrate_apps, plan=plan, ) # Migrate! if self.verbosity >= 1: self.stdout.write( self.style.MIGRATE_HEADING("Running migrations:")) if not plan: fake = False fake_initial = False else: fake = options['fake'] fake_initial = options['fake_initial'] post_migrate_state = executor.migrate( targets, plan=plan, state=pre_migrate_state.clone(), fake=fake, fake_initial=fake_initial, ) # post_migrate signals have access to all models. Ensure that all models # are reloaded in case any are delayed. post_migrate_state.clear_delayed_apps_cache() post_migrate_apps = post_migrate_state.apps # Re-render models of real apps to include relationships now that # we've got a final state. This wouldn't be necessary if real apps # models were rendered with relationships in the first place. with post_migrate_apps.bulk_update(): model_keys = [] for model_state in post_migrate_apps.real_models: model_key = model_state.app_label, model_state.name_lower model_keys.append(model_key) post_migrate_apps.unregister_model(*model_key) post_migrate_apps.render_multiple([ ModelState.from_model(apps.get_model(*model)) for model in model_keys ]) # Send the post_migrate signal, so individual apps can do whatever they need # to do at this point. emit_post_migrate_signal( self.verbosity, self.interactive, connection.alias, apps=post_migrate_apps, plan=plan, )
Vim�UnDo��H�d�9ܟEsG��n�7��ζ���[�0l;1 BlogPost = apps.get_model("blog", "Post")0&]�W�_�����]�W���5�_�����]�W��5�_�����]�W��"from django.apps import apps from django.test import TestCase;from django.db.migrations.executor import MigrationExecutor from django.db import connectionclass TestMigrations(TestCase): @property def app(self):I return apps.get_containing_app_config(type(self).__module__).name migrate_from = None migrate_to = None def setUp(self):7 assert self.migrate_from and self.migrate_to, \n "TestCase '{}' must define migrate_from and migrate_to properties".format(type(self).__name__); self.migrate_from = [(self.app, self.migrate_from)]7 self.migrate_to = [(self.app, self.migrate_to)]0 executor = MigrationExecutor(connection)H 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 test0 executor = MigrationExecutor(connection)0 executor.loader.build_graph() # reload.) executor.migrate(self.migrate_to)G self.apps = executor.loader.project_state(self.migrate_to).apps5�_�$+����]�W��%�%�$5�_�&����]�W��)from django.apps import apps from django.test import TestCase;from django.db.migrations.executor import MigrationExecutor from django.db import connectionclass TestMigrations(TestCase): @property def app(self):I return apps.get_containing_app_config(type(self).__module__).name migrate_from = None migrate_to = None def setUp(self): assert (1 self.migrate_from and self.migrate_toY ), "TestCase '{}' must define migrate_from and migrate_to properties".format( type(self).__name__ ); self.migrate_from = [(self.app, self.migrate_from)]7 self.migrate_to = [(self.app, self.migrate_to)]0 executor = MigrationExecutor(connection)H 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 test0 executor = MigrationExecutor(connection)0 executor.loader.build_graph() # reload.) executor.migrate(self.migrate_to)G self.apps = executor.loader.project_state(self.migrate_to).apps) def setUpBeforeMigration(self, apps): pass5�_�'����]�W��(�)�' �'5�_�)����]�W��(*<"lass TagsTestCase(TestMigrations):5�_� )����]�W��<from django.apps import apps from django.test import TestCase;from django.db.migrations.executor import MigrationExecutor from django.db import connectionclass TestMigrations(TestCase): @property def app(self):I return apps.get_containing_app_config(type(self).__module__).name migrate_from = None migrate_to = None def setUp(self): assert (1 self.migrate_from and self.migrate_toY ), "TestCase '{}' must define migrate_from and migrate_to properties".format( type(self).__name__ ); self.migrate_from = [(self.app, self.migrate_from)]7 self.migrate_to = [(self.app, self.migrate_to)]0 executor = MigrationExecutor(connection)H 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 test0 executor = MigrationExecutor(connection)0 executor.loader.build_graph() # reload.) executor.migrate(self.migrate_to)G self.apps = executor.loader.project_state(self.migrate_to).apps) def setUpBeforeMigration(self, apps): pass#class TagsTestCase(TestMigrations):, migrate_from = '0009_previous_migration'. migrate_to = '0010_migration_being_tested') def setUpBeforeMigration(self, apps):1 BlogPost = apps.get_model('blog', 'Post')/ self.post_id = BlogPost.objects.create(, title = "A test post with tags", body = "", tags = "tag1 tag2", ).id! def test_tags_migrated(self):6 BlogPost = self.apps.get_model('blog', 'Post')4 post = BlogPost.objects.get(id=self.post_id). self.assertEqual(post.tags.count(), 2)9 self.assertEqual(post.tags.all()[0].name, "tag1")9 self.assertEqual(post.tags.all()[1].name, "tag2")5�_� F����]�W��;Y ), "TestCase '{}' must define migrate_from and migrate_to properties".format(5�_� 0&����]�W��/1;1 BlogPost = apps.get_model("blog", "Post")5�_� 0(����]�W��/1;2 BlogPost = apps.get_model("blolg", "Post")5��
def healthcheck(request): executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS]) plan = executor.migration_plan(executor.loader.graph.leaf_nodes()) status = 503 if plan else 200 return HttpResponse(status=status)
def test_detect_soft_applied_add_field_manytomanyfield(self): """ executor.detect_soft_applied() detects ManyToManyField tables from an AddField operation. This checks the case of AddField in a migration with other operations (0001) and the case of AddField in its own migration (0002). """ tables = [ # from 0001 "migrations_project", "migrations_task", "migrations_project_tasks", # from 0002 "migrations_task_projects", ] executor = MigrationExecutor(connection) # Create the tables for 0001 but make it look like the migration hasn't # been applied. executor.migrate([("migrations", "0001_initial")]) executor.migrate([("migrations", None)], fake=True) for table in tables[:3]: self.assertTableExists(table) # Table detection sees 0001 is applied but not 0002. migration = executor.loader.get_migration("migrations", "0001_initial") self.assertIs(executor.detect_soft_applied(None, migration)[0], True) migration = executor.loader.get_migration("migrations", "0002_initial") self.assertIs(executor.detect_soft_applied(None, migration)[0], False) # Create the tables for both migrations but make it look like neither # has been applied. executor.loader.build_graph() executor.migrate([("migrations", "0001_initial")], fake=True) executor.migrate([("migrations", "0002_initial")]) executor.loader.build_graph() executor.migrate([("migrations", None)], fake=True) # Table detection sees 0002 is applied. migration = executor.loader.get_migration("migrations", "0002_initial") self.assertIs(executor.detect_soft_applied(None, migration)[0], True) # Leave the tables for 0001 except the many-to-many table. That missing # table should cause detect_soft_applied() to return False. with connection.schema_editor() as editor: for table in tables[2:]: editor.execute(editor.sql_delete_table % {"table": table}) migration = executor.loader.get_migration("migrations", "0001_initial") self.assertIs(executor.detect_soft_applied(None, migration)[0], False) # Cleanup by removing the remaining tables. with connection.schema_editor() as editor: for table in tables[:2]: editor.execute(editor.sql_delete_table % {"table": table}) for table in tables: self.assertTableNotExists(table)
def handle(self, *arg, **options): # it's necessary to delay this import in case # database migrations are still running from awx.main.models.ha import Instance executor = MigrationExecutor(connection) migrating = bool( executor.migration_plan(executor.loader.graph.leaf_nodes())) registered = False if not migrating: try: Instance.objects.me() registered = True except RuntimeError: pass if migrating or not registered: # In containerized deployments, migrations happen in the task container, # and the services running there don't start until migrations are # finished. # *This* service runs in the web container, and it's possible that it can # start _before_ migrations are finished, thus causing issues with the ORM # queries it makes (specifically, conf.settings queries). # This block is meant to serve as a sort of bail-out for the situation # where migrations aren't yet finished (similar to the migration # detection middleware that the uwsgi processes have) or when instance # registration isn't done yet logger.error( 'AWX is currently installing/upgrading. Trying again in 5s...' ) time.sleep(5) return if options.get('status'): try: stats_all = BroadcastWebsocketStatsManager.get_stats_sync() except redis.exceptions.ConnectionError as e: print( f"Unable to get Broadcast Websocket Status. Failed to connect to redis {e}" ) return data = {} for family in stats_all: if family.type == 'gauge' and len(family.samples) > 1: for sample in family.samples: if sample.value >= 1: data[family.name] = sample.labels[family.name] break else: data[family.name] = family.samples[0].value me = Instance.objects.me() hostnames = [ i.hostname for i in Instance.objects.exclude(hostname=me.hostname) ] host_stats = Command.get_connection_status(me, hostnames, data) lines = Command._format_lines(host_stats) print( f'Broadcast websocket connection status from "{me.hostname}" to:' ) print('\n'.join(lines)) host_stats = Command.get_connection_stats(me, hostnames, data) lines = Command._format_lines(host_stats) print( f'\nBroadcast websocket connection stats from "{me.hostname}" to:' ) print('\n'.join(lines)) return try: broadcast_websocket_mgr = BroadcastWebsocketManager() task = broadcast_websocket_mgr.start() loop = asyncio.get_event_loop() loop.run_until_complete(task) except KeyboardInterrupt: logger.debug('Terminating Websocket Broadcaster')
def handle(self, app_label=None, migration_name=None, **options): self.verbosity = int(options.get('verbosity')) self.interactive = options.get('interactive') if app_label is None or migration_name is None: self.stderr.write(self.usage_str) sys.exit(1) # Load the current graph state, check the app and migration they asked for exists executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS]) if app_label not in executor.loader.migrated_apps: raise CommandError( "App '%s' does not have migrations (so squashmigrations on it makes no sense)" % app_label) try: migration = executor.loader.get_migration_by_prefix( app_label, migration_name) except AmbiguityError: raise CommandError( "More than one migration matches '%s' in app '%s'. Please be more specific." % (migration_name, app_label)) except KeyError: raise CommandError( "Cannot find a migration matching '%s' from app '%s'." % (migration_name, app_label)) # Work out the list of predecessor migrations migrations_to_squash = [ executor.loader.get_migration(al, mn) for al, mn in executor.loader.graph.forwards_plan(( migration.app_label, migration.name)) if al == migration.app_label ] # Tell them what we're doing and optionally ask if we should proceed if self.verbosity > 0 or self.interactive: self.stdout.write( self.style.MIGRATE_HEADING( "Will squash the following migrations:")) for migration in migrations_to_squash: self.stdout.write(" - %s" % migration.name) if self.interactive: answer = None while not answer or answer not in "yn": answer = six.moves.input("Do you wish to proceed? [yN] ") if not answer: answer = "n" break else: answer = answer[0].lower() if answer != "y": return # Load the operations from all those migrations and concat together operations = [] for smigration in migrations_to_squash: operations.extend(smigration.operations) if self.verbosity > 0: self.stdout.write(self.style.MIGRATE_HEADING("Optimizing...")) optimizer = MigrationOptimizer() new_operations = optimizer.optimize(operations, migration.app_label) if self.verbosity > 0: if len(new_operations) == len(operations): self.stdout.write(" No optimizations possible.") else: self.stdout.write( " Optimized from %s operations to %s operations." % (len(operations), len(new_operations))) # Work out the value of replaces (any squashed ones we're re-squashing) # need to feed their replaces into ours replaces = [] for migration in migrations_to_squash: if migration.replaces: replaces.extend(migration.replaces) else: replaces.append((migration.app_label, migration.name)) # Make a new migration with those operations subclass = type( "Migration", (migrations.Migration, ), { "dependencies": [], "operations": new_operations, "replaces": replaces, }) new_migration = subclass("0001_squashed_%s" % migration.name, app_label) # Write out the new migration file writer = MigrationWriter(new_migration) with open(writer.path, "wb") as fh: fh.write(writer.as_string()) if self.verbosity > 0: self.stdout.write( self.style.MIGRATE_HEADING( "Created new squashed migration %s" % writer.path)) self.stdout.write( " You should commit this migration but leave the old ones in place;" ) self.stdout.write( " the new migration will be used for new installs. Once you are sure" ) self.stdout.write( " all instances of the codebase have applied the migrations you squashed," ) self.stdout.write(" you can delete them.")
def flush(self, **options): database = options.get('database') connection = connections[database] verbosity = options.get('verbosity') interactive = options.get('interactive') # The following are stealth options used by Django's internals. reset_sequences = options.get('reset_sequences', True) allow_cascade = options.get('allow_cascade', False) inhibit_post_migrate = options.get('inhibit_post_migrate', False) self.style = no_style() # Import the 'management' module within each installed app, to register # dispatcher events. for app_config in apps.get_app_configs(): try: import_module('.management', app_config.name) except ImportError: pass # custom: only_django False # get current version before flush current_version = get_current_version(connection) sql_list = sql_flush(self.style, connection, only_django=False, reset_sequences=reset_sequences, allow_cascade=allow_cascade) if interactive: confirm = input("""You have requested a flush of the database. This will IRREVERSIBLY DESTROY all data currently in the %r database, and return each table to an empty state. Are you sure you want to do this? Type 'yes' to continue, or 'no' to cancel: """ % connection.settings_dict['NAME']) else: confirm = 'yes' if confirm == 'yes': try: with transaction.atomic( using=database, savepoint=connection.features.can_rollback_ddl): with connection.cursor() as cursor: for sql in sql_list: cursor.execute(sql) except Exception as e: new_msg = ( "Database %s couldn't be flushed. Possible reasons:\n" " * The database isn't running or isn't configured " "correctly.\n" " * At least one of the expected database tables doesn't " "exist.\n" " * The SQL was invalid.\n" "Hint: Look at the output of 'django-admin sqlflush'. " "That's the SQL this command wasn't able to run.\n" "The full error: %s") % (connection.settings_dict['NAME'], e) raise CommandError(new_msg) from e if not inhibit_post_migrate: self.emit_post_migrate(verbosity, interactive, database, current_version) # Reinstall the initial_data fixture. if options.get('load_initial_data'): # Remove any option that is not handle by loaddata # We need to load loaddata command, get its parser to extract # valid options app_name = get_commands()['loaddata'] command = load_command_class(app_name, 'loaddata') parser = command.create_parser('loaddata', 'initial_data') valid_options = [ action.dest for action in parser._actions if action.option_strings ] app_options = { k: v for k, v in options.items() if k in valid_options } # Reinstall the initial_data fixture for apps without # migrations. from django.db.migrations.executor import MigrationExecutor executor = MigrationExecutor(connection) for app_label in executor.loader.unmigrated_apps: app_options['app_label'] = app_label try: call_command('loaddata', 'initial_data', **app_options) except CommandError: # fails with django 1.10 if initial_data does not exist pass else: self.stdout.write("Flush cancelled.\n")
def handle(self, *args, **options): self.verbosity = options.get('verbosity') self.interactive = options.get('interactive') self.show_traceback = options.get('traceback') self.load_initial_data = options.get('load_initial_data') # Import the 'management' module within each installed app, to register # dispatcher events. for app_config in apps.get_app_configs(): if module_has_submodule(app_config.module, "management"): import_module('.management', app_config.name) # Get the database we're operating from db = options.get('database') connection = connections[db] # If they asked for a migration listing, quit main execution flow and show it if options.get("list", False): warnings.warn( "The 'migrate --list' command is deprecated. Use 'showmigrations' instead.", RemovedInDjango20Warning, stacklevel=2) self.stdout.ending = None # Remove when #21429 is fixed return call_command( 'showmigrations', '--list', app_labels=[options['app_label']] if options['app_label'] else None, database=db, no_color=options.get('no_color'), settings=options.get('settings'), stdout=self.stdout, traceback=self.show_traceback, verbosity=self.verbosity, ) # Hook for backends needing any database preparation connection.prepare_database() # Work out which apps have migrations and which do not executor = MigrationExecutor(connection, self.migration_progress_callback) # Before anything else, see if there's conflicting apps and drop out # hard if there are any conflicts = executor.loader.detect_conflicts() if conflicts: name_str = "; ".join("%s in %s" % (", ".join(names), app) for app, names in conflicts.items()) raise CommandError( "Conflicting migrations detected (%s).\nTo fix them run " "'python manage.py makemigrations --merge'" % name_str) # If they supplied command line arguments, work out what they mean. run_syncdb = False target_app_labels_only = True if options['app_label'] and options['migration_name']: app_label, migration_name = options['app_label'], options[ 'migration_name'] if app_label not in executor.loader.migrated_apps: raise CommandError( "App '%s' does not have migrations (you cannot selectively " "sync unmigrated apps)" % app_label) if migration_name == "zero": targets = [(app_label, None)] else: try: migration = executor.loader.get_migration_by_prefix( app_label, migration_name) except AmbiguityError: raise CommandError( "More than one migration matches '%s' in app '%s'. " "Please be more specific." % (migration_name, app_label)) except KeyError: raise CommandError( "Cannot find a migration matching '%s' from app '%s'." % (migration_name, app_label)) targets = [(app_label, migration.name)] target_app_labels_only = False elif options['app_label']: app_label = options['app_label'] if app_label not in executor.loader.migrated_apps: raise CommandError( "App '%s' does not have migrations (you cannot selectively " "sync unmigrated apps)" % app_label) targets = [ key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label ] else: targets = executor.loader.graph.leaf_nodes() run_syncdb = True plan = executor.migration_plan(targets) # Print some useful info if self.verbosity >= 1: self.stdout.write( self.style.MIGRATE_HEADING("Operations to perform:")) if run_syncdb and executor.loader.unmigrated_apps: self.stdout.write( self.style.MIGRATE_LABEL(" Synchronize unmigrated apps: ") + (", ".join(executor.loader.unmigrated_apps))) if target_app_labels_only: self.stdout.write( self.style.MIGRATE_LABEL(" Apply all migrations: ") + (", ".join(set(a for a, n in targets)) or "(none)")) else: if targets[0][1] is None: self.stdout.write( self.style.MIGRATE_LABEL(" Unapply all migrations: ") + "%s" % (targets[0][0], )) else: self.stdout.write( self.style.MIGRATE_LABEL( " Target specific migration: ") + "%s, from %s" % (targets[0][1], targets[0][0])) # Run the syncdb phase. # If you ever manage to get rid of this, I owe you many, many drinks. # Note that pre_migrate is called from inside here, as it needs # the list of models about to be installed. if run_syncdb and executor.loader.unmigrated_apps: if self.verbosity >= 1: self.stdout.write( self.style.MIGRATE_HEADING( "Synchronizing apps without migrations:")) created_models = self.sync_apps(connection, executor.loader.unmigrated_apps) else: created_models = [] emit_pre_migrate_signal([], self.verbosity, self.interactive, connection.alias) # The test runner requires us to flush after a syncdb but before migrations, # so do that here. if options.get("test_flush", False): call_command( 'flush', verbosity=max(self.verbosity - 1, 0), interactive=False, database=db, reset_sequences=False, inhibit_post_migrate=True, ) # Migrate! if self.verbosity >= 1: self.stdout.write( self.style.MIGRATE_HEADING("Running migrations:")) if not plan: if self.verbosity >= 1: self.stdout.write(" No migrations to apply.") # If there's changes that aren't in migrations yet, tell them how to fix it. autodetector = MigrationAutodetector( executor.loader.project_state(), ProjectState.from_apps(apps), ) changes = autodetector.changes(graph=executor.loader.graph) if changes: self.stdout.write( self.style.NOTICE( " Your models have changes that are not yet reflected " "in a migration, and so won't be applied.")) self.stdout.write( self.style.NOTICE( " Run 'manage.py makemigrations' to make new " "migrations, and then re-run 'manage.py migrate' to " "apply them.")) else: fake = options.get("fake") fake_initial = options.get("fake_initial") executor.migrate(targets, plan, fake=fake, fake_initial=fake_initial) # Send the post_migrate signal, so individual apps can do whatever they need # to do at this point. emit_post_migrate_signal(created_models, self.verbosity, self.interactive, connection.alias)
def test_fix_primary_email_migration(transactional_db): executor = MigrationExecutor(connection) app = "profiles" migrate_from = [(app, "0024_order_emails")] migrate_to = [(app, "0025_fix_primary_emails")] executor.migrate(migrate_from) old_apps = executor.loader.project_state(migrate_from).apps # Create some old data. Profile = old_apps.get_model(app, "Profile") Email = old_apps.get_model(app, "Email") profile_with_one_primary_email = Profile.objects.create() Email.objects.create( profile=profile_with_one_primary_email, email="*****@*****.**", primary=True, ) profile_with_emails_but_no_primary_email = Profile.objects.create() Email.objects.create( profile=profile_with_emails_but_no_primary_email, email="*****@*****.**", primary=False, ) profile_with_no_email = Profile.objects.create() profile_with_multiple_primary_emails = Profile.objects.create() Email.objects.create( profile=profile_with_multiple_primary_emails, email="*****@*****.**", primary=True, ) Email.objects.create( profile=profile_with_multiple_primary_emails, email="*****@*****.**", primary=True, ) # Migrate forwards. executor.loader.build_graph() # reload. executor.migrate(migrate_to) new_apps = executor.loader.project_state(migrate_to).apps # Test the new data. Profile = new_apps.get_model(app, "Profile") profile = Profile.objects.get(pk=profile_with_one_primary_email.pk) assert profile.emails.filter(primary=True).count() == 1 profile = Profile.objects.get( pk=profile_with_emails_but_no_primary_email.pk) assert profile.emails.filter(primary=True).count() == 1 with pytest.raises(Profile.DoesNotExist): Profile.objects.get(pk=profile_with_no_email.pk) profile = Profile.objects.get(pk=profile_with_multiple_primary_emails.pk) assert profile.emails.filter(primary=True).count() == 1
def is_database_synchronized(database): connection = connections[database] connection.prepare_database() executor = MigrationExecutor(connection) targets = executor.loader.graph.leaf_nodes() return False if executor.migration_plan(targets) else True
def handle_noargs(self, **options): database = options.get('database') connection = connections[database] verbosity = int(options.get('verbosity')) interactive = options.get('interactive') # The following are stealth options used by Django's internals. reset_sequences = options.get('reset_sequences', True) allow_cascade = options.get('allow_cascade', False) inhibit_post_migrate = options.get('inhibit_post_migrate', False) self.style = no_style() # Import the 'management' module within each installed app, to register # dispatcher events. for app_config in apps.get_app_configs(): try: import_module('.management', app_config.name) except ImportError: pass sql_list = sql_flush(self.style, connection, only_django=True, reset_sequences=reset_sequences, allow_cascade=allow_cascade) if interactive: confirm = input("""You have requested a flush of the database. This will IRREVERSIBLY DESTROY all data currently in the %r database, and return each table to an empty state. Are you sure you want to do this? Type 'yes' to continue, or 'no' to cancel: """ % connection.settings_dict['NAME']) else: confirm = 'yes' if confirm == 'yes': try: with transaction.atomic( using=database, savepoint=connection.features.can_rollback_ddl): with connection.cursor() as cursor: for sql in sql_list: cursor.execute(sql) except Exception as e: new_msg = ( "Database %s couldn't be flushed. Possible reasons:\n" " * The database isn't running or isn't configured correctly.\n" " * At least one of the expected database tables doesn't exist.\n" " * The SQL was invalid.\n" "Hint: Look at the output of 'django-admin.py sqlflush'. That's the SQL this command wasn't able to run.\n" "The full error: %s") % (connection.settings_dict['NAME'], e) six.reraise(CommandError, CommandError(new_msg), sys.exc_info()[2]) if not inhibit_post_migrate: self.emit_post_migrate(verbosity, interactive, database) # Reinstall the initial_data fixture. if options.get('load_initial_data'): # Reinstall the initial_data fixture for apps without migrations. from django.db.migrations.executor import MigrationExecutor executor = MigrationExecutor(connection) app_options = options.copy() for app_label in executor.loader.unmigrated_apps: app_options['app_label'] = app_label call_command('loaddata', 'initial_data', **app_options) else: self.stdout.write("Flush cancelled.\n")