def create_orm(): from django.conf import settings from south.orm import FakeORM get_migration_number_re = re.compile(r'^((\d+)_.*)\.py$') migrations_folder = os.path.join(settings.SITE_SRC_ROOT, 'forum/migrations') highest_number = 0 highest_file = None for f in os.listdir(migrations_folder): if os.path.isfile(os.path.join(migrations_folder, f)): m = get_migration_number_re.match(f) if m: found = int(m.group(2)) if found > highest_number: highest_number = found highest_file = m.group(1) mod = __import__('forum.migrations.%s' % highest_file, globals(), locals(), ['forum.migrations']) return FakeORM(getattr(mod, 'Migration'), "forum")
def test_not_deleted_auto(self): empty_defs = {} old_defs = freezer.freeze_apps(["non_managed"]) class InitialMigration(SchemaMigration): "Serves as fake previous migration" def forwards(self, orm): pass def backwards(self, orm): pass models = self.full_defs complete_apps = ['non_managed'] migrations = Migrations("non_managed") initial_orm = FakeORM(InitialMigration, "non_managed") changes = AutoChanges( migrations=migrations, old_defs=self.full_defs, old_orm=initial_orm, new_defs=empty_defs, ) change_list = changes.get_changes() if list(change_list): self.fail("Auto migration deletes table for non-managed model")
def test_not_added_auto(self): empty_defs = {} class EmptyMigration(SchemaMigration): "Serves as fake previous migration" def forwards(self, orm): pass def backwards(self, orm): pass models = empty_defs complete_apps = ['non_managed'] migrations = Migrations("non_managed") empty_orm = FakeORM(EmptyMigration, "non_managed") changes = AutoChanges( migrations=migrations, old_defs=empty_defs, old_orm=empty_orm, new_defs=self.full_defs, ) change_list = changes.get_changes() if list(change_list): self.fail("Auto migration creates table for non-managed model")
def prev_orm(self): if getattr(self.migration_class(), 'symmetrical', False): return self.orm() previous = self.previous() if previous is None: # First migration? The 'previous ORM' is empty. return FakeORM(None, self.app_label()) return previous.orm()
def get_indexes(self): # TODO: не удаляются индексы у внешних ключей и добавочные # _like-индексы к ним. Например у House migration = Migrations('fias', force_creation=True) # получим текущее состояние базы new_defs = dict( (k, v) for k, v in freezer.freeze_apps([migration.app_label()]).items() if k.split(".")[0] == migration.app_label()) # скопируем и удалим все индексы old_defs = copy.deepcopy(new_defs) for table in old_defs.values(): for key, value in table.items(): # удалим 'index_together' if key == 'Meta' and 'index_together' in value: del value['index_together'] if isinstance(value, tuple): # удалим 'unique' if 'unique' in value[2]: value[2]['unique'] = False # удалим 'db_index' if 'db_index' in value[2]: value[2]['db_index'] = False class InitialMigration(SchemaMigration): def forwards(self, orm): pass def backwards(self, orm): pass models = old_defs complete_apps = ['fias'] initial_orm = FakeORM(InitialMigration, "fias") # получим все изменения, т.е. список индексов change_source = changes.AutoChanges( migrations=migration, old_defs=old_defs, old_orm=initial_orm, new_defs=new_defs, ) for action_name, params in change_source.get_changes(): try: action_class = getattr(actions, action_name) except AttributeError: raise ValueError("Invalid action name from source: %s" % action_name) else: if issubclass(action_class, AddUnique): yield action_class, params
def test_not_modified_auto(self): fake_defs = { 'non_managed.legacy': { 'Meta': { 'object_name': 'Legacy', 'db_table': "'legacy_table'", 'managed': 'False' }, 'id': ('django.db.models.fields.AutoField', [], { 'primary_key': 'True' }), 'name': ('django.db.models.fields.CharField', [], { 'max_length': '10', 'null': 'True' }), #'size': ('django.db.models.fields.IntegerField', [], {}) # The "change" is the addition of this field } } class InitialMigration(SchemaMigration): "Serves as fake previous migration" def forwards(self, orm): pass def backwards(self, orm): pass models = fake_defs complete_apps = ['non_managed'] from non_managed import models as dummy_import_to_force_loading_models # TODO: Does needing this indicate a bug in MokeyPatcher? reload_module(dummy_import_to_force_loading_models) # really force... migrations = Migrations("non_managed") initial_orm = FakeORM(InitialMigration, "non_managed") changes = AutoChanges(migrations=migrations, old_defs=fake_defs, old_orm=initial_orm, new_defs=self.full_defs) change_list = changes.get_changes() if list(change_list): self.fail("Auto migration changes table for non-managed model")
def get_migration(app, name): """ Returns the migration class implied by 'name'. """ try: module = __import__(app.__name__ + "." + name, '', '', ['Migration']) migclass = module.Migration migclass.orm = FakeORM(migclass, get_app_name(app)) module._ = lambda x: x # Fake i18n return migclass except ImportError: print " ! Migration %s:%s probably doesn't exist." % ( get_app_name(app), name) print " - Traceback:" raise except Exception, e: print "While loading migration '%s.%s':" % (get_app_name(app), name) raise
def run_migrations(toprint, torun, recorder, app, migrations, fake=False, db_dry_run=False, verbosity=0): """ Runs the specified migrations forwards/backwards, in order. """ for migration in migrations: app_name = get_app_name(app) if verbosity: print toprint % (app_name, migration) # Get migration class klass = get_migration(app, migration) # Find its predecessor, and attach the ORM from that as prev_orm. all_names = get_migration_names(app) idx = all_names.index(migration) # First migration? The 'previous ORM' is empty. if idx == 0: klass.prev_orm = FakeORM(None, app) else: klass.prev_orm = get_migration(app, all_names[idx-1]).orm # If this is a 'fake' migration, do nothing. if fake: if verbosity: print " (faked)" # OK, we should probably do something then. else: runfunc = getattr(klass(), torun) args = inspect.getargspec(runfunc) # Get the correct ORM. if torun == "forwards": orm = klass.orm else: orm = klass.prev_orm db.current_orm = orm # If the database doesn't support running DDL inside a transaction # *cough*MySQL*cough* then do a dry run first. if not db.has_ddl_transactions or db_dry_run: if not (hasattr(klass, "no_dry_run") and klass.no_dry_run): db.dry_run = True # Only hide SQL if this is an automatic dry run. if not db.has_ddl_transactions: db.debug, old_debug = False, db.debug pending_creates = db.get_pending_creates() db.start_transaction() try: if len(args[0]) == 1: # They don't want an ORM param runfunc() else: runfunc(orm) db.rollback_transactions_dry_run() except: traceback.print_exc() print " ! Error found during dry run of migration! Aborting." return False if not db.has_ddl_transactions: db.debug = old_debug db.clear_run_data(pending_creates) db.dry_run = False elif db_dry_run: print " - Migration '%s' is marked for no-dry-run." % migration # If they really wanted to dry-run, then quit! if db_dry_run: return if db.has_ddl_transactions: db.start_transaction() try: if len(args[0]) == 1: # They don't want an ORM param runfunc() else: runfunc(orm) db.execute_deferred_sql() except: if db.has_ddl_transactions: db.rollback_transaction() raise else: traceback.print_exc() print " ! Error found during real run of migration! Aborting." print print " ! Since you have a database that does not support running" print " ! schema-altering statements in transactions, we have had to" print " ! leave it in an interim state between migrations." if torun == "forwards": print print " ! You *might* be able to recover with:" db.debug = db.dry_run = True if len(args[0]) == 1: klass().backwards() else: klass().backwards(klass.prev_orm) print print " ! The South developers regret this has happened, and would" print " ! like to gently persuade you to consider a slightly" print " ! easier-to-deal-with DBMS." return False else: if db.has_ddl_transactions: db.commit_transaction() if not db_dry_run: # Record us as having done this recorder(app_name, migration) if not fake: # Send a signal saying it ran # Actually, don't - we're implementing this properly in 0.7 #ran_migration.send(None, app=app_name, migration=migration, method=torun) pass
def orm(self): return FakeORM(self.migration_class(), self.app_label())
def prev_orm(self): previous = self.previous() if previous is None: # First migration? The 'previous ORM' is empty. return FakeORM(None, self.app_label()) return previous.orm()
def apply_south_migration(migration_cls, *_args, **_kwargs): orm = FakeORM(migration_cls, 'testapp') migration_instance = migration_cls() migration_instance.forwards(orm)
'Meta': {'unique_together': "(('user', 'key'),)", 'object_name': 'UserProperty'}, 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'key': ('django.db.models.fields.CharField', [], {'max_length': '16'}), 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'properties'", 'to': "orm['forum.User']"}), 'value': ('forum.models.utils.PickledObjectField', [], {'null': 'True'}) }, 'forum.validationhash': { 'Meta': {'unique_together': "(('user', 'type'),)", 'object_name': 'ValidationHash'}, 'expiration': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime(2010, 7, 2, 13, 6, 46, 883626)'}), 'hash_code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'seed': ('django.db.models.fields.CharField', [], {'max_length': '12'}), 'type': ('django.db.models.fields.CharField', [], {'max_length': '12'}), 'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['forum.User']"}) }, 'forum.vote': { 'Meta': {'unique_together': "(('user', 'node'),)", 'object_name': 'Vote'}, 'action': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'vote'", 'unique': 'True', 'to': "orm['forum.Action']"}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'node': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['forum.Node']"}), 'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'votes'", 'to': "orm['forum.User']"}), 'value': ('django.db.models.fields.SmallIntegerField', [], {}), 'voted_at': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}) } } complete_apps = ['forum'] orm = FakeORM(Migration, "forum")