def get_migration_abspath(app_label, migration_name): from django.db.migrations.loader import MigrationLoader if django.VERSION >= (1, 11): module_name, _ = MigrationLoader.migrations_module(app_label) else: module_name = MigrationLoader.migrations_module(app_label) migration_path = "{}.{}".format(module_name, migration_name) migration_module = import_module(migration_path) migration_file = migration_module.__file__ if migration_file.endswith(".pyc"): migration_file = migration_file[:-1] return migration_file
def path(self): migrations_package_name = MigrationLoader.migrations_module(self.migration.app_label) # See if we can import the migrations module directly try: migrations_module = import_module(migrations_package_name) # Python 3 fails when the migrations directory does not have a # __init__.py file if not hasattr(migrations_module, '__file__'): raise ImportError basedir = os.path.dirname(upath(migrations_module.__file__)) except ImportError: app_config = apps.get_app_config(self.migration.app_label) migrations_package_basename = migrations_package_name.split(".")[-1] # Alright, see if it's a direct submodule of the app if '%s.%s' % (app_config.name, migrations_package_basename) == migrations_package_name: basedir = os.path.join(app_config.path, migrations_package_basename) else: # In case of using MIGRATION_MODULES setting and the custom # package doesn't exist, create one. package_dirs = migrations_package_name.split(".") create_path = os.path.join(upath(sys.path[0]), *package_dirs) if not os.path.isdir(create_path): os.makedirs(create_path) for i in range(1, len(package_dirs) + 1): init_dir = os.path.join(upath(sys.path[0]), *package_dirs[:i]) init_path = os.path.join(init_dir, "__init__.py") if not os.path.isfile(init_path): open(init_path, "w").close() return os.path.join(create_path, self.filename) return os.path.join(basedir, self.filename)
def basedir(self): migrations_package_name = MigrationLoader.migrations_module( self.migration.app_label) if migrations_package_name is None: raise ValueError( "Django can't create migrations for app '%s' because " "migrations have been disabled via the MIGRATION_MODULES " "setting." % self.migration.app_label) # See if we can import the migrations module directly try: migrations_module = import_module(migrations_package_name) except ImportError: pass else: try: return upath(module_dir(migrations_module)) except ValueError: pass # Alright, see if it's a direct submodule of the app app_config = apps.get_app_config(self.migration.app_label) maybe_app_name, _, migrations_package_basename = migrations_package_name.rpartition( ".") if app_config.name == maybe_app_name: return os.path.join(app_config.path, migrations_package_basename) # In case of using MIGRATION_MODULES setting and the custom package # doesn't exist, create one, starting from an existing package existing_dirs, missing_dirs = migrations_package_name.split("."), [] while existing_dirs: missing_dirs.insert(0, existing_dirs.pop(-1)) try: base_module = import_module(".".join(existing_dirs)) except ImportError: continue else: try: base_dir = upath(module_dir(base_module)) except ValueError: continue else: break else: raise ValueError( "Could not locate an appropriate location to create " "migrations package %s. Make sure the toplevel " "package exists and can be imported." % migrations_package_name) final_dir = os.path.join(base_dir, *missing_dirs) if not os.path.isdir(final_dir): os.makedirs(final_dir) for missing_dir in missing_dirs: base_dir = os.path.join(base_dir, missing_dir) with open(os.path.join(base_dir, "__init__.py"), "w"): pass return final_dir
def get_installed_app_labels_with_migrations(): """ Get the app labels, because settings.INSTALLED_APPS doesn't necessarily give us the labels. Remove django.contrib.contenttypes because we want it to run before us. Return list of tuples like ('admin', '__first__') """ from django.apps import apps apps_with_migrations = [] for app in apps.get_app_configs(): if app.label == 'contenttypes': continue # Ignore the contenttypes app migrations_module = MigrationLoader.migrations_module(app.label) try: # Django 1.11 changed the return value of the migrations_module call to a 2-element # tuple. The actual module is the first entry if isinstance(migrations_module, tuple): migrations_module = migrations_module[0] module = import_module(migrations_module) except ImportError: continue if not hasattr(module, "__path__"): continue # Make sure there are python files in the migration folder (other than the init file) has_files = any(x for x in os.listdir(module.__path__[0]) if x.endswith(".py") and x != "__init__.py") if not has_files: continue apps_with_migrations.append(app.label) return [(x, '__first__') for x in apps_with_migrations]
def get_installed_app_labels_with_migrations(): """ Get the app labels, because settings.INSTALLED_APPS doesn't necessarily give us the labels. Remove django.contrib.contenttypes because we want it to run before us. Return list of tuples like ('admin', '__first__') """ from django.apps import apps apps_with_migrations = [] for app in apps.get_app_configs(): if app.label == 'contenttypes': continue # Ignore the contenttypes app migrations_module = MigrationLoader.migrations_module(app.label) try: # Django 1.11 changed the return value of the migrations_module call to a 2-element # tuple. The actual module is the first entry if isinstance(migrations_module, tuple): migrations_module = migrations_module[0] module = import_module(migrations_module) except ImportError: continue if not hasattr(module, "__path__"): continue # Make sure there are python files in the migration folder (other than the init file) has_files = any( x for x in os.listdir(module.__path__[0]) if x.endswith(".py") and x != "__init__.py" ) if not has_files: continue apps_with_migrations.append(app.label) return [(x, '__first__') for x in apps_with_migrations]
def get_installed_app_labels_with_migrations(): """ Get the app labels, because settings.INSTALLED_APPS doesn't necessarily give us the labels. Remove django.contrib.contenttypes because we want it to run before us. Return list of tuples like ('admin', '__first__') """ from django.apps import apps apps_with_migrations = [] for app in apps.get_app_configs(): if app.label == 'contenttypes': continue # Ignore the contenttypes app migrations_module = MigrationLoader.migrations_module(app.label) try: module = import_module(migrations_module) except ImportError: continue if not hasattr(module, "__path__"): continue # Make sure there are python files in the migration folder has_files = bool(x for x in os.listdir(module.__path__[0]) if x.endswith(".py")) if not has_files: continue apps_with_migrations.append(app.label) return [(x, '__first__') for x in apps_with_migrations]
def _get_migrations(included_apps): """ Get migrations for included apps. """ migration_objects = [] for app_config in apps.get_app_configs(): if app_config.name not in included_apps: continue app_label = app_config.label module_name, _ = MigrationLoader.migrations_module(app_label) if module_name is None: continue try: module = import_module(module_name) except ImportError: continue directory = os.path.dirname(module.__file__) for name in os.listdir(directory): if name.endswith(".py"): import_name = name.rsplit(".", 1)[0] if import_name[0] not in "_.~": migration_objects.append( MigrationRecorder.Migration(app=app_label, name=import_name)) return migration_objects
def basedir(self): migrations_package_name = MigrationLoader.migrations_module(self.migration.app_label) if migrations_package_name is None: raise ValueError( "Django can't create migrations for app '%s' because " "migrations have been disabled via the MIGRATION_MODULES " "setting." % self.migration.app_label ) # See if we can import the migrations module directly try: migrations_module = import_module(migrations_package_name) except ImportError: pass else: try: return upath(module_dir(migrations_module)) except ValueError: pass # Alright, see if it's a direct submodule of the app app_config = apps.get_app_config(self.migration.app_label) maybe_app_name, _, migrations_package_basename = migrations_package_name.rpartition(".") if app_config.name == maybe_app_name: return os.path.join(app_config.path, migrations_package_basename) # In case of using MIGRATION_MODULES setting and the custom package # doesn't exist, create one, starting from an existing package existing_dirs, missing_dirs = migrations_package_name.split("."), [] while existing_dirs: missing_dirs.insert(0, existing_dirs.pop(-1)) try: base_module = import_module(".".join(existing_dirs)) except ImportError: continue else: try: base_dir = upath(module_dir(base_module)) except ValueError: continue else: break else: raise ValueError( "Could not locate an appropriate location to create " "migrations package %s. Make sure the toplevel " "package exists and can be imported." % migrations_package_name) final_dir = os.path.join(base_dir, *missing_dirs) if not os.path.isdir(final_dir): os.makedirs(final_dir) for missing_dir in missing_dirs: base_dir = os.path.join(base_dir, missing_dir) with open(os.path.join(base_dir, "__init__.py"), "w"): pass return final_dir
def __init__(self, app_label, do_reload=False): self.app_label = app_label # Some logic duplicated from MigrationLoader.load_disk, but avoiding # loading all migrations since that's relatively slow. self.migrations_module_name, _explicit = MigrationLoader.migrations_module( app_label) try: self.migrations_module = import_module(self.migrations_module_name) except ModuleNotFoundError: # Unmigrated app self.migrations_module = None else: if do_reload: reload(self.migrations_module)
def path(self): migrations_package_name = MigrationLoader.migrations_module(self.migration.app_label) # See if we can import the migrations module directly try: migrations_module = import_module(migrations_package_name) basedir = os.path.dirname(migrations_module.__file__) except ImportError: app_config = apps.get_app_config(self.migration.app_label) migrations_package_basename = migrations_package_name.split(".")[-1] # Alright, see if it's a direct submodule of the app if '%s.%s' % (app_config.name, migrations_package_basename) == migrations_package_name: basedir = os.path.join(app_config.path, migrations_package_basename) else: raise ImportError("Cannot open migrations module %s for app %s" % (migrations_package_name, self.migration.app_label)) return os.path.join(basedir, self.filename)
def path(self): migrations_module_name = MigrationLoader.migrations_module(self.migration.app_label) app_module = cache.get_app(self.migration.app_label) # See if we can import the migrations module directly try: migrations_module = import_module(migrations_module_name) basedir = os.path.dirname(migrations_module.__file__) except ImportError: # Alright, see if it's a direct submodule of the app oneup = ".".join(migrations_module_name.split(".")[:-1]) app_oneup = ".".join(app_module.__name__.split(".")[:-1]) if oneup == app_oneup: basedir = os.path.join(os.path.dirname(app_module.__file__), migrations_module_name.split(".")[-1]) else: raise ImportError("Cannot open migrations module %s for app %s" % (migrations_module_name, self.migration.app_label)) return os.path.join(basedir, self.filename)
def check_migrations(app_configs, **kwargs): if app_configs is None: app_configs = apps.get_app_configs() errors = [] hint = ( "Assign an explicit stage to it, break its operation into multiple " "migrations if it's not already applied or define an explicit stage for " "it using `MIGRATION_STAGE_OVERRIDE` or `MIGRATION_STAGE_FALLBACK` if the " "migration is not under your control.") for app_config in app_configs: # Most of the following code is taken from MigrationLoader.load_disk # while allowing non-global app_configs to be used. module_name, _explicit = MigrationLoader.migrations_module( app_config.label) if module_name is None: # pragma: no cover continue try: module = import_module(module_name) except ImportError: # This is not the place to deal with migration issues. continue directory = os.path.dirname(module.__file__) migration_names = set() for name in os.listdir(directory): if name.endswith(".py"): import_name = name.rsplit(".", 1)[0] migration_names.add(import_name) for migration_name in migration_names: try: migration_class = import_string( f"{module_name}.{migration_name}.Migration") except ImportError: # This is not the place to deal with migration issues. continue migration = migration_class(migration_name, app_config.label) try: must_post_deploy_migration(migration) except ValueError as e: errors.append( Error( str(e), hint=hint, obj=(migration.app_label, migration.name), id="migrations.0001", )) return errors
def __init__(self, name, app_label): # by changing app_label here to 'wagtailcore' we trick Django migrations system # to think that this migration belongs to wagtailcore app # this is necessary to make model name resolution work app_label = 'wagtailcore' super(Migration, self).__init__(name, app_label) # find last wagtailcore migration mod_name = MigrationLoader.migrations_module(app_label) if DJANGO_VERSION >= (1, 11): # Django 1.11 returns tuple(str, bool) while older versions return str mod_name = mod_name[0] mod = import_module(mod_name) migrations = [] # this loop acts the same way as MigrationLoader. for name in os.listdir(os.path.dirname(mod.__file__)): if not name.endswith('.py'): continue import_name = name.rsplit('.', 1)[0] if import_name[0] in '_.~': continue migrations.append(import_name) last_migration = sorted(migrations, reverse=True)[0] # By using `replaces` we make sure that this migration doesn't have ambiguous `app_label`. # When this migration is applied Django writes only replaced migration # to django_migrations table in DB. Otherwise migration would have # 'wagtailtranslation' as app_label in django_migrations table and # `migrate` command would consider this migration as unapplied due # to app_label mismatch. self.replaces = [ (app_label, last_migration), ] # import operations from wagtail migration we are replacing # and prepend them to operations of this migration mod_path = '{}.{}'.format(mod_name, last_migration) orig_migration = import_module(mod_path).Migration self.operations[:0] = orig_migration.operations self.dependencies = orig_migration.dependencies
class Command(MakeMigrationsCommand): option_list = BaseCommand.option_list def handle(self, *app_labels, **options): self.verbosity = int(options.get('verbosity')) self.interactive = options.get('interactive') self.dry_run = False self.loader = MigrationLoader(None, ignore_no_migrations=True) for app_label in app_labels: self.handle_app(app_label) def handle_app(self, app_label): app = apps.get_app_config(app_label) self.convert(app) def convert(self, app): print('Processing %s' % app.label) if not self.has_south_migrations(app) and not self.has_initial_data_outside_of_migrations(app): print('App %s is already migrated to new style migrations' % app.label) return True if self.has_south_migrations(app): self.remove_migrations(app) self.create_new_migrations(app) if self.has_initial_data_outside_of_migrations(app): print('Found initial_data outside of migrations') self.create_data_migration_from_initial_data(app) def has_south_migrations(self, app): """ Apps with South migrations are in both sets""" return (app.label in self.loader.unmigrated_apps and app.label in self.loader.migrated_apps) def has_initial_data_outside_of_migrations(self, app): # Are there any initial_data fixtures if not self.get_initial_data_fixtures(app): return False # Check if initial data is already inside migration leaf_nodes = self.loader.graph.leaf_nodes(app.label) if not leaf_nodes: return True _, migration_name = leaf_nodes[0] migration_string = open(os.path.join(self.get_migrations_dir(app), migration_name + '.py')).read() if "call_command('loaddata'" in migration_string: return False return True def get_migrations_dir(self, app): module_name = self.loader.migrations_module(app.label) module = import_module(module_name) return os.path.dirname(module.__file__) def get_initial_data_fixtures(self, app): fixture_dir = os.path.join(app.path, 'fixtures') return list(glob.iglob(os.path.join(fixture_dir, 'initial_data.*'))) def remove_migrations(self, app): print(' Removing old South migrations') directory = self.get_migrations_dir(app) for name in os.listdir(directory): if (name.endswith('.py') or name.endswith('.pyc'))and name != '__init__.py': print(' Deleting %s %s' % (name, '(fake)' if self.dry_run else '')) if not self.dry_run: os.remove(os.path.join(directory, name)) def create_new_migrations(self, app): call_command('makemigrations', app.label, dry_run=self.dry_run, verbosity=self.verbosity) def create_data_migration_from_initial_data(self, app): # Create empty migration call_command('makemigrations', app.label, empty=True, dry_run=self.dry_run, verbosity=self.verbosity) # Get latest migration self.loader.build_graph() _, migration_name = self.loader.graph.leaf_nodes(app.label)[0] # Find the file directory = self.get_migrations_dir(app) empty_migration_file = os.path.join(directory, migration_name + '.py') # Inject code migration_string = open(empty_migration_file).read() callable_code = RUN_CODE % ( ', '.join(map(lambda fixture_name: '"%s"' % os.path.basename(fixture_name), self.get_initial_data_fixtures(app))), app.label ) migration_string = (migration_string.replace('class Migration', callable_code + 'class Migration') .replace('operations = [', 'operations = [' + OPERATIONS)) with open(empty_migration_file, "wb") as fh: fh.write(migration_string.encode('utf-8')) # wipe *.pyc try: os.remove(os.path.join(directory, os.path.join(directory, migration_name + '.pyc'))) except OSError: pass
from __future__ import unicode_literals