def write_sql(sql, database): """Output and return a list of SQL statements. Args: sql (list): A list of SQL statements. Each entry might be a string, or a tuple consisting of a format string and formatting arguments. database (unicode): The database the SQL statements would be executed on. Returns: list of unicode: The formatted list of SQL statements. """ evolver = EvolutionOperationsMulti(database).get_evolver() qp = evolver.quote_sql_param out_sql = [] for statement in sql: if isinstance(statement, tuple): statement = six.text_type(statement[0] % tuple( qp(evolver.normalize_value(s)) for s in statement[1] )) print(statement) out_sql.append(statement) return out_sql
def rescan_indexes(self): """Rescan the list of indexes from the database. This will look up all indexes found in the database, recording each one's table. If there are existing indexes being tracked for a table containing indexes, they will be removed. """ evolver = EvolutionOperationsMulti(self.db_name).get_evolver() connection = evolver.connection introspection = connection.introspection cursor = connection.cursor() for table_name in introspection.get_table_list(cursor): if hasattr(table_name, 'name'): # Django >= 1.7 table_name = table_name.name if self.has_table(table_name): self.clear_indexes(table_name) else: self.add_table(table_name) indexes = evolver.get_indexes_for_table(table_name) for index_name, index_info in six.iteritems(indexes): self.add_index(table_name=table_name, index_name=index_name, columns=index_info['columns'], unique=index_info['unique'])
def rescan_tables(self): """Rescan the list of tables from the database. This will look up all tables found in the database, along with information (such as indexes) on those tables. Existing information on the tables will be flushed. """ evolver = EvolutionOperationsMulti(self.db_name).get_evolver() connection = evolver.connection introspection = connection.introspection cursor = connection.cursor() for table_name in introspection.get_table_list(cursor): # NOTE: The table names are already normalized, so there's no # need to normalize them again. if hasattr(table_name, 'name'): # In Django >= 1.7, we get back TableInfo namedtuples, # which have 'name' and 'type' keys. We don't care about # anything but 'name'. table_name = table_name.name if self.has_table(table_name): self.clear_indexes(table_name) else: self.add_table(table_name) constraints = evolver.get_constraints_for_table(table_name) for constraint_name, constraint_info in six.iteritems(constraints): self.add_index(table_name=table_name, index_name=constraint_name, columns=constraint_info['columns'], unique=constraint_info['unique'])
def execute_sql(cursor, sql, database): """ Execute a list of SQL statements on the provided cursor, unrolling parameters as required """ evolver = EvolutionOperationsMulti(database).get_evolver() statement = None try: for statement in sql: if isinstance(statement, tuple): statement = (statement[0].strip(), statement[1]) if statement[0] and not statement[0].startswith('--'): cursor.execute( statement[0], tuple( evolver.normalize_value(s) for s in statement[1])) else: statement = statement.strip() if statement and not statement.startswith('--'): cursor.execute(statement) except Exception, e: # Augment the exception so that callers can get the SQL statement # that failed. e.last_sql_statement = statement raise
def rescan_indexes(self): """Rescan the list of indexes from the database. This will look up all indexes found in the database, recording each one's table. If there are existing indexes being tracked for a table containing indexes, they will be removed. """ evolver = EvolutionOperationsMulti(self.db_name).get_evolver() connection = evolver.connection introspection = connection.introspection cursor = connection.cursor() for table_name in introspection.get_table_list(cursor): if hasattr(table_name, 'name'): # Django >= 1.7 table_name = table_name.name if self.has_table(table_name): self.clear_indexes(table_name) else: self.add_table(table_name) indexes = evolver.get_indexes_for_table(table_name) for index_name, index_info in six.iteritems(indexes): self.add_index(table_name=table_name, index_name=index_name, columns=index_info['columns'], unique=index_info['unique'])
def test_set_db_index_true_and_existing_index(self): """Testing ChangeField with setting db_index=True and existing index in the database """ class DestModel(models.Model): my_id = models.AutoField(primary_key=True) alt_pk = models.IntegerField() int_field = models.IntegerField(db_column='custom_db_column') int_field1 = models.IntegerField(db_index=True) int_field2 = models.IntegerField(db_index=True) int_field3 = models.IntegerField(unique=True) int_field4 = models.IntegerField(unique=False) char_field = models.CharField(max_length=20) char_field1 = models.CharField(max_length=25, null=True) char_field2 = models.CharField(max_length=30, null=False) m2m_field1 = models.ManyToManyField( ChangeAnchor1, db_table='change_field_non-default_m2m_table') evolver = EvolutionOperationsMulti('default', self.database_sig).get_evolver() index_name = evolver.get_default_index_name( 'tests_testmodel', DestModel._meta.get_field('int_field2')) self.database_sig['tests_testmodel']['indexes'] = { index_name: { 'unique': False, 'columns': ['int_field2'], } } self.assertTrue(has_index_with_columns( self.database_sig, 'tests_testmodel', ['int_field2'])) self.perform_evolution_tests( DestModel, [ ChangeField('TestModel', 'int_field2', initial=None, db_index=True), ], ("In model tests.TestModel:\n" " In field 'int_field2':\n" " Property 'db_index' has changed"), [ "ChangeField('TestModel', 'int_field2', initial=None," " db_index=True)", ], 'AddDBIndexNoOpChangeModel', rescan_indexes=False) self.assertTrue(has_index_with_columns( self.test_database_sig, 'tests_testmodel', ['int_field2']))
def rescan_indexes_for_database_sig(database_sig, database): evolver = EvolutionOperationsMulti(database).get_evolver() connection = evolver.connection introspection = connection.introspection cursor = connection.cursor() for table_name in introspection.get_table_list(cursor): table_sig = create_empty_database_table_sig() indexes = evolver.get_indexes_for_table(table_name) for index_name, index_info in indexes.iteritems(): table_sig['indexes'][index_name] = index_info database_sig[table_name] = table_sig
def test_set_db_index_true_and_existing_index(self): """Testing ChangeField with setting db_index=True and existing index in the database """ class DestModel(models.Model): my_id = models.AutoField(primary_key=True) alt_pk = models.IntegerField() int_field = models.IntegerField(db_column='custom_db_column') int_field1 = models.IntegerField(db_index=True) int_field2 = models.IntegerField(db_index=True) int_field3 = models.IntegerField(unique=True) int_field4 = models.IntegerField(unique=False) char_field = models.CharField(max_length=20) char_field1 = models.CharField(max_length=25, null=True) char_field2 = models.CharField(max_length=30, null=False) m2m_field1 = models.ManyToManyField( ChangeAnchor1, db_table='change_field_non-default_m2m_table') evolver = EvolutionOperationsMulti('default', self.database_sig).get_evolver() index_name = evolver.get_default_index_name( 'tests_testmodel', DestModel._meta.get_field('int_field2')) self.database_sig['tests_testmodel']['indexes'] = { index_name: { 'unique': False, 'columns': ['int_field2'], } } self.assertTrue( has_index_with_columns(self.database_sig, 'tests_testmodel', ['int_field2'])) self.perform_evolution_tests(DestModel, [ ChangeField('TestModel', 'int_field2', initial=None, db_index=True), ], ("In model tests.TestModel:\n" " In field 'int_field2':\n" " Property 'db_index' has changed"), [ "ChangeField('TestModel', 'int_field2', initial=None," " db_index=True)", ], 'AddDBIndexNoOpChangeModel', rescan_indexes=False) self.assertTrue( has_index_with_columns(self.test_database_sig, 'tests_testmodel', ['int_field2']))
def __init__(self, app_mutator, model_name, app_label, proj_sig, database_sig, database): self.app_mutator = app_mutator self.model_name = model_name self.app_label = app_label self.database = (database or get_database_for_model_name(app_label, model_name)) self.can_simulate = True self._ops = [] self._finalized = False assert self.database evolution_ops = EvolutionOperationsMulti(self.database, self.database_sig) self.evolver = evolution_ops.get_evolver()
def __init__(self, app_mutator, model_name, app_label, proj_sig, database_sig, database): self.app_mutator = app_mutator self.model_name = model_name self.app_label = app_label self.database = (database or get_database_for_model_name(app_label, model_name)) self.can_simulate = True self._ops = [] self._finalized = False assert self.database evolution_ops = EvolutionOperationsMulti(self.database, self.database_sig) self.evolver = evolution_ops.get_evolver()
def write_sql(sql, database): "Output a list of SQL statements, unrolling parameters as required" evolver = EvolutionOperationsMulti(database).get_evolver() qp = evolver.quote_sql_param out_sql = [] for statement in sql: if isinstance(statement, tuple): statement = unicode( statement[0] % tuple(qp(evolver.normalize_value(s)) for s in statement[1])) print statement out_sql.append(statement) return out_sql
def evolver(self, model, database=None): if is_multi_db() and database is None: db_name = router.db_for_write(model) else: db_name = database or 'default' return EvolutionOperationsMulti(db_name).get_evolver()
class DatabaseSigTests(TestCase): """Testing database signatures.""" def setUp(self): self.database_sig = create_database_sig('default') self.evolver = EvolutionOperationsMulti('default').get_evolver() def test_initial_state(self): """Testing initial state of database_sig""" tables = self.database_sig.keys() # Check that a few known tables are in the list, to make sure # the scan worked. self.assertTrue('auth_permission' in tables) self.assertTrue('auth_user' in tables) self.assertTrue('django_evolution' in tables) self.assertTrue('django_project_version' in tables) self.assertTrue('indexes' in self.database_sig['django_evolution']) # Check the Evolution model index_name = self.evolver.get_default_index_name( Evolution._meta.db_table, Evolution._meta.get_field('version')) indexes = self.database_sig['django_evolution']['indexes'] self.assertTrue(index_name in indexes) self.assertEqual( indexes[index_name], { 'unique': False, 'columns': ['version_id'], })
def evolver(self, model): db_name = None if is_multi_db(): db_name = router.db_for_write(model) return EvolutionOperationsMulti(db_name).get_evolver()
def __enter__(self): """Enter the context manager. This will prepare internal state for execution, and optionally disable constraint checking (if requested during construction). The context manager must be entered before operations will work. Context: SQLExecutor: This instance. """ connection = self._connection database = self._database if (connection.in_atomic_block and not connection.features.can_rollback_ddl): logger.warning('Some database schema modifications may not be ' 'able to be rolled back on this database if ' 'something goes wrong.') if not self._check_constraints: self._constraints_disabled = \ connection.disable_constraint_checking() self._cursor = connection.cursor() self._evolver_backend = \ EvolutionOperationsMulti(database).get_evolver() return self
def write_sql(sql, database): "Output a list of SQL statements, unrolling parameters as required" evolver = EvolutionOperationsMulti(database).get_evolver() qp = evolver.quote_sql_param out_sql = [] for statement in sql: if isinstance(statement, tuple): statement = unicode(statement[0] % tuple( qp(evolver.normalize_value(s)) for s in statement[1] )) print statement out_sql.append(statement) return out_sql
def write_sql(sql, database): "Output a list of SQL statements, unrolling parameters as required" qp = EvolutionOperationsMulti(database).get_evolver().quote_sql_param for statement in sql: if isinstance(statement, tuple): print unicode(statement[0] % tuple(qp(s) for s in statement[1])) else: print unicode(statement)
def get_evolver(self): """Return an evolver for the database. Returns: django_evolution.db.EvolutionOperationsMulti: The database evolver for this type of database. """ return EvolutionOperationsMulti(self.database, self.database_state).get_evolver()
def rescan_indexes_for_database_sig(database_sig, database): evolver = EvolutionOperationsMulti(database).get_evolver() connection = evolver.connection introspection = connection.introspection cursor = connection.cursor() for table_name in introspection.get_table_list(cursor): if hasattr(table_name, 'name'): # Django >= 1.7 table_name = table_name.name table_sig = create_empty_database_table_sig() indexes = evolver.get_indexes_for_table(table_name) for index_name, index_info in indexes.iteritems(): table_sig['indexes'][index_name] = index_info database_sig[table_name] = table_sig
def execute_sql(cursor, sql, database): """ Execute a list of SQL statements on the provided cursor, unrolling parameters as required """ evolver = EvolutionOperationsMulti(database).get_evolver() for statement in sql: if isinstance(statement, tuple): statement = (statement[0].strip(), statement[1]) if statement[0] and not statement[0].startswith('--'): cursor.execute(statement[0], tuple( evolver.normalize_value(s) for s in statement[1] )) else: statement = statement.strip() if statement and not statement.startswith('--'): cursor.execute(statement)
def execute_sql(cursor, sql, database): """Execute a list of SQL statements. Args: cursor (object): The database backend's cursor. sql (list): A list of SQL statements. Each entry might be a string, or a tuple consisting of a format string and formatting arguments. database (unicode): The database the SQL statements would be executed on. """ evolver = EvolutionOperationsMulti(database).get_evolver() statement = None try: for statement in sql: if isinstance(statement, tuple): statement = (statement[0].strip(), statement[1]) if statement[0] and not statement[0].startswith('--'): cursor.execute(statement[0], tuple( evolver.normalize_value(s) for s in statement[1] )) else: statement = statement.strip() if statement and not statement.startswith('--'): cursor.execute(statement) except Exception as e: # Augment the exception so that callers can get the SQL statement # that failed. e.last_sql_statement = statement raise
def get_default_tablespace(db_name): """Return the default tablespace for a database. Version Added: 2.2 Args: db_name (unicode): The name of the database. Returns: unicode: The default tablespace for the database, or ``None``. """ evolver = EvolutionOperationsMulti(db_name).get_evolver() return evolver.default_tablespace
def evolver(self, model, database_state, database=None): if database is None: database = get_database_for_model_name(model.app_label, model.model_name) return EvolutionOperationsMulti(database, database_state).get_evolver()
def register_models(database_state, models, register_indexes=False, new_app_label='tests', db_name='default', app=evo_test): """Register models for testing purposes. Args: database_state (django_evolution.db.state.DatabaseState): The database state to populate with model information. models (list of django.db.models.Model): The models to register. register_indexes (bool, optional): Whether indexes should be registered for any models. Defaults to ``False``. new_app_label (str, optional): The label for the test app. Defaults to "tests". db_name (str, optional): The name of the database connection. Defaults to "default". app (module, optional): The application module for the test models. Returns: collections.OrderedDict: A dictionary of registered models. The keys are model names, and the values are the models. """ app_cache = OrderedDict() evolver = EvolutionOperationsMulti(db_name, database_state).get_evolver() db_connection = connections[db_name or DEFAULT_DB_ALIAS] max_name_length = db_connection.ops.max_name_length() for new_object_name, model in reversed(models): # Grab some state from the model's meta instance. Some of this will # be original state that we'll keep around to help us unregister old # values and compute new ones. meta = model._meta orig_app_label = meta.app_label orig_db_table = meta.db_table orig_object_name = meta.object_name orig_model_name = get_model_name(model) # Find out if the table name being used is a custom table name, or # one generated by Django. new_model_name = new_object_name.lower() new_db_table = orig_db_table generated_db_table = truncate_name( '%s_%s' % (orig_app_label, orig_model_name), max_name_length) if orig_db_table == generated_db_table: # It was a generated one, so replace it with a version containing # the new model and app names. new_db_table = truncate_name('%s_%s' % (new_app_label, new_model_name), max_name_length) meta.db_table = new_db_table # Set the new app/model names back on the meta instance. meta.app_label = new_app_label meta.object_name = new_object_name set_model_name(model, new_model_name) # Add an entry for the table in the database state, if it's not # already there. if not database_state.has_table(new_db_table): database_state.add_table(new_db_table) if register_indexes: # Now that we definitely have an entry, store the indexes for # all the fields in the database state, so that other operations # can look up the index names. for field in meta.local_fields: if field.db_index or field.unique: new_index_name = create_index_name( db_connection, new_db_table, field_names=[field.name], col_names=[field.column], unique=field.unique) database_state.add_index( index_name=new_index_name, table_name=new_db_table, columns=[field.column], unique=field.unique) for field_names in meta.unique_together: fields = evolver.get_fields_for_names(model, field_names) new_index_name = create_index_name( db_connection, new_db_table, field_names=field_names, unique=True) database_state.add_index( index_name=new_index_name, table_name=new_db_table, columns=[field.column for field in fields], unique=True) for field_names in getattr(meta, 'index_together', []): # Django >= 1.5 fields = evolver.get_fields_for_names(model, field_names) new_index_name = create_index_together_name( db_connection, new_db_table, field_names=[field.name for field in fields]) database_state.add_index( index_name=new_index_name, table_name=new_db_table, columns=[field.column for field in fields]) if getattr(meta, 'indexes', None): # Django >= 1.11 for index, orig_index in zip(meta.indexes, meta.original_attrs['indexes']): if not orig_index.name: # The name was auto-generated. We'll need to generate # it again for the new table name. index.set_name_with_model(model) fields = evolver.get_fields_for_names( model, index.fields, allow_sort_prefixes=True) database_state.add_index( index_name=index.name, table_name=new_db_table, columns=[field.column for field in fields]) # ManyToManyFields have their own tables, which will also need to be # renamed. Go through each of them and figure out what changes need # to be made. for field in meta.local_many_to_many: through = get_remote_field(field).through if not through: continue through_meta = through._meta through_orig_model_name = get_model_name(through) through_new_model_name = through_orig_model_name # Find out if the through table name is a custom table name, or # one generated by Django. generated_db_table = truncate_name( '%s_%s' % (orig_db_table, field.name), max_name_length) if through_meta.db_table == generated_db_table: # This is an auto-generated table name. Start changing the # state for it. assert through_meta.app_label == orig_app_label through_meta.app_label = new_app_label # Transform the 'through' table information only if we've # transformed the parent db_table. if new_db_table != orig_db_table: through_meta.db_table = truncate_name( '%s_%s' % (new_db_table, field.name), max_name_length) through_meta.object_name = \ through_meta.object_name.replace(orig_object_name, new_object_name) through_new_model_name = \ through_orig_model_name.replace(orig_model_name, new_model_name) set_model_name(through, through_new_model_name) # Change each of the columns for the fields on the # ManyToManyField's model to reflect the new model names. for through_field in through._meta.local_fields: through_remote_field = get_remote_field(through_field) if (through_remote_field and get_remote_field_model(through_remote_field)): column = through_field.column if (column.startswith((orig_model_name, 'to_%s' % orig_model_name, 'from_%s' % orig_model_name))): # This is a field that references one end of the # relation or another. Update the model naem in the # field's column. through_field.column = column.replace(orig_model_name, new_model_name) # Replace the entry in the models cache for the through table, # removing the old name and adding the new one. if through_orig_model_name in all_models[orig_app_label]: unregister_app_model(orig_app_label, through_orig_model_name) app_cache[through_new_model_name] = through register_app_models(new_app_label, [(through_new_model_name, through)]) # Unregister with the old model name and register the new one. if orig_model_name in all_models[orig_app_label]: unregister_app_model(orig_app_label, orig_model_name) register_app_models(new_app_label, [(new_model_name, model)]) app_cache[new_model_name] = model # If the app hasn't yet been registered, do that now. if not is_app_registered(app): register_app(new_app_label, app) return app_cache
def _register_models(database_sig, app_label='tests', db_name='default', app=evo_test, *models, **kwargs): """Register models for testing purposes. Args: database_sig (dict): The database signature to populate with model information. app_label (str, optional): The label for the test app. Defaults to "tests". db_name (str, optional): The name of the database connection. Defaults to "default". app (module, optional): The application module for the test models. *models (tuple): The models to register. **kwargs (dict): Additional keyword arguments. This supports: ``register_indexes``: Specifies whether indexes should be registered for any models. Defaults to ``False``. Returns: collections.OrderedDict: A dictionary of registered models. The keys are model names, and the values are the models. """ django_evolution_models = all_models['django_evolution'] app_cache = OrderedDict() evolver = EvolutionOperationsMulti(db_name, database_sig).get_evolver() register_indexes = kwargs.get('register_indexes', False) my_connection = connections[db_name or DEFAULT_DB_ALIAS] max_name_length = my_connection.ops.max_name_length() for name, model in reversed(models): orig_model_name = get_model_name(model) if orig_model_name in django_evolution_models: unregister_app_model('django_evolution', orig_model_name) orig_db_table = model._meta.db_table orig_object_name = model._meta.object_name generated_db_table = truncate_name( '%s_%s' % (model._meta.app_label, orig_model_name), max_name_length) if orig_db_table.startswith(generated_db_table): model._meta.db_table = '%s_%s' % (app_label, name.lower()) model._meta.db_table = truncate_name(model._meta.db_table, max_name_length) model._meta.app_label = app_label model._meta.object_name = name model_name = name.lower() set_model_name(model, model_name) # Add an entry for the table in database_sig, if it's not already # there. if model._meta.db_table not in database_sig: database_sig[model._meta.db_table] = \ signature.create_empty_database_table_sig() if register_indexes: # Now that we definitely have an entry, store the indexes for # all the fields in database_sig, so that other operations can # look up the index names. for field in model._meta.local_fields: if field.db_index or field.unique: index_name = create_index_name( my_connection, model._meta.db_table, field_names=[field.name], col_names=[field.column], unique=field.unique) signature.add_index_to_database_sig( evolver, database_sig, model, [field], index_name=index_name, unique=field.unique) for field_names in model._meta.unique_together: index_name = create_index_name( my_connection, model._meta.db_table, field_names=field_names, unique=True) signature.add_index_to_database_sig( evolver, database_sig, model, evolver.get_fields_for_names(model, field_names), index_name=index_name, unique=True) for field_names in getattr(model._meta, 'index_together', []): fields = evolver.get_fields_for_names(model, field_names) index_name = create_index_name( my_connection, model._meta.db_table, field_names=[field.name for field in fields], col_names=[field.column for field in fields]) signature.add_index_to_database_sig( evolver, database_sig, model, fields, index_name=index_name) # Register the model with the app. add_app_test_model(model, app_label=app_label) for field in model._meta.local_many_to_many: if not field.rel.through: continue through = field.rel.through generated_db_table = truncate_name( '%s_%s' % (orig_db_table, field.name), max_name_length) if through._meta.db_table == generated_db_table: through._meta.app_label = app_label # Transform the 'through' table information only # if we've transformed the parent db_table. if model._meta.db_table != orig_db_table: through._meta.db_table = \ '%s_%s' % (model._meta.db_table, field.name) through._meta.object_name = \ through._meta.object_name.replace( orig_object_name, model._meta.object_name) set_model_name( through, get_model_name(through).replace(orig_model_name, model_name)) through._meta.db_table = \ truncate_name(through._meta.db_table, max_name_length) for field in through._meta.local_fields: if field.rel and field.rel.to: column = field.column if (column.startswith(orig_model_name) or column.startswith('to_%s' % orig_model_name) or column.startswith('from_%s' % orig_model_name)): field.column = column.replace( orig_model_name, get_model_name(model)) through_model_name = get_model_name(through) if through_model_name in django_evolution_models: unregister_app_model('django_evolution', through_model_name) app_cache[through_model_name] = through add_app_test_model(through, app_label=app_label) app_cache[model_name] = model if not is_app_registered(app): register_app(app_label, app) return app_cache
def setUp(self): self.database_sig = create_database_sig('default') self.evolver = EvolutionOperationsMulti('default').get_evolver()
def setUp(self): self.database_sig = create_database_sig('default') self.evolver = EvolutionOperationsMulti('default').get_evolver()
def register_models(database_state, models, register_indexes=False, new_app_label='tests', db_name='default', app=evo_test): """Register models for testing purposes. Args: database_state (django_evolution.db.state.DatabaseState): The database state to populate with model information. models (list of django.db.models.Model): The models to register. register_indexes (bool, optional): Whether indexes should be registered for any models. Defaults to ``False``. new_app_label (str, optional): The label for the test app. Defaults to "tests". db_name (str, optional): The name of the database connection. Defaults to "default". app (module, optional): The application module for the test models. Returns: collections.OrderedDict: A dictionary of registered models. The keys are model names, and the values are the models. """ app_cache = OrderedDict() evolver = EvolutionOperationsMulti(db_name, database_state).get_evolver() db_connection = connections[db_name or DEFAULT_DB_ALIAS] max_name_length = db_connection.ops.max_name_length() for new_object_name, model in reversed(models): # Grab some state from the model's meta instance. Some of this will # be original state that we'll keep around to help us unregister old # values and compute new ones. meta = model._meta orig_app_label = meta.app_label orig_db_table = meta.db_table orig_object_name = meta.object_name orig_model_name = get_model_name(model) # Find out if the table name being used is a custom table name, or # one generated by Django. new_model_name = new_object_name.lower() new_db_table = orig_db_table generated_db_table = truncate_name( '%s_%s' % (orig_app_label, orig_model_name), max_name_length) if orig_db_table == generated_db_table: # It was a generated one, so replace it with a version containing # the new model and app names. new_db_table = truncate_name('%s_%s' % (new_app_label, new_model_name), max_name_length) meta.db_table = new_db_table # Set the new app/model names back on the meta instance. meta.app_label = new_app_label meta.object_name = new_object_name set_model_name(model, new_model_name) # Add an entry for the table in the database state, if it's not # already there. if not database_state.has_table(new_db_table): database_state.add_table(new_db_table) if register_indexes: # Now that we definitely have an entry, store the indexes for # all the fields in the database state, so that other operations # can look up the index names. for field in meta.local_fields: if field.db_index or field.unique: new_index_name = create_index_name( db_connection, new_db_table, field_names=[field.name], col_names=[field.column], unique=field.unique) database_state.add_index( index_name=new_index_name, table_name=new_db_table, columns=[field.column], unique=field.unique) for field_names in meta.unique_together: fields = evolver.get_fields_for_names(model, field_names) new_index_name = create_index_name( db_connection, new_db_table, field_names=field_names, unique=True) database_state.add_index( index_name=new_index_name, table_name=new_db_table, columns=[field.column for field in fields], unique=True) for field_names in getattr(meta, 'index_together', []): # Django >= 1.5 fields = evolver.get_fields_for_names(model, field_names) new_index_name = create_index_together_name( db_connection, new_db_table, field_names=[field.name for field in fields]) database_state.add_index( index_name=new_index_name, table_name=new_db_table, columns=[field.column for field in fields]) if getattr(meta, 'indexes', None): # Django >= 1.11 for index, orig_index in zip(meta.indexes, meta.original_attrs['indexes']): if not orig_index.name: # The name was auto-generated. We'll need to generate # it again for the new table name. index.set_name_with_model(model) fields = evolver.get_fields_for_names( model, index.fields, allow_sort_prefixes=True) database_state.add_index( index_name=index.name, table_name=new_db_table, columns=[field.column for field in fields]) # ManyToManyFields have their own tables, which will also need to be # renamed. Go through each of them and figure out what changes need # to be made. for field in meta.local_many_to_many: through = get_remote_field(field).through if not through: continue through_meta = through._meta through_orig_model_name = get_model_name(through) through_new_model_name = through_orig_model_name # Find out if the through table name is a custom table name, or # one generated by Django. generated_db_table = truncate_name( '%s_%s' % (orig_db_table, field.name), max_name_length) if through_meta.db_table == generated_db_table: # This is an auto-generated table name. Start changing the # state for it. assert through_meta.app_label == orig_app_label through_meta.app_label = new_app_label # Transform the 'through' table information only if we've # transformed the parent db_table. if new_db_table != orig_db_table: through_meta.db_table = truncate_name( '%s_%s' % (new_db_table, field.name), max_name_length) through_meta.object_name = \ through_meta.object_name.replace(orig_object_name, new_object_name) through_new_model_name = \ through_orig_model_name.replace(orig_model_name, new_model_name) set_model_name(through, through_new_model_name) # Change each of the columns for the fields on the # ManyToManyField's model to reflect the new model names. for through_field in through._meta.local_fields: through_remote_field = get_remote_field(through_field) if (through_remote_field and get_remote_field_model(through_remote_field)): column = through_field.column if (column.startswith((orig_model_name, 'to_%s' % orig_model_name, 'from_%s' % orig_model_name))): # This is a field that references one end of the # relation or another. Update the model naem in the # field's column. through_field.column = column.replace(orig_model_name, new_model_name) # Replace the entry in the models cache for the through table, # removing the old name and adding the new one. if through_orig_model_name in all_models[orig_app_label]: unregister_app_model(orig_app_label, through_orig_model_name) app_cache[through_new_model_name] = through register_app_models(new_app_label, [(through_new_model_name, through)]) # Unregister with the old model name and register the new one. if orig_model_name in all_models[orig_app_label]: unregister_app_model(orig_app_label, orig_model_name) register_app_models(new_app_label, [(new_model_name, model)]) app_cache[new_model_name] = model # If the app hasn't yet been registered, do that now. if not is_app_registered(app): register_app(new_app_label, app) return app_cache