diff options
Diffstat (limited to 'lib/python2.7/site-packages/south/migration/__init__.py')
-rw-r--r-- | lib/python2.7/site-packages/south/migration/__init__.py | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/lib/python2.7/site-packages/south/migration/__init__.py b/lib/python2.7/site-packages/south/migration/__init__.py new file mode 100644 index 0000000..1d91ddf --- /dev/null +++ b/lib/python2.7/site-packages/south/migration/__init__.py @@ -0,0 +1,235 @@ +""" +Main migration logic. +""" + +from __future__ import print_function + +import sys + +from django.core.exceptions import ImproperlyConfigured + +import south.db +from south import exceptions +from south.models import MigrationHistory +from south.db import db, DEFAULT_DB_ALIAS +from south.migration.migrators import (Backwards, Forwards, + DryRunMigrator, FakeMigrator, + LoadInitialDataMigrator) +from south.migration.base import Migration, Migrations +from south.migration.utils import SortedSet +from south.migration.base import all_migrations +from south.signals import pre_migrate, post_migrate + + +def to_apply(forwards, done): + return [m for m in forwards if m not in done] + +def to_unapply(backwards, done): + return [m for m in backwards if m in done] + +def problems(pending, done): + last = None + if not pending: + raise StopIteration() + for migration in pending: + if migration in done: + last = migration + continue + if last and migration not in done: + yield last, migration + +def forwards_problems(pending, done, verbosity): + """ + Takes the list of linearised pending migrations, and the set of done ones, + and returns the list of problems, if any. + """ + return inner_problem_check(problems(reversed(pending), done), done, verbosity) + +def backwards_problems(pending, done, verbosity): + return inner_problem_check(problems(pending, done), done, verbosity) + +def inner_problem_check(problems, done, verbosity): + "Takes a set of possible problems and gets the actual issues out of it." + result = [] + for last, migration in problems: + checked = set([]) + # 'Last' is the last applied migration. Step back from it until we + # either find nothing wrong, or we find something. + to_check = list(last.dependencies) + while to_check: + checking = to_check.pop() + if checking in checked: + continue + checked.add(checking) + + if checking not in done: + # That's bad. Error. + if verbosity: + print((" ! Migration %s should not have been applied " + "before %s but was." % (last, checking))) + result.append((last, checking)) + else: + to_check.extend(checking.dependencies) + return result + +def check_migration_histories(histories, delete_ghosts=False, ignore_ghosts=False): + "Checks that there's no 'ghost' migrations in the database." + exists = SortedSet() + ghosts = [] + for h in histories: + try: + m = h.get_migration() + m.migration() + except exceptions.UnknownMigration: + ghosts.append(h) + except ImproperlyConfigured: + pass # Ignore missing applications + else: + exists.add(m) + if ghosts: + # They may want us to delete ghosts. + if delete_ghosts: + for h in ghosts: + h.delete() + elif not ignore_ghosts: + raise exceptions.GhostMigrations(ghosts) + return exists + +def get_dependencies(target, migrations): + forwards = list + backwards = list + if target is None: + backwards = migrations[0].backwards_plan + else: + forwards = target.forwards_plan + # When migrating backwards we want to remove up to and + # including the next migration up in this app (not the next + # one, that includes other apps) + migration_before_here = target.next() + if migration_before_here: + backwards = migration_before_here.backwards_plan + return forwards, backwards + +def get_direction(target, applied, migrations, verbosity, interactive): + # Get the forwards and reverse dependencies for this target + forwards, backwards = get_dependencies(target, migrations) + # Is the whole forward branch applied? + problems = None + forwards = forwards() + workplan = to_apply(forwards, applied) + if not workplan: + # If they're all applied, we only know it's not backwards + direction = None + else: + # If the remaining migrations are strictly a right segment of + # the forwards trace, we just need to go forwards to our + # target (and check for badness) + problems = forwards_problems(forwards, applied, verbosity) + direction = Forwards(verbosity=verbosity, interactive=interactive) + if not problems: + # What about the whole backward trace then? + backwards = backwards() + missing_backwards = to_apply(backwards, applied) + if missing_backwards != backwards: + # If what's missing is a strict left segment of backwards (i.e. + # all the higher migrations) then we need to go backwards + workplan = to_unapply(backwards, applied) + problems = backwards_problems(backwards, applied, verbosity) + direction = Backwards(verbosity=verbosity, interactive=interactive) + return direction, problems, workplan + +def get_migrator(direction, db_dry_run, fake, load_initial_data): + if not direction: + return direction + if db_dry_run: + direction = DryRunMigrator(migrator=direction, ignore_fail=False) + elif fake: + direction = FakeMigrator(migrator=direction) + elif load_initial_data: + direction = LoadInitialDataMigrator(migrator=direction) + return direction + +def get_unapplied_migrations(migrations, applied_migrations): + applied_migration_names = ['%s.%s' % (mi.app_name,mi.migration) for mi in applied_migrations] + + for migration in migrations: + is_applied = '%s.%s' % (migration.app_label(), migration.name()) in applied_migration_names + if not is_applied: + yield migration + +def migrate_app(migrations, target_name=None, merge=False, fake=False, db_dry_run=False, yes=False, verbosity=0, load_initial_data=False, skip=False, database=DEFAULT_DB_ALIAS, delete_ghosts=False, ignore_ghosts=False, interactive=False): + app_label = migrations.app_label() + + verbosity = int(verbosity) + # Fire off the pre-migrate signal + pre_migrate.send(None, app=app_label, verbosity=verbosity, interactive=verbosity, db=database) + + # If there aren't any, quit quizically + if not migrations: + print("? You have no migrations for the '%s' app. You might want some." % app_label) + return + + # Load the entire dependency graph + Migrations.calculate_dependencies() + + # Check there's no strange ones in the database + applied_all = MigrationHistory.objects.filter(applied__isnull=False).order_by('applied').using(database) + applied = applied_all.filter(app_name=app_label).using(database) + south.db.db = south.db.dbs[database] + Migrations.invalidate_all_modules() + + south.db.db.debug = (verbosity > 1) + + if target_name == 'current-1': + if applied.count() > 1: + previous_migration = applied[applied.count() - 2] + if verbosity: + print('previous_migration: %s (applied: %s)' % (previous_migration.migration, previous_migration.applied)) + target_name = previous_migration.migration + else: + if verbosity: + print('previous_migration: zero') + target_name = 'zero' + elif target_name == 'current+1': + try: + first_unapplied_migration = get_unapplied_migrations(migrations, applied).next() + target_name = first_unapplied_migration.name() + except StopIteration: + target_name = None + + applied_all = check_migration_histories(applied_all, delete_ghosts, ignore_ghosts) + + # Guess the target_name + target = migrations.guess_migration(target_name) + if verbosity: + if target_name not in ('zero', None) and target.name() != target_name: + print(" - Soft matched migration %s to %s." % (target_name, + target.name())) + print("Running migrations for %s:" % app_label) + + # Get the forwards and reverse dependencies for this target + direction, problems, workplan = get_direction(target, applied_all, migrations, + verbosity, interactive) + if problems and not (merge or skip): + raise exceptions.InconsistentMigrationHistory(problems) + + # Perform the migration + migrator = get_migrator(direction, db_dry_run, fake, load_initial_data) + if migrator: + migrator.print_title(target) + success = migrator.migrate_many(target, workplan, database) + # Finally, fire off the post-migrate signal + if success: + post_migrate.send(None, app=app_label, verbosity=verbosity, interactive=verbosity, db=database) + else: + if verbosity: + # Say there's nothing. + print('- Nothing to migrate.') + # If we have initial data enabled, and we're at the most recent + # migration, do initial data. + # Note: We use a fake Forwards() migrator here. It's never used really. + if load_initial_data: + migrator = LoadInitialDataMigrator(migrator=Forwards(verbosity=verbosity)) + migrator.load_initial_data(target, db=database) + # Send signal. + post_migrate.send(None, app=app_label, verbosity=verbosity, interactive=verbosity, db=database) |