def eval_in_context(self, code, app, extra_imports={}): "Evaluates the given code in the context of the migration file." # Drag in the migration module's locals (hopefully including models.py) fake_locals = dict(inspect.getmodule(self.cls).__dict__) # Remove all models from that (i.e. from modern models.py), to stop pollution for key, value in fake_locals.items(): if isinstance(value, type) and issubclass(value, models.Model) and hasattr(value, "_meta"): del fake_locals[key] # We add our models into the locals for the eval fake_locals.update(dict([ (name.split(".")[-1], model) for name, model in self.models.items() ])) # Make sure the ones for this app override. fake_locals.update(dict([ (name.split(".")[-1], model) for name, model in self.models.items() if name.split(".")[0] == app ])) # Ourselves as orm, to allow non-fail cross-app referencing fake_locals['orm'] = self # And a fake _ function fake_locals['_'] = lambda x: x # Datetime; there should be no datetime direct accesses fake_locals['datetime'] = datetime # Now, go through the requested imports and import them. for name, value in extra_imports.items(): # First, try getting it out of locals. parts = value.split(".") try: obj = fake_locals[parts[0]] for part in parts[1:]: obj = getattr(obj, part) except (KeyError, AttributeError): pass else: fake_locals[name] = obj continue # OK, try to import it directly try: fake_locals[name] = ask_for_it_by_name(value) except ImportError: if name == "SouthFieldClass": raise ValueError("Cannot import the required field '%s'" % value) else: print "WARNING: Cannot import '%s'" % value # Use ModelsLocals to make lookups work right for CapitalisedModels fake_locals = ModelsLocals(fake_locals) return eval(code, globals(), fake_locals)
def eval_in_context(self, code, app, extra_imports={}): "Evaluates the given code in the context of the migration file." # Drag in the migration module's locals (hopefully including models.py) fake_locals = dict(inspect.getmodule(self.cls).__dict__) # Remove all models from that (i.e. from modern models.py), to stop pollution for key, value in fake_locals.items(): if isinstance(value, type) and issubclass( value, models.Model) and hasattr(value, "_meta"): del fake_locals[key] # We add our models into the locals for the eval fake_locals.update( dict([(name.split(".")[-1], model) for name, model in self.models.items()])) # Make sure the ones for this app override. fake_locals.update( dict([(name.split(".")[-1], model) for name, model in self.models.items() if name.split(".")[0] == app])) # Ourselves as orm, to allow non-fail cross-app referencing fake_locals['orm'] = self # And a fake _ function fake_locals['_'] = lambda x: x # Datetime; there should be no datetime direct accesses fake_locals['datetime'] = datetime # Now, go through the requested imports and import them. for name, value in extra_imports.items(): # First, try getting it out of locals. parts = value.split(".") try: obj = fake_locals[parts[0]] for part in parts[1:]: obj = getattr(obj, part) except (KeyError, AttributeError): pass else: fake_locals[name] = obj continue # OK, try to import it directly try: fake_locals[name] = ask_for_it_by_name(value) except ImportError: if name == "SouthFieldClass": raise ValueError("Cannot import the required field '%s'" % value) else: print "WARNING: Cannot import '%s'" % value # Use ModelsLocals to make lookups work right for CapitalisedModels fake_locals = ModelsLocals(fake_locals) return eval(code, globals(), fake_locals)
def all_migrations(applications=None): """ Returns all Migrations for all `applications` that are migrated. """ if applications is None: applications = models.get_apps() for model_module in applications: # The app they've passed is the models module - go up one level app_path = ".".join(model_module.__name__.split(".")[:-1]) app = ask_for_it_by_name(app_path) try: yield Migrations(app) except exceptions.NoMigrations: pass
def innodb_ready_rename_column(orm, models, table, old_column_name, new_column_name, app_model, new_field_name): """ Foreign key renaming which works for InnoDB More: http://south.aeracode.org/ticket/466 Parameters: - orm: a reference to 'orm' parameter passed to Migration.forwards()/backwards() - models: reference to Migration.models data structure - table: e.g. 'askbot_thread' - old_column_name: e.g. 'question_post_id' - new_column_name: e.g. 'question_id' - app_model: e.g. 'askbot.thread' (should be a dict key into 'models') - new_field_name: e.g. 'question' (usually it's same as new_column_name, only without trailing '_id') """ use_workaround = houston_do_we_have_a_problem(table) # ditch the foreign key if use_workaround: db.delete_foreign_key(table, old_column_name) # rename column db.rename_column(table, old_column_name, new_column_name) # restore the foreign key if not use_workaround: return model_def = models[app_model][new_field_name] assert model_def[0] == 'django.db.models.fields.related.ForeignKey' # Copy the dict so that we don't change the original # (otherwise the dry run would change it for the "normal" run # and the latter would try to convert str to model once again) fkey_params = model_def[2].copy() assert 'to' in fkey_params assert fkey_params['to'].startswith("orm['") assert fkey_params['to'].endswith("']") fkey_params['to'] = orm[fkey_params['to'][ 5:-2]] # convert "orm['app.models']" string to actual model field = ask_for_it_by_name(model_def[0])(**fkey_params) # INFO: ask_for_it_by_name() if equivalent to self.gf() which is usually used in migrations, e.g.: # db.alter_column('askbot_badgedata', 'slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=50)) db.alter_column(table, new_column_name, field) db.clear_deferred_sql()
def innodb_ready_rename_column(orm, models, table, old_column_name, new_column_name, app_model, new_field_name): """ Foreign key renaming which works for InnoDB More: http://south.aeracode.org/ticket/466 Parameters: - orm: a reference to 'orm' parameter passed to Migration.forwards()/backwards() - models: reference to Migration.models data structure - table: e.g. 'askbot_thread' - old_column_name: e.g. 'question_post_id' - new_column_name: e.g. 'question_id' - app_model: e.g. 'askbot.thread' (should be a dict key into 'models') - new_field_name: e.g. 'question' (usually it's same as new_column_name, only without trailing '_id') """ use_workaround = houston_do_we_have_a_problem(table) # ditch the foreign key if use_workaround: db.delete_foreign_key(table, old_column_name) # rename column db.rename_column(table, old_column_name, new_column_name) # restore the foreign key if not use_workaround: return model_def = models[app_model][new_field_name] assert model_def[0] == "django.db.models.fields.related.ForeignKey" # Copy the dict so that we don't change the original # (otherwise the dry run would change it for the "normal" run # and the latter would try to convert str to model once again) fkey_params = model_def[2].copy() assert "to" in fkey_params assert fkey_params["to"].startswith("orm['") assert fkey_params["to"].endswith("']") fkey_params["to"] = orm[fkey_params["to"][5:-2]] # convert "orm['app.models']" string to actual model field = ask_for_it_by_name(model_def[0])(**fkey_params) # INFO: ask_for_it_by_name() if equivalent to self.gf() which is usually used in migrations, e.g.: # db.alter_column('askbot_badgedata', 'slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=50)) db.alter_column(table, new_column_name, field) db.clear_deferred_sql()
def gf(self, field_name): "Gets a field by absolute reference." return ask_for_it_by_name(field_name)
def list_migrations(app_name): """List schema migrations in the given app.""" app = ask_for_it_by_name(app_name) return [migration.name() for migration in Migrations(app)]
def gf(self, field_name): "Gets a field by absolute reference." field = ask_for_it_by_name(field_name) field.model = FakeModel return field