def rename_column(self, model, old_field, new_field): if old_field.column == new_field.column: # No Operation return [] qn = self.connection.ops.quote_name max_name_length = self.connection.ops.max_name_length() opts = model._meta refs = {} models = [] return AlterTableSQLResult( self, model, pre_sql=self.remove_field_constraints(old_field, opts, models, refs), alter_table=[ { 'independent': True, 'sql': 'RENAME COLUMN %s TO %s' % (truncate_name(qn(old_field.column), max_name_length), truncate_name(qn(new_field.column), max_name_length)), }, ], post_sql=self.add_primary_key_field_constraints( old_field, new_field, models, refs) )
def rename_column(self, model, old_field, new_field): if old_field.column == new_field.column: # No Operation return [] qn = self.connection.ops.quote_name max_name_length = self.connection.ops.max_name_length() opts = model._meta refs = {} models = [] return AlterTableSQLResult( self, model, pre_sql=self.remove_field_constraints(old_field, opts, models, refs), alter_table=[ { 'independent': True, 'sql': 'RENAME COLUMN %s TO %s' % (truncate_name(qn(old_field.column), max_name_length), truncate_name(qn(new_field.column), max_name_length)), }, ], post_sql=self.add_primary_key_field_constraints( old_field, new_field, models, refs))
def rename_column(self, model, old_field, new_field): if old_field.column == new_field.column: # No Operation return [] qn = self.connection.ops.quote_name max_name_length = self.connection.ops.max_name_length() sql_result = AlterTableSQLResult(self, model) pre_sql, stash = self.stash_field_ref_constraints(model=model, replaced_fields={ old_field: new_field, }) sql_result.add_pre_sql(pre_sql) sql_result.add_alter_table([{ 'independent': True, 'sql': 'RENAME COLUMN %s TO %s' % (truncate_name(qn(old_field.column), max_name_length), truncate_name(qn(new_field.column), max_name_length)), }]) sql_result.add_post_sql(self.restore_field_ref_constraints(stash)) return sql_result
def generate_unique_constraint_name(table, col_names): """Return the expected name for a unique constraint. This will generate a constraint name for the current version of Django, for comparison purposes. Args: table (str): The table name. col_names (list of str): The list of column names for the constraint. Returns: The expected constraint name for this version of Django. """ if django.VERSION[:2] >= (1, 7): max_length = connection.ops.max_name_length() or 200 index_unique_name = _generate_index_unique_name_hash( connection, table, col_names) name = '_%s%s_uniq' % (col_names[0], index_unique_name) full_name = '%s%s' % (table, name) if len(full_name) > max_length: full_name = '%s%s' % (table[:(max_length - len(name))], name) return full_name else: name = digest(connection, col_names) return truncate_name('%s_%s' % (table, name), connection.ops.max_name_length())
def get_default_index_name(self, table_name, field): """Return a default index name for the database. This will return an index name for the given field that matches what the database or Django database backend would automatically generate when marking a field as indexed or unique. This can be overridden by subclasses if the database or Django database backend provides different values. Args: table_name (str): The name of the table for the index. field (django.db.models.Field): The field for the index. Returns: str: The name of the index. """ assert field.unique or field.db_index if field.unique: return truncate_name(field.column, self.connection.ops.max_name_length()) elif field.db_index: return create_index_name(self.connection, table_name, field_names=[field.name], col_names=[field.column]) else: # This won't be reached, due to the assert above. raise NotImplementedError
def record_index(self, model, fields, use_constraint_name=False, index_name=None, unique=False): """Records an index in the database signature. This is a convenience to record an index in the database signature for future lookups. It can take an index name, or it can generate a constraint name if that's to be used. """ if not index_name and use_constraint_name: index_name = truncate_name( '%s_%s_key' % (model._meta.db_table, fields[0].column), self.connection.ops.max_name_length()) assert index_name or not unique add_index_to_database_sig(self, self.database_sig, model, fields, index_name=index_name, unique=unique)
def rename_table(self, model, old_db_tablename, db_tablename): sql_result = SQLResult() if old_db_tablename == db_tablename: # No Operation return sql_result max_name_length = self.connection.ops.max_name_length() refs = {} models = [] for field in model._meta.local_many_to_many: remote_field = get_remote_field(field) if (remote_field and remote_field.through and remote_field.through._meta.db_table == old_db_tablename): through = remote_field.through for m2m_field in through._meta.local_fields: remote_m2m_field = get_remote_field(m2m_field) if remote_m2m_field: remote_m2m_field_model = get_remote_field_model( remote_m2m_field) if remote_m2m_field_model == model: models.append(remote_m2m_field_model) refs.setdefault(remote_m2m_field_model, []).append( (through, m2m_field)) remove_refs = refs.copy() if self.supports_constraints: for relto in models: sql_result.add_pre_sql( sql_delete_constraints(self.connection, relto, remove_refs)) sql_result.add( self.get_rename_table_sql(model, old_db_tablename, db_tablename)) for relto in models: for rel_class, f in refs[relto]: if rel_class._meta.db_table == old_db_tablename: rel_class._meta.db_table = db_tablename rel_class._meta.db_table = \ truncate_name(rel_class._meta.db_table, max_name_length) if self.supports_constraints: sql_result.add_post_sql( sql_add_constraints(self.connection, relto, refs)) return sql_result
def rename_table(self, model, old_db_tablename, db_tablename): sql_result = SQLResult() if old_db_tablename == db_tablename: # No Operation return sql_result max_name_length = self.connection.ops.max_name_length() refs = {} models = [] for field in model._meta.local_many_to_many: if (field.rel and field.rel.through and field.rel.through._meta.db_table == old_db_tablename): through = field.rel.through for m2m_field in through._meta.local_fields: if m2m_field.rel and m2m_field.rel.to == model: models.append(m2m_field.rel.to) refs.setdefault(m2m_field.rel.to, []).append( (through, m2m_field)) remove_refs = refs.copy() if self.supports_constraints: for relto in models: sql_result.add_pre_sql(sql_delete_constraints( self.connection, relto, remove_refs)) sql_result.add(self.get_rename_table_sql( model, old_db_tablename, db_tablename)) for relto in models: for rel_class, f in refs[relto]: if rel_class._meta.db_table == old_db_tablename: rel_class._meta.db_table = db_tablename rel_class._meta.db_table = \ truncate_name(rel_class._meta.db_table, max_name_length) if self.supports_constraints: sql_result.add_post_sql(sql_add_constraints( self.connection, relto, refs)) return sql_result
def get_new_constraint_name(self, table_name, column): """Return a newly-generated constraint name. Args: table_name (unicode): The name of the table. column (unicode): The name of the column. Returns: unicode: The new constraint name. """ return truncate_name('%s_%s_key' % (table_name, column), self.connection.ops.max_name_length())
def get_default_index_name(self, table_name, field): """Return a default index name for the database. This will return an index name for the given field that matches what the database or Django database backend would automatically generate when marking a field as indexed or unique. This can be overridden by subclasses if the database or Django database backend provides different values. Args: table_name (str): The name of the table for the index. field (django.db.models.Field): The field for the index. Returns: str: The name of the index. """ if django.VERSION[:2] >= (1, 7): # On Django 1.7+, the default behavior for the index name is used. return super(EvolutionOperations, self).get_default_index_name(table_name, field) else: # On Django < 1.7, a custom form of index name is used. assert field.unique or field.db_index if field.unique: index_name = '%s_%s_key' % (table_name, field.column) elif field.db_index: index_name = '%s_%s' % (table_name, field.column) return truncate_name(index_name, self.connection.ops.max_name_length())
def get_default_index_name(self, table_name, field): """Return a default index name for the database. This will return an index name for the given field that matches what the database or Django database backend would automatically generate when marking a field as indexed or unique. This can be overridden by subclasses if the database or Django database backend provides different values. Args: table_name (str): The name of the table for the index. field (django.db.models.Field): The field for the index. Returns: str: The name of the index. """ if django.VERSION[:2] >= (1, 7): # On Django 1.7+, the default behavior for the index name is used. return super(EvolutionOperations, self).get_default_index_name( table_name, field) else: # On Django < 1.7, a custom form of index name is used. assert field.unique or field.db_index if field.unique: index_name = '%s_%s_key' % (table_name, field.column) elif field.db_index: index_name = '%s_%s' % (table_name, field.column) return truncate_name(index_name, self.connection.ops.max_name_length())
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 generate_unique_constraint_name(connection, table, col_names): """Return the expected name for a unique constraint. This will generate a constraint name for the current version of Django, for comparison purposes. Args: connection (django.db.backends.base.base.BaseDatabaseWrapper): The database connection. table (unicode): The table name. col_names (list of unicode): The list of column names for the constraint. Returns: unicode: The expected constraint name for this version of Django. """ django_version = django.VERSION[:2] if django_version >= (1, 11): # Django 1.11 changed how index names are generated and then # shortened, choosing to shorten more preemptively. This does impact # the tests, so we need to be sure to get the logic right. max_length = connection.ops.max_name_length() or 200 index_unique_name = _generate_index_unique_name_hash( connection, table, col_names) suffix = '%s_uniq' % index_unique_name col_names_part = '_'.join(col_names) full_name = '%s_%s_%s' % (table, col_names_part, suffix) if len(full_name) > max_length: if len(suffix) > (max_length // 3): suffix = suffix[:max_length // 3] part_lengths = (max_length - len(suffix)) // 2 - 1 full_name = '%s_%s_%s' % (table[:part_lengths], col_names_part[:part_lengths], suffix) return full_name elif django_version >= (1, 7): # Django versions >= 1.7 all use roughly the same format for unique # constraint index names, but starting in Django 1.11, the format # changed slightly. In 1.7 through 1.10, the name contained only the # first column (if specifying more than one), but in 1.11, that # changed to contain all column names (for unique_together). max_length = connection.ops.max_name_length() or 200 index_unique_name = _generate_index_unique_name_hash( connection, table, col_names) name = '_%s_%s_uniq' % (col_names[0], index_unique_name) full_name = '%s%s' % (table, name) if len(full_name) > max_length: full_name = '%s%s' % (table[:(max_length - len(name))], name) return full_name else: # Convert each of the field names to Python's native string format, # which is what the default name would normally be in. name = digest(connection, [ str(col_name) for col_name in col_names ]) return truncate_name('%s_%s' % (table, name), connection.ops.max_name_length())
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 generate_index_name(db_type, table, col_names, field_names=None, index_together=False): """Generate a suitable index name to test against. The returned index name is meant for use in the test data modules, and is used to compare our own expectations of how an index should be named with the naming Django provides in its own functions. Args: db_type (str): The database type for the index. Currently, only "postgres" does anything special. table (str): The name of the table the index refers to. col_names (str or list of str): The column name, or list of column names, for the index. This is used for Postgres (when not using ``index_together``), or for Django < 1.5. Otherwise, it's interchangeable with ``field_names``. field_names (str or list of str, optional): The field name, or list of field names, for the index. This is interchangeable with ``column_names`` on Django >= 1.5 (unless using Postgres without ``index_together``), or when passing ``default=True``. index_together (bool, optional): Whether this index covers multiple fields indexed together through Django's ``Model._meta.index_together``. Defaults to ``False``. Returns: str: The resulting index name for the given criteria. """ if not isinstance(col_names, list): col_names = [col_names] if field_names and not isinstance(field_names, list): field_names = [field_names] if not field_names: field_names = col_names assert len(field_names) == len(col_names) django_version = django.VERSION[:2] # Note that we're checking Django versions/engines specifically, since # we want to test that we're getting the right index names for the # right versions of Django, rather than asking Django for them. # # The order here matters. if django_version >= (1, 7): if len(col_names) == 1: assert not index_together # Django 1.7 went back to passing a single column name (and # not a list as a single variable argument) when there's only # one column. name = digest(connection, col_names[0]) else: assert index_together index_unique_name = _generate_index_unique_name_hash( connection, table, col_names) name = '%s%s_idx' % (col_names[0], index_unique_name) elif db_type == 'postgres' and not index_together: # Postgres computes the index names separately from the rest of # the engines. It just uses '<tablename>_<colname>", same as # Django < 1.2. We only do this for normal indexes, though, not # index_together. name = col_names[0] elif django_version >= (1, 5): # Django >= 1.5 computed the digest of the representation of a # list of either field names or column names. Note that digest() # takes variable positional arguments, which this is not passing. # This is due to a design bug in these versions. name = digest(connection, field_names or col_names) elif django_version >= (1, 2): # Django >= 1.2, < 1.7 used the digest of the name of the first # column. There was no index_together in these releases. name = digest(connection, col_names[0]) else: # Django < 1.2 used just the name of the first column, no digest. name = col_names[0] return truncate_name('%s_%s' % (table, name), connection.ops.max_name_length())
def generate_index_name(connection, table, col_names, field_names=None, index_together=False, model_meta_indexes=False): """Generate a suitable index name to test against. The returned index name is meant for use in the test data modules, and is used to compare our own expectations of how an index should be named with the naming Django provides in its own functions. Args: connection (django.db.backends.base.base.BaseDatabaseWrapper): The database connection. table (unicode): The name of the table the index refers to. col_names (unicode or list of unicode): The column name, or list of column names, for the index. This is used for Postgres (when not using ``index_together``), or for Django < 1.5. Otherwise, it's interchangeable with ``field_names``. field_names (str or list of str, optional): The field name, or list of field names, for the index. This is interchangeable with ``column_names`` on Django >= 1.5 (unless using Postgres without ``index_together``), or when passing ``default=True``. index_together (bool, optional): Whether this index covers multiple fields indexed together through Django's ``Model._meta.index_together``. Defaults to ``False``. model_meta_indexes (bool, optional): The index comes from a :py:class:`django.db.models.Options.indexes` entry. Returns: unicode: The resulting index name for the given criteria. """ if not isinstance(col_names, list): col_names = [col_names] if field_names and not isinstance(field_names, list): field_names = [field_names] if not field_names: field_names = col_names assert len(field_names) == len(col_names) django_version = django.VERSION[:2] # Note that we're checking Django versions/engines specifically, since # we want to test that we're getting the right index names for the # right versions of Django, rather than asking Django for them. # # The order here matters. if django_version >= (1, 11): # Django 1.11+ changed the index format again, this time to include # all relevant column names in the plain text part of the index # (instead of just in the hash). Like with 1.7 through 1.10, the # index_together entries have a "_idx" suffix. However, there's # otherwise no difference in format between those and single-column # indexes. # # It's also worth noting that with the introduction of # Model._meta.indexes, there's *another* new index format. It's # similar, but different enough, and needs to be handled specially. if model_meta_indexes: name = '%s_%s' % ( col_names[0][:7], digest(connection, *([table] + col_names + ['idx']))[:6], ) table = table[:11] else: index_unique_name = _generate_index_unique_name_hash( connection, table, col_names) name = '%s_%s' % ('_'.join(col_names), index_unique_name) if model_meta_indexes or index_together: name = '%s_idx' % name elif django_version >= (1, 7): if len(col_names) == 1: assert not index_together # Django 1.7 went back to passing a single column name (and # not a list as a single variable argument) when there's only # one column. name = digest(connection, col_names[0]) else: assert index_together index_unique_name = _generate_index_unique_name_hash( connection, table, col_names) name = '%s_%s_idx' % (col_names[0], index_unique_name) elif connection.vendor == 'postgresql' and not index_together: # Postgres computes the index names separately from the rest of # the engines. It just uses '<tablename>_<colname>", same as # Django < 1.2. We only do this for normal indexes, though, not # index_together. name = col_names[0] elif django_version >= (1, 5): # Django >= 1.5 computed the digest of the representation of a # list of either field names or column names. Note that digest() # takes variable positional arguments, which this is not passing. # This is due to a design bug in these versions. # # We convert each of the field names to Python's native string # format, which is what the default name would normally be in. name = digest(connection, [ str(field_name) for field_name in (field_names or col_names) ]) elif django_version >= (1, 2): # Django >= 1.2, < 1.7 used the digest of the name of the first # column. There was no index_together in these releases. name = digest(connection, col_names[0]) else: # Django < 1.2 used just the name of the first column, no digest. name = col_names[0] return truncate_name('%s_%s' % (table, name), connection.ops.max_name_length())