Generated: Wed 2013-03-13 10:33 CET
Source file: /media/Envs/Envs/filer-gallery/lib/python2.7/site-packages/south/migration/migrators.py
Stats: 0 executed, 216 missed, 15 excluded, 129 ignored
from copy import copy, deepcopyfrom cStringIO import StringIOimport datetimeimport inspectimport sysimport tracebackfrom django.core.management import call_commandfrom django.core.management.commands import loaddatafrom django.db import modelsimport south.dbfrom south import exceptionsfrom south.db import DEFAULT_DB_ALIASfrom south.models import MigrationHistoryfrom south.signals import ran_migrationclass Migrator(object): def __init__(self, verbosity=0, interactive=False): self.verbosity = int(verbosity) self.interactive = bool(interactive) @staticmethod def title(target): raise NotImplementedError() def print_title(self, target): if self.verbosity: print self.title(target) @staticmethod def status(target): raise NotImplementedError() def print_status(self, migration): status = self.status(migration) if self.verbosity and status: print status @staticmethod def orm(migration): raise NotImplementedError() def backwards(self, migration): return self._wrap_direction(migration.backwards(), migration.prev_orm()) def direction(self, migration): raise NotImplementedError() @staticmethod def _wrap_direction(direction, orm): args = inspect.getargspec(direction) if len(args[0]) == 1: # Old migration, no ORM should be passed in return direction return (lambda: direction(orm)) @staticmethod def record(migration, database): raise NotImplementedError() def run_migration_error(self, migration, extra_info=''): return ( ' ! Error found during real run of migration! Aborting.\n' '\n' ' ! Since you have a database that does not support running\n' ' ! schema-altering statements in transactions, we have had \n' ' ! to leave it in an interim state between migrations.\n' '%s\n' ' ! The South developers regret this has happened, and would\n' ' ! like to gently persuade you to consider a slightly\n' ' ! easier-to-deal-with DBMS (one that supports DDL transactions)\n' ' ! NOTE: The error which caused the migration to fail is further up.' ) % extra_info def run_migration(self, migration): migration_function = self.direction(migration) south.db.db.start_transaction() try: migration_function() south.db.db.execute_deferred_sql() except: south.db.db.rollback_transaction() if not south.db.db.has_ddl_transactions: print self.run_migration_error(migration) print "Error in migration: %s" % migration raise else: try: south.db.db.commit_transaction() except: print "Error during commit in migration: %s" % migration raise def run(self, migration): # Get the correct ORM. south.db.db.current_orm = self.orm(migration) # If we're not already in a dry run, and the database doesn't support # running DDL inside a transaction, *cough*MySQL*cough* then do a dry # run first. if not isinstance(getattr(self, '_wrapper', self), DryRunMigrator): if not south.db.db.has_ddl_transactions: dry_run = DryRunMigrator(migrator=self, ignore_fail=False) dry_run.run_migration(migration) return self.run_migration(migration) def done_migrate(self, migration, database): south.db.db.start_transaction() try: # Record us as having done this self.record(migration, database) except: south.db.db.rollback_transaction() raise else: south.db.db.commit_transaction() def send_ran_migration(self, migration): ran_migration.send(None, app=migration.app_label(), migration=migration, method=self.__class__.__name__.lower()) def migrate(self, migration, database): """ Runs the specified migration forwards/backwards, in order. """ app = migration.migrations._migrations migration_name = migration.name() self.print_status(migration) result = self.run(migration) self.done_migrate(migration, database) self.send_ran_migration(migration) return result def migrate_many(self, target, migrations, database): raise NotImplementedError()class MigratorWrapper(object): def __init__(self, migrator, *args, **kwargs): self._migrator = copy(migrator) attributes = dict([(k, getattr(self, k)) for k in self.__class__.__dict__.iterkeys() if not k.startswith('__')]) self._migrator.__dict__.update(attributes) self._migrator.__dict__['_wrapper'] = self def __getattr__(self, name): return getattr(self._migrator, name)class DryRunMigrator(MigratorWrapper): def __init__(self, ignore_fail=True, *args, **kwargs): super(DryRunMigrator, self).__init__(*args, **kwargs) self._ignore_fail = ignore_fail def _run_migration(self, migration): if migration.no_dry_run(): if self.verbosity: print " - Migration '%s' is marked for no-dry-run." % migration return south.db.db.dry_run = True # preserve the constraint cache as it can be mutated by the dry run constraint_cache = deepcopy(south.db.db._constraint_cache) if self._ignore_fail: south.db.db.debug, old_debug = False, south.db.db.debug pending_creates = south.db.db.get_pending_creates() south.db.db.start_transaction() migration_function = self.direction(migration) try: try: migration_function() south.db.db.execute_deferred_sql() except: raise exceptions.FailedDryRun(migration, sys.exc_info()) finally: south.db.db.rollback_transactions_dry_run() if self._ignore_fail: south.db.db.debug = old_debug south.db.db.clear_run_data(pending_creates) south.db.db.dry_run = False # restore the preserved constraint cache from before dry run was # executed south.db.db._constraint_cache = constraint_cache def run_migration(self, migration): try: self._run_migration(migration) except exceptions.FailedDryRun: if self._ignore_fail: return False raise def done_migrate(self, *args, **kwargs): pass def send_ran_migration(self, *args, **kwargs): passclass FakeMigrator(MigratorWrapper): def run(self, migration): if self.verbosity: print ' (faked)' def send_ran_migration(self, *args, **kwargs): passclass LoadInitialDataMigrator(MigratorWrapper): def load_initial_data(self, target, db='default'): if target is None or target != target.migrations[-1]: return # Load initial data, if we ended up at target if self.verbosity: print " - Loading initial data for %s." % target.app_label() # Override Django's get_apps call temporarily to only load from the # current app old_get_apps = models.get_apps new_get_apps = lambda: [models.get_app(target.app_label())] models.get_apps = new_get_apps loaddata.get_apps = new_get_apps try: call_command('loaddata', 'initial_data', verbosity=self.verbosity, database=db) finally: models.get_apps = old_get_apps loaddata.get_apps = old_get_apps def migrate_many(self, target, migrations, database): migrator = self._migrator result = migrator.__class__.migrate_many(migrator, target, migrations, database) if result: self.load_initial_data(target, db=database) return Trueclass Forwards(Migrator): """ Runs the specified migration forwards, in order. """ torun = 'forwards' @staticmethod def title(target): if target is not None: return " - Migrating forwards to %s." % target.name() else: assert False, "You cannot migrate forwards to zero." @staticmethod def status(migration): return ' > %s' % migration @staticmethod def orm(migration): return migration.orm() def forwards(self, migration): return self._wrap_direction(migration.forwards(), migration.orm()) direction = forwards @staticmethod def record(migration, database): # Record us as having done this record = MigrationHistory.for_migration(migration, database) try: from django.utils.timezone import now record.applied = now() except ImportError: record.applied = datetime.datetime.utcnow() if database != DEFAULT_DB_ALIAS: record.save(using=database) else: # Django 1.1 and below always go down this branch. record.save() def format_backwards(self, migration): if migration.no_dry_run(): return " (migration cannot be dry-run; cannot discover commands)" old_debug, old_dry_run = south.db.db.debug, south.db.db.dry_run south.db.db.debug = south.db.db.dry_run = True stdout = sys.stdout sys.stdout = StringIO() try: try: self.backwards(migration)() return sys.stdout.getvalue() except: raise finally: south.db.db.debug, south.db.db.dry_run = old_debug, old_dry_run sys.stdout = stdout def run_migration_error(self, migration, extra_info=''): extra_info = ('\n' '! You *might* be able to recover with:' '%s' '%s' % (self.format_backwards(migration), extra_info)) return super(Forwards, self).run_migration_error(migration, extra_info) def migrate_many(self, target, migrations, database): try: for migration in migrations: result = self.migrate(migration, database) if result is False: # The migrations errored, but nicely. return False finally: # Call any pending post_syncdb signals south.db.db.send_pending_create_signals(verbosity=self.verbosity, interactive=self.interactive) return Trueclass Backwards(Migrator): """ Runs the specified migration backwards, in order. """ torun = 'backwards' @staticmethod def title(target): if target is None: return " - Migrating backwards to zero state." else: return " - Migrating backwards to just after %s." % target.name() @staticmethod def status(migration): return ' < %s' % migration @staticmethod def orm(migration): return migration.prev_orm() direction = Migrator.backwards @staticmethod def record(migration, database): # Record us as having not done this record = MigrationHistory.for_migration(migration, database) if record.id is not None: if database != DEFAULT_DB_ALIAS: record.delete(using=database) else: # Django 1.1 always goes down here record.delete() def migrate_many(self, target, migrations, database): for migration in migrations: self.migrate(migration, database) return True