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 migrate(): """Migrate the database. This is a simplified version of Django's ``migrate`` management command that does not require ``management.call_command`` which is not functional in the native binary environment. """ # Import the 'management' module within each installed app, to register # dispatcher events. verbosity = int(settings.DEBUG) interactive = False 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, migration_progress_callback) executor.loader.check_consistent_history(connection) conflicts = executor.loader.detect_conflicts() if conflicts: raise Exception("Conflicting mgirations detected.") targets = executor.loader.graph.leaf_nodes() plan = executor.migration_plan(targets) pre_migrate_state = executor._create_project_state( with_applied_migrations=True) pre_migrate_apps = pre_migrate_state.apps emit_pre_migrate_signal( verbosity, False, connection.alias, apps=pre_migrate_apps, plan=plan, ) post_migrate_state = executor.migrate( targets, plan=plan, state=pre_migrate_state.clone(), fake=False, fake_initial=False, ) post_migrate_state.clear_delayed_apps_cache() post_migrate_apps = post_migrate_state.apps 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 ]) emit_post_migrate_signal( verbosity, interactive, connection.alias, apps=post_migrate_apps, plan=plan, ) logger.info("Database configured.")
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(True) 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 create(model, using): """ Mostly lifted from django.core.management.commands.migrate """ # Get database connection and cursor db = using or DEFAULT_DB_ALIAS connection = connections[db] cursor = connection.cursor() try: # Get a list of already installed *models* so that references work right. tables = connection.introspection.table_names() seen_models = connection.introspection.installed_models(tables) created_models = set() pending_references = {} # Abort if the table we're going to create already exists if model._meta.db_table in tables: return emit_pre_migrate_signal(0, False, connection.alias) # with transaction.atomic(using=connection.alias, savepoint=False): # sql, references = connection.creation.sql_create_model(model, no_style(), seen_models) # seen_models.add(model) # created_models.add(model) # for refto, refs in references.items(): # pending_references.setdefault(refto, []).extend(refs) # if refto in seen_models: # sql.extend(connection.creation.sql_for_pending_references(refto, no_style(), pending_references)) # sql.extend(connection.creation.sql_for_pending_references(model, no_style(), pending_references)) # # for statement in sql: # cursor.execute(statement) # custom_sql = custom_sql_for_model(model, no_style(), connection) # for sql in custom_sql: # cursor.execute(sql) # index_sql = connection.creation.sql_indexes_for_model(model, no_style()) # for sql in index_sql: # cursor.execute(sql) # tables.append(connection.introspection.table_name_converter(model._meta.db_table)) with transaction.atomic( using=connection.alias, savepoint=connection.features.can_rollback_ddl): deferred_sql = [] if model._meta.can_migrate(connection): with connection.schema_editor() as editor: editor.create_model(model) deferred_sql.extend(editor.deferred_sql) editor.deferred_sql = [] created_models.add(model) for statement in deferred_sql: cursor.execute(statement) finally: cursor.close() # Send the post_syncdb signal, so individual apps can do whatever they need # to do at this point. emit_post_migrate_signal(0, False, connection.alias) create_content_type(model)
def run_wagtail_migration_before_core_34(apps, schema_editor): """ Migration 34 needs migration 0040 from wagtail core and this Migration will run wagtail migration before molo core migration 34 """ db_alias = schema_editor.connection.alias emit_pre_migrate_signal(verbosity=2, interactive=False, db=db_alias)
def handle(self, app_label, migration_name, *args, **options): self.verbosity = options.get('verbosity') interactive = options.get('interactive') record = options.get('record') connection = connections[DEFAULT_DB_ALIAS] connection.prepare_database() executor = MigrationExecutor(connection, None) # 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 app_label not in executor.loader.migrated_apps: raise CommandError( "App '%s' does not have migrations (you cannot selectively sync unmigrated apps)" % 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)) migration_module = import_module(migration.__module__) # check migration can be run offline apply_function = getattr(migration_module, APPLY_FUNCTION, None) if not apply_function or not callable(apply_function): raise CommandError("Migration %s does not contain function named '%s'" % (migration, APPLY_FUNCTION)) plan = executor.migration_plan([(app_label, migration.name)]) if record and not plan: raise CommandError("Migration %s has already been applied" % migration) emit_pre_migrate_signal(self.verbosity, interactive, connection.alias) self.stdout.write(self.style.MIGRATE_HEADING("Operations to perform:")) self.stdout.write(" Manually apply migration %s" % migration) if record: self.stdout.write(" Record migration %s as applied" % migration) self.stdout.write(self.style.MIGRATE_HEADING("Manual migration:")) self.apply_migration(migration, apply_function) if record: self.record_migration(migration, executor) # send the post_migrate signal, so individual apps can do whatever they need to do at this point. emit_post_migrate_signal(self.verbosity, interactive, connection.alias)
def emit_pre_migrate_or_sync(verbosity, interactive, database_name, create_models, pre_migrate_state, plan): """Emit the pre_migrate and/or pre_sync signals. This will emit the :py:data:`~django.db.models.signals.pre_migrate` and/or :py:data:`~django.db.models.signals.pre_sync` signals, providing the appropriate arguments for the current version of Django. Args: verbosity (int): The verbosity level for output. interactive (bool): Whether handlers of the signal can prompt on the terminal for input. database_name (unicode): The name of the database being migrated. create_models (list of django.db.models.Model): The list of models being created outside of any migrations. pre_migrate_state (django.db.migrations.state.ProjectState): The project state prior to any migrations. plan (list): The full migration plan being applied. """ emit_kwargs = { 'db': database_name, 'interactive': interactive, 'verbosity': verbosity, } if django_version <= (1, 8): emit_kwargs['create_models'] = create_models elif django_version >= (1, 10): if pre_migrate_state: apps = pre_migrate_state.apps else: apps = None emit_kwargs.update({ 'apps': apps, 'plan': plan, }) if emit_pre_sync_signal: emit_pre_sync_signal(**emit_kwargs) else: emit_pre_migrate_signal(**emit_kwargs)
def _migrate_from_old_targets(self, tmp_path): 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): 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: 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)
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 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 handle(self, *args, **options): # self 是「命令处理对象」 print('【django.core.management.commands.migrate.Command.handle】args:', args) #print('【django.core.management.commands.migrate.Command.handle】options:' ) #for k, v in options.items(): # print(f'\t{k:<22}{v}') database = options['database'] # 默认值是 'default' if not options['skip_checks']: # 此方法定义在父类 django.core.management.base.BaseCommand 中 self.check(databases=[database]) 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) # connections 是 django.db.utils.ConnectionHandler 类的实例,叫做「数据库连接处理对象」 # 此处调用其 __getitem__ 方法,返回的是「数据库包装对象」 # 以 MySQL 为例,其所属类定义在 django.db.backends.mysql.base 模块中 # 所以下面的 connection 是 django.db.backends.mysql.base.DatabaseWrapper 类的实例 connection = connections[database] print( '【django.core.management.commands.migrate.Command.handle】connection:', connection) # 此方法定义在实例的父类中,是空函数 connection.prepare_database() # 下面这十几行代码用于处理数据版本控制相关的事情 # 此 MigrationExecutor 类定义在 django.db.migrations.executor 模块中,其实例是「数据库版本迁移执行器」 # 实例化时提供两个参数,它们分别会被赋值给实例的同名属性: # connection 是「数据库包装对象」,这是位置参数 # process_callback 是可选参数,这里提供的是定义在当前类中的方法 executor = MigrationExecutor(connection, self.migration_progress_callback) # executor 是「数据库版本迁移执行器」 # executor.loader 是 django.db.migrations.loader.MigrationLoader 类的实例 # 该实例的 connection 属性值就是 self 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 app '%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 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']: targets = [ key for key in executor.loader.graph.leaf_nodes() if key[0] == app_label ] else: # executor 是 django.db.migrations.executor.MigrationExecutor 类的实例 # 叫做「数据库版本迁移执行器」 # executor.loader 是 django.db.migrations.loader.MigrationLoader 类的实例 # 此实例的 graph 属性值是 django.db.migrations.graph.MigrationGraph 类的实例 # 调用其 leaf_nodes 获得各个应用中最新版本的迁移文件名 # targets 是列表,里面是二元元组,类似这样: # [('admin', '0003_logentry_add_action_flag_choices'), ...] targets = executor.loader.graph.leaf_nodes() #print('【django.core.management.commands.migrate.Command.handle】targets:') #for i in targets: # print('\t', i) # executor 是 django.db.migrations.executor.MigrationExecutor 类的实例 # 叫做「数据库版本迁移执行器」 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 app: %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 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.")) fake = False fake_initial = False else: fake = options['fake'] fake_initial = options['fake_initial'] # executor 是 django.db.migrations.executor.MigrationExecutor 类的实例,叫做「数据库版本迁移执行器」 # 参数: # targets 列表,里面是类似 ('auth', '0012_alter_user_first_name_max_length') 这样的元组 # plan 列表,里面是类似 (<Migration auth.0001_initial>, False) 这样的元组 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, *args, **options): """ Handle liquibase command parameters """ database = getattr(settings, 'LIQUIMIGRATE_DATABASE', options['database']) try: dbsettings = databases[database] except KeyError: raise CommandError("don't know such a connection: %s" % database) verbosity = int(options.get('verbosity')) # get driver driver_class = (options.get('driver') or dbsettings.get('ENGINE').split('.')[-1]) dbtag, driver, classpath = LIQUIBASE_DRIVERS.get( driver_class, (None, None, None)) classpath = options.get('classpath') or classpath if driver is None: raise CommandError( "unsupported db driver '%s'\n" "available drivers: %s" % (driver_class, ' '.join(LIQUIBASE_DRIVERS.keys()))) # command options changelog_file = (options.get('changelog_file') or _get_changelog_file(options['database'])) username = options.get('username') or dbsettings.get('USER') or '' password = options.get('password') or dbsettings.get('PASSWORD') or '' url = options.get('url') or _get_url_for_db(dbtag, dbsettings) command = options['command'] cmdargs = { 'jar': LIQUIBASE_JAR, 'changelog_file': changelog_file, 'username': username, 'password': password, 'command': command, 'driver': driver, 'classpath': classpath, 'url': url, 'args': ' '.join(args), } cmdline = "java -jar %(jar)s --changeLogFile %(changelog_file)s \ --username=%(username)s --password=%(password)s \ --driver=%(driver)s --classpath=%(classpath)s --url=%(url)s \ %(command)s %(args)s" % (cmdargs) if verbosity > 0: print("changelog file: %s" % (changelog_file, )) print("executing: %s" % (cmdline, )) created_models = None # we dont know it if emit_pre_migrate_signal and not options.get('no_signals'): if django_19_or_newer: emit_pre_migrate_signal(1, options.get('interactive'), database) else: emit_pre_migrate_signal(created_models, 1, options.get('interactive'), database) rc = os.system(cmdline) if rc == 0: try: if not options.get('no_signals'): if emit_post_migrate_signal: if django_19_or_newer: emit_post_migrate_signal( 0, options.get('interactive'), database) else: emit_post_migrate_signal( created_models, 0, options.get('interactive'), database) elif emit_post_sync_signal: emit_post_sync_signal(created_models, 0, options.get('interactive'), database) if not django_19_or_newer: call_command('loaddata', 'initial_data', verbosity=1, database=database) except TypeError: # singledb (1.1 and older) emit_post_sync_signal(created_models, 0, options.get('interactive')) call_command('loaddata', 'initial_data', verbosity=0) else: raise CommandError('Liquibase returned an error code %s' % rc)
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, )
def sync_apps(self, connection, app_labels): "Runs the old syncdb-style operation on a list of app_labels." cursor = connection.cursor() try: # Get a list of already installed *models* so that references work right. tables = connection.introspection.table_names(cursor) seen_models = connection.introspection.installed_models(tables) created_models = set() pending_references = {} # Build the manifest of apps and models that are to be synchronized all_models = [ (app_config.label, router.get_migratable_models(app_config, connection.alias, include_auto_created=True)) for app_config in apps.get_app_configs() if app_config.models_module is not None and app_config.label in app_labels ] def model_installed(model): opts = model._meta converter = connection.introspection.table_name_converter # Note that if a model is unmanaged we short-circuit and never try to install it return not ((converter(opts.db_table) in tables) or (opts.auto_created and converter( opts.auto_created._meta.db_table) in tables)) manifest = OrderedDict( (app_name, list(filter(model_installed, model_list))) for app_name, model_list in all_models) create_models = set(itertools.chain(*manifest.values())) emit_pre_migrate_signal(create_models, self.verbosity, self.interactive, connection.alias) # Create the tables for each model if self.verbosity >= 1: self.stdout.write(" Creating tables...\n") with transaction.atomic(using=connection.alias, savepoint=False): for app_name, model_list in manifest.items(): for model in model_list: # Create the model's database table, if it doesn't already exist. if self.verbosity >= 3: self.stdout.write( " Processing %s.%s model\n" % (app_name, model._meta.object_name)) sql, references = connection.creation.sql_create_model( model, no_style(), seen_models) seen_models.add(model) created_models.add(model) for refto, refs in references.items(): pending_references.setdefault(refto, []).extend(refs) if refto in seen_models: sql.extend( connection.creation. sql_for_pending_references( refto, no_style(), pending_references)) sql.extend( connection.creation.sql_for_pending_references( model, no_style(), pending_references)) if self.verbosity >= 1 and sql: self.stdout.write(" Creating table %s\n" % model._meta.db_table) for statement in sql: cursor.execute(statement) tables.append( connection.introspection.table_name_converter( model._meta.db_table)) # We force a commit here, as that was the previous behavior. # If you can prove we don't need this, remove it. transaction.set_dirty(using=connection.alias) finally: cursor.close() # The connection may have been closed by a syncdb handler. cursor = connection.cursor() try: # Install custom SQL for the app (but only if this # is a model we've just created) if self.verbosity >= 1: self.stdout.write(" Installing custom SQL...\n") for app_name, model_list in manifest.items(): for model in model_list: if model in created_models: custom_sql = custom_sql_for_model( model, no_style(), connection) if custom_sql: if self.verbosity >= 2: self.stdout.write( " Installing custom SQL for %s.%s model\n" % (app_name, model._meta.object_name)) try: with transaction.commit_on_success_unless_managed( using=connection.alias): for sql in custom_sql: cursor.execute(sql) except Exception as e: self.stderr.write( " Failed to install custom SQL for %s.%s model: %s\n" % (app_name, model._meta.object_name, e)) if self.show_traceback: traceback.print_exc() else: if self.verbosity >= 3: self.stdout.write( " No custom SQL for %s.%s model\n" % (app_name, model._meta.object_name)) if self.verbosity >= 1: self.stdout.write(" Installing indexes...\n") # Install SQL indices for all newly created models for app_name, model_list in manifest.items(): for model in model_list: if model in created_models: index_sql = connection.creation.sql_indexes_for_model( model, no_style()) if index_sql: if self.verbosity >= 2: self.stdout.write( " Installing index for %s.%s model\n" % (app_name, model._meta.object_name)) try: with transaction.commit_on_success_unless_managed( using=connection.alias): for sql in index_sql: cursor.execute(sql) except Exception as e: self.stderr.write( " Failed to install index for %s.%s model: %s\n" % (app_name, model._meta.object_name, e)) finally: cursor.close() # Load initial_data fixtures (unless that has been disabled) if self.load_initial_data: for app_label in app_labels: call_command('loaddata', 'initial_data', verbosity=self.verbosity, database=connection.alias, skip_validation=True, app_label=app_label, hide_empty=True) return created_models
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, *args, **options): self.verbosity = options['verbosity'] self.interactive = options['interactive'] # @@ TODO あとで。 # managementを読んで何をしているのか。 # 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) logger.debug('options {}'.format(options)) # @@ 普通は`default`が入っている db = options['database'] # @@ 対応するDBへのコネクションラッパを取得する # ConnectionHandler().__getitem__(db) # django.db.backends.sqlite3.base.DatabaseWrapper が戻る connection = connections[db] logger.debug('connection {}'.format(connection)) # @@ sqlite3では未実装なのでpass # 実際のところgisだけっぽい # postgisのbaseより。 # def prepare_database(self): # super().prepare_database() # # Check that postgis extension is installed. # with self.cursor() as cursor: # cursor.execute("CREATE EXTENSION IF NOT EXISTS postgis") connection.prepare_database() # Work out which apps have migrations and which do not # @@ executorの取得 # migration_progress_callbackは進捗をstdoutにいい感じにだすための処理 # DBから状態を取り出し、適用をするクラス executor = MigrationExecutor(connection, self.migration_progress_callback) # Raise an error if any migrations are applied before their dependencies. # @@ # executor.loaderはMigrationLoader # 適用済マイグレーションのツリーの一貫性チェック # 適用されているマイグレーションのparentがgraph.nodesにあるかを見ている # どこかで辿りきれなくなっているのはまずいので。どこかで流れが変わっている可能性がある executor.loader.check_consistent_history(connection) # Before anything else, see if there's conflicting apps and drop out # hard if there are any # @@ マイグレーションファイルのコンフリクトチェック # 同じAppで複数のリーフが存在していないかを見ている 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. # @@ app_labelやmigration_nameの対応 # 該当する部分だけをdisk_migrationから取り出す # 未指定なら全部の末端ノードを取り出す 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_nameは前方一致で単一になればなんでもいい 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() logger.debug('migration target {}'.format(targets)) # @@ # 実際に適用すべきマイグレーションを決定する plan = executor.migration_plan(targets) logger.debug('plan {}'.format(plan)) # @@ # https://docs.djangoproject.com/en/2.0/ref/django-admin/#django-admin-migrate # --run-syncdb¶ # Allows creating tables for apps without migrations. While this isn’t recommended, # the migrations framework is sometimes too slow on large projects with hundreds of models. # これのためにあるらしい run_syncdb = options['run_syncdb'] and executor.loader.unmigrated_apps logger.debug('executor.loader.unmigrated_apps {}'.format(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]) ) # @@ マイグレーション前のProjectStateを構成する # 恐らく適用済の範囲だけっぽい pre_migrate_state = executor._create_project_state(with_applied_migrations=True) pre_migrate_apps = pre_migrate_state.apps # @@ シグナルを投げる # 最終的にinject_rename_contenttypes_operationsが呼ばれる 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:")) 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. # @@ これは優しさ # もしmakemigrationsされてない変更があれば警告する 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." )) fake = False fake_initial = False else: fake = options['fake'] fake_initial = options['fake_initial'] # @@ migrateを実行する 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, *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 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)
# -*- coding: utf-8 -*-
def handle(self, *args, **options): """ Handle liquibase command parameters """ database = getattr(settings, 'LIQUIMIGRATE_DATABASE', options['database']) try: dbsettings = databases[database] except KeyError: raise CommandError("don't know such a connection: %s" % database) verbosity = int(options.get('verbosity')) # get driver driver_class = options.get('driver') or \ dbsettings.get('ENGINE').split('.')[-1] dbtag, driver, classpath = LIQUIBASE_DRIVERS.get(driver_class, (None, None, None)) classpath = options.get('classpath') or classpath if driver is None: raise CommandError("unsupported db driver '%s'\n\ available drivers: %s" % (driver_class, ' '.join(LIQUIBASE_DRIVERS.keys()))) # command options changelog_file = options.get('changelog_file') or \ _get_changelog_file(options['database']) username = options.get('username') or dbsettings.get('USER') or '' password = options.get('password') or dbsettings.get('PASSWORD') or '' url = options.get('url') or _get_url_for_db(dbtag, dbsettings) if len(args) < 1: raise CommandError("give me any command, for example 'update'") command = args[0] cmdargs = { 'jar': LIQUIBASE_JAR, 'changelog_file': changelog_file, 'username': username, 'password': password, 'command': command, 'driver': driver, 'classpath': classpath, 'url': url, 'args': ' '.join(args[1:]), } cmdline = "java -jar %(jar)s --changeLogFile %(changelog_file)s \ --username=%(username)s --password=%(password)s \ --driver=%(driver)s --classpath=%(classpath)s --url=%(url)s \ %(command)s %(args)s" % (cmdargs) if verbosity > 0: print "changelog file: %s" % (changelog_file,) print "executing: %s" % (cmdline,) created_models = None # we dont know it if emit_pre_migrate_signal and not options.get('no_signals'): emit_pre_migrate_signal(created_models, 0, options.get('interactive'), database) rc = os.system(cmdline) if rc == 0: try: if not options.get('no_signals'): if emit_post_migrate_signal: emit_post_migrate_signal(created_models, 0, options.get('interactive'), database) elif emit_post_sync_signal: emit_post_sync_signal(created_models, 0, options.get('interactive'), database) call_command('loaddata', 'initial_data', verbosity=0, database=database) except TypeError: # singledb (1.1 and older) emit_post_sync_signal(created_models, 0, options.get('interactive')) call_command('loaddata', 'initial_data', verbosity=0) else: raise CommandError('Liquibase returned an error code %s' % rc)
def sync_apps(self, connection, app_labels): "Runs the old syncdb-style operation on a list of app_labels." cursor = connection.cursor() try: # Get a list of already installed *models* so that references work right. tables = connection.introspection.table_names(cursor) created_models = set() # Build the manifest of apps and models that are to be synchronized all_models = [ (app_config.label, router.get_migratable_models(app_config, connection.alias, include_auto_created=False)) for app_config in apps.get_app_configs() if app_config.models_module is not None and app_config.label in app_labels ] def model_installed(model): opts = model._meta converter = connection.introspection.table_name_converter # Note that if a model is unmanaged we short-circuit and never try to install it return not ((converter(opts.db_table) in tables) or (opts.auto_created and converter( opts.auto_created._meta.db_table) in tables)) manifest = OrderedDict( (app_name, list(filter(model_installed, model_list))) for app_name, model_list in all_models) create_models = set(itertools.chain(*manifest.values())) emit_pre_migrate_signal(create_models, self.verbosity, self.interactive, connection.alias) # Create the tables for each model if self.verbosity >= 1: self.stdout.write(" Creating tables...\n") with transaction.atomic( using=connection.alias, savepoint=connection.features.can_rollback_ddl): deferred_sql = [] for app_name, model_list in manifest.items(): for model in model_list: if model._meta.proxy or not model._meta.managed: continue if self.verbosity >= 3: self.stdout.write( " Processing %s.%s model\n" % (app_name, model._meta.object_name)) with connection.schema_editor() as editor: if self.verbosity >= 1: self.stdout.write(" Creating table %s\n" % model._meta.db_table) editor.create_model(model) deferred_sql.extend(editor.deferred_sql) editor.deferred_sql = [] created_models.add(model) if self.verbosity >= 1: self.stdout.write(" Running deferred SQL...\n") for statement in deferred_sql: cursor.execute(statement) finally: cursor.close() # The connection may have been closed by a syncdb handler. cursor = connection.cursor() try: # Install custom SQL for the app (but only if this # is a model we've just created) if self.verbosity >= 1: self.stdout.write(" Installing custom SQL...\n") for app_name, model_list in manifest.items(): for model in model_list: if model in created_models: custom_sql = custom_sql_for_model( model, no_style(), connection) if custom_sql: if self.verbosity >= 2: self.stdout.write( " Installing custom SQL for %s.%s model\n" % (app_name, model._meta.object_name)) try: with transaction.atomic( using=connection.alias): for sql in custom_sql: cursor.execute(sql) except Exception as e: self.stderr.write( " Failed to install custom SQL for %s.%s model: %s\n" % (app_name, model._meta.object_name, e)) if self.show_traceback: traceback.print_exc() else: if self.verbosity >= 3: self.stdout.write( " No custom SQL for %s.%s model\n" % (app_name, model._meta.object_name)) finally: cursor.close() # Load initial_data fixtures (unless that has been disabled) if self.load_initial_data: for app_label in app_labels: call_command( 'loaddata', 'initial_data', verbosity=self.verbosity, database=connection.alias, app_label=app_label, hide_empty=True, ) return created_models
def handle(self, *args, **options): self.verbosity = verbosity = options.get('verbosity') self.interactive = interactive = options.get('interactive') migrations_dir = options.get('input_dir') try: default_input_dir = os.path.join( settings.BASE_DIR, DEFAULT_PENDING_MIGRATIONS_DIRECTORY) except AttributeError: default_input_dir = None if migrations_dir is None: if not default_input_dir: raise CommandError( "No input directory to read migrations from. Either set " "BASE_DIR in your settings or provide a directory path " "via the --input-dir option.") else: migrations_dir = default_input_dir elif not migrations_dir: raise CommandError( "Provide a real directory path via the --input-dir option.") if not (os.path.exists(migrations_dir) and os.listdir(migrations_dir)): raise CommandError("Input directory (%s) doesn't exist or is " "empty." % migrations_dir) # Get the database we're operating from db = options.get('database') connection = connections[db] # Hook for backends needing any database preparation try: connection.prepare_database() except AttributeError: # pragma: no cover pass executor = MigrationExecutor(connection, self.migration_progress_callback) # Replace the loader with a pending migration one executor.loader = PendingMigrationLoader( connection, pending_migrations_dir=migrations_dir) targets = executor.loader.graph.leaf_nodes() pending_migration_keys = executor.loader.pending_migrations.keys() if options.get('unapply'): targets = [] # We only want to unapply the collected migrations for key, migration in executor.loader.pending_migrations.items(): app_label, migration_name = key migration_found = False for dependency in migration.dependencies: pending = dependency in pending_migration_keys if dependency[0] == app_label and not pending: result = executor.loader.check_key(dependency, app_label) dependency = result or dependency targets.append(dependency) migration_found = True if not migration_found: targets.append((app_label, None)) else: # Trim non-collected migrations for migration_key in list(targets): if migration_key not in pending_migration_keys: targets.remove(migration_key) plan = executor.migration_plan(targets) MIGRATE_HEADING = self.style.MIGRATE_HEADING MIGRATE_LABEL = self.style.MIGRATE_LABEL # Print some useful info if verbosity > 0: self.stdout.write(MIGRATE_HEADING("Operations to perform:")) for target in targets: if target[1] is None: self.stdout.write(MIGRATE_LABEL( " Unapply all migrations: ") + "%s" % (target[0],) ) else: self.stdout.write(MIGRATE_LABEL( " Target specific migration: ") + "%s, from %s" % (target[1], target[0]) ) try: # pragma: no cover emit_pre_migrate_signal([], verbosity, interactive, connection.alias) except TypeError: # pragma: no cover emit_pre_migrate_signal(verbosity, interactive, connection.alias) # Migrate! if verbosity > 0: self.stdout.write(MIGRATE_HEADING("Running migrations:")) if not plan: if verbosity > 0: self.stdout.write(" No migrations to apply.") # If there's changes not in migrations, 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 makeprojectmigrations' to make new " "migrations, and then run 'manage.py migrateproject' " "to apply them." )) else: executor.migrate(targets, plan, fake=options.get("fake", False)) # A little database clean-up for app_label, migration_name in pending_migration_keys: executor.recorder.record_unapplied(app_label, migration_name) # Send the post_migrate signal, so individual apps can do whatever they # need to do at this point. try: # pragma: no cover emit_post_migrate_signal([], verbosity, interactive, connection.alias) except TypeError: # pragma: no cover emit_post_migrate_signal(verbosity, interactive, connection.alias)
def sync_apps(self, connection, app_labels): "Runs the old syncdb-style operation on a list of app_labels." cursor = connection.cursor() try: # Get a list of already installed *models* so that references work right. tables = connection.introspection.table_names(cursor) seen_models = connection.introspection.installed_models(tables) created_models = set() pending_references = {} # Build the manifest of apps and models that are to be synchronized all_models = [ (app_config.label, router.get_migratable_models(app_config, connection.alias, include_auto_created=True)) for app_config in apps.get_app_configs() if app_config.models_module is not None and app_config.label in app_labels ] def model_installed(model): opts = model._meta converter = connection.introspection.table_name_converter # Note that if a model is unmanaged we short-circuit and never try to install it return not ((converter(opts.db_table) in tables) or (opts.auto_created and converter(opts.auto_created._meta.db_table) in tables)) manifest = OrderedDict( (app_name, list(filter(model_installed, model_list))) for app_name, model_list in all_models ) create_models = set(itertools.chain(*manifest.values())) emit_pre_migrate_signal(create_models, self.verbosity, self.interactive, connection.alias) # Create the tables for each model if self.verbosity >= 1: self.stdout.write(" Creating tables...\n") with transaction.atomic(using=connection.alias, savepoint=connection.features.can_rollback_ddl): for app_name, model_list in manifest.items(): for model in model_list: # Create the model's database table, if it doesn't already exist. if self.verbosity >= 3: self.stdout.write( " Processing %s.%s model\n" % (app_name, model._meta.object_name) ) sql, references = connection.creation.sql_create_model(model, no_style(), seen_models) seen_models.add(model) created_models.add(model) for refto, refs in references.items(): pending_references.setdefault(refto, []).extend(refs) if refto in seen_models: sql.extend( connection.creation.sql_for_pending_references( refto, no_style(), pending_references, ) ) sql.extend( connection.creation.sql_for_pending_references( model, no_style(), pending_references ) ) if self.verbosity >= 1 and sql: self.stdout.write(" Creating table %s\n" % model._meta.db_table) for statement in sql: cursor.execute(statement) tables.append(connection.introspection.table_name_converter(model._meta.db_table)) finally: cursor.close() # The connection may have been closed by a syncdb handler. cursor = connection.cursor() try: # Install custom SQL for the app (but only if this # is a model we've just created) if self.verbosity >= 1: self.stdout.write(" Installing custom SQL...\n") for app_name, model_list in manifest.items(): for model in model_list: if model in created_models: custom_sql = custom_sql_for_model(model, no_style(), connection) if custom_sql: if self.verbosity >= 2: self.stdout.write( " Installing custom SQL for %s.%s model\n" % (app_name, model._meta.object_name) ) try: with transaction.atomic(using=connection.alias): for sql in custom_sql: cursor.execute(sql) except Exception as e: self.stderr.write( " Failed to install custom SQL for %s.%s model: %s\n" % (app_name, model._meta.object_name, e) ) if self.show_traceback: traceback.print_exc() else: if self.verbosity >= 3: self.stdout.write( " No custom SQL for %s.%s model\n" % (app_name, model._meta.object_name) ) if self.verbosity >= 1: self.stdout.write(" Installing indexes...\n") # Install SQL indices for all newly created models for app_name, model_list in manifest.items(): for model in model_list: if model in created_models: index_sql = connection.creation.sql_indexes_for_model(model, no_style()) if index_sql: if self.verbosity >= 2: self.stdout.write( " Installing index for %s.%s model\n" % (app_name, model._meta.object_name) ) savepoint = connection.features.can_rollback_ddl try: with transaction.atomic(using=connection.alias, savepoint=savepoint): for sql in index_sql: cursor.execute(sql) except Exception as e: self.stderr.write( " Failed to install index for %s.%s model: %s\n" % (app_name, model._meta.object_name, e) ) finally: cursor.close() # Load initial_data fixtures (unless that has been disabled) if self.load_initial_data: for app_label in app_labels: call_command( 'loaddata', 'initial_data', verbosity=self.verbosity, database=connection.alias, skip_validation=True, app_label=app_label, hide_empty=True, ) return created_models
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']: # Validate app_label. app_label = options['app_label'] try: apps.get_app_config(app_label) except LookupError as err: raise CommandError(str(err)) if 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 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']: 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) 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 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: 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: 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." )) 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, )