Loading django/db/backends/mysql/introspection.py +36 −26 Original line number Diff line number Diff line Loading @@ -105,32 +105,42 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): def get_constraints(self, cursor, table_name): """ Retrieves any constraints (unique, pk, fk, check) across one or more columns. Returns {'cnname': {'columns': set(columns), 'primary_key': bool, 'unique': bool}} Returns {'cnname': {'columns': set(columns), 'primary_key': bool, 'unique': bool, 'foreign_key': None|(tbl, col)}} """ constraints = {} # Loop over the constraint tables, collecting things as constraints ifsc_tables = ["constraint_column_usage", "key_column_usage"] for ifsc_table in ifsc_tables: cursor.execute(""" SELECT kc.constraint_name, kc.column_name, c.constraint_type FROM information_schema.%s AS kc JOIN information_schema.table_constraints AS c ON kc.table_schema = c.table_schema AND kc.table_name = c.table_name AND kc.constraint_name = c.constraint_name # Get the actual constraint names and columns name_query = """ SELECT kc.`constraint_name`, kc.`column_name`, kc.`referenced_table_name`, kc.`referenced_column_name` FROM information_schema.key_column_usage AS kc WHERE kc.table_schema = %%s AND kc.table_name = %%s """ % ifsc_table, [self.connection.settings_dict['NAME'], table_name]) for constraint, column, kind in cursor.fetchall(): # If we're the first column, make the record kc.table_schema = %s AND kc.table_name = %s """ cursor.execute(name_query, [self.connection.settings_dict['NAME'], table_name]) for constraint, column, ref_table, ref_column in cursor.fetchall(): if constraint not in constraints: constraints[constraint] = { "columns": set(), "primary_key": kind.lower() == "primary key", "foreign_key": kind.lower() == "foreign key", "unique": kind.lower() in ["primary key", "unique"], 'columns': set(), 'primary_key': False, 'unique': False, 'foreign_key': (ref_table, ref_column) if ref_column else None, } # Record the details constraints[constraint]['columns'].add(column) # Now get the constraint types type_query = """ SELECT c.constraint_name, c.constraint_type FROM information_schema.table_constraints AS c WHERE c.table_schema = %s AND c.table_name = %s """ cursor.execute(type_query, [self.connection.settings_dict['NAME'], table_name]) for constraint, kind in cursor.fetchall(): if kind.lower() == "primary key": constraints[constraint]['primary_key'] = True constraints[constraint]['unique'] = True elif kind.lower() == "unique": constraints[constraint]['unique'] = True # Return return constraints django/db/backends/mysql/schema.py +2 −3 Original line number Diff line number Diff line Loading @@ -7,6 +7,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_alter_column_null = "MODIFY %(column)s %(type)s NULL" sql_alter_column_not_null = "MODIFY %(column)s %(type)s NULL" sql_alter_column_type = "MODIFY %(column)s %(type)s" sql_rename_column = "ALTER TABLE %(table)s CHANGE %(old_column)s %(new_column)s %(type)s" sql_delete_unique = "ALTER TABLE %(table)s DROP INDEX %(name)s" Loading @@ -17,8 +19,5 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_delete_pk = "ALTER TABLE %(table)s DROP PRIMARY KEY" alter_string_set_null = 'MODIFY %(column)s %(type)s NULL;' alter_string_drop_null = 'MODIFY %(column)s %(type)s NOT NULL;' django/db/backends/schema.py +13 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ class BaseDatabaseSchemaEditor(object): - Repointing of M2Ms - Check constraints (PosIntField) - PK changing - db_index on alter field """ # Overrideable SQL templates Loading Loading @@ -358,7 +359,7 @@ class BaseDatabaseSchemaEditor(object): """ # Ensure this field is even column-based old_type = old_field.db_type(connection=self.connection) new_type = new_field.db_type(connection=self.connection) new_type = self._type_for_alter(new_field) if old_type is None and new_type is None: # TODO: Handle M2M fields being repointed return Loading Loading @@ -389,6 +390,7 @@ class BaseDatabaseSchemaEditor(object): "table": self.quote_name(model._meta.db_table), "old_column": self.quote_name(old_field.column), "new_column": self.quote_name(new_field.column), "type": new_type, }) # Next, start accumulating actions to do actions = [] Loading Loading @@ -426,6 +428,7 @@ class BaseDatabaseSchemaEditor(object): actions.append(( self.sql_alter_column_null % { "column": self.quote_name(new_field.column), "type": new_type, }, [], )) Loading @@ -433,6 +436,7 @@ class BaseDatabaseSchemaEditor(object): actions.append(( self.sql_alter_column_null % { "column": self.quote_name(new_field.column), "type": new_type, }, [], )) Loading Loading @@ -460,6 +464,14 @@ class BaseDatabaseSchemaEditor(object): } ) def _type_for_alter(self, field): """ Returns a field's type suitable for ALTER COLUMN. By default it just returns field.db_type(). To be overriden by backend specific subclasses """ return field.db_type(connection=self.connection) def _create_index_name(self, model, column_names, suffix=""): "Generates a unique name for an index/unique constraint." # If there is just one column in the index, use a default algorithm from Django Loading tests/modeltests/schema/tests.py +2 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ class SchemaTests(TestCase): connection.rollback() # Delete any tables made for our models cursor = connection.cursor() connection.disable_constraint_checking() for model in self.models: # Remove any M2M tables first for field in model._meta.local_many_to_many: Loading @@ -59,6 +60,7 @@ class SchemaTests(TestCase): connection.rollback() else: connection.commit() connection.enable_constraint_checking() # Unhook our models for model in self.models: model._meta.managed = False Loading Loading
django/db/backends/mysql/introspection.py +36 −26 Original line number Diff line number Diff line Loading @@ -105,32 +105,42 @@ class DatabaseIntrospection(BaseDatabaseIntrospection): def get_constraints(self, cursor, table_name): """ Retrieves any constraints (unique, pk, fk, check) across one or more columns. Returns {'cnname': {'columns': set(columns), 'primary_key': bool, 'unique': bool}} Returns {'cnname': {'columns': set(columns), 'primary_key': bool, 'unique': bool, 'foreign_key': None|(tbl, col)}} """ constraints = {} # Loop over the constraint tables, collecting things as constraints ifsc_tables = ["constraint_column_usage", "key_column_usage"] for ifsc_table in ifsc_tables: cursor.execute(""" SELECT kc.constraint_name, kc.column_name, c.constraint_type FROM information_schema.%s AS kc JOIN information_schema.table_constraints AS c ON kc.table_schema = c.table_schema AND kc.table_name = c.table_name AND kc.constraint_name = c.constraint_name # Get the actual constraint names and columns name_query = """ SELECT kc.`constraint_name`, kc.`column_name`, kc.`referenced_table_name`, kc.`referenced_column_name` FROM information_schema.key_column_usage AS kc WHERE kc.table_schema = %%s AND kc.table_name = %%s """ % ifsc_table, [self.connection.settings_dict['NAME'], table_name]) for constraint, column, kind in cursor.fetchall(): # If we're the first column, make the record kc.table_schema = %s AND kc.table_name = %s """ cursor.execute(name_query, [self.connection.settings_dict['NAME'], table_name]) for constraint, column, ref_table, ref_column in cursor.fetchall(): if constraint not in constraints: constraints[constraint] = { "columns": set(), "primary_key": kind.lower() == "primary key", "foreign_key": kind.lower() == "foreign key", "unique": kind.lower() in ["primary key", "unique"], 'columns': set(), 'primary_key': False, 'unique': False, 'foreign_key': (ref_table, ref_column) if ref_column else None, } # Record the details constraints[constraint]['columns'].add(column) # Now get the constraint types type_query = """ SELECT c.constraint_name, c.constraint_type FROM information_schema.table_constraints AS c WHERE c.table_schema = %s AND c.table_name = %s """ cursor.execute(type_query, [self.connection.settings_dict['NAME'], table_name]) for constraint, kind in cursor.fetchall(): if kind.lower() == "primary key": constraints[constraint]['primary_key'] = True constraints[constraint]['unique'] = True elif kind.lower() == "unique": constraints[constraint]['unique'] = True # Return return constraints
django/db/backends/mysql/schema.py +2 −3 Original line number Diff line number Diff line Loading @@ -7,6 +7,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_alter_column_null = "MODIFY %(column)s %(type)s NULL" sql_alter_column_not_null = "MODIFY %(column)s %(type)s NULL" sql_alter_column_type = "MODIFY %(column)s %(type)s" sql_rename_column = "ALTER TABLE %(table)s CHANGE %(old_column)s %(new_column)s %(type)s" sql_delete_unique = "ALTER TABLE %(table)s DROP INDEX %(name)s" Loading @@ -17,8 +19,5 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): sql_delete_pk = "ALTER TABLE %(table)s DROP PRIMARY KEY" alter_string_set_null = 'MODIFY %(column)s %(type)s NULL;' alter_string_drop_null = 'MODIFY %(column)s %(type)s NOT NULL;'
django/db/backends/schema.py +13 −1 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ class BaseDatabaseSchemaEditor(object): - Repointing of M2Ms - Check constraints (PosIntField) - PK changing - db_index on alter field """ # Overrideable SQL templates Loading Loading @@ -358,7 +359,7 @@ class BaseDatabaseSchemaEditor(object): """ # Ensure this field is even column-based old_type = old_field.db_type(connection=self.connection) new_type = new_field.db_type(connection=self.connection) new_type = self._type_for_alter(new_field) if old_type is None and new_type is None: # TODO: Handle M2M fields being repointed return Loading Loading @@ -389,6 +390,7 @@ class BaseDatabaseSchemaEditor(object): "table": self.quote_name(model._meta.db_table), "old_column": self.quote_name(old_field.column), "new_column": self.quote_name(new_field.column), "type": new_type, }) # Next, start accumulating actions to do actions = [] Loading Loading @@ -426,6 +428,7 @@ class BaseDatabaseSchemaEditor(object): actions.append(( self.sql_alter_column_null % { "column": self.quote_name(new_field.column), "type": new_type, }, [], )) Loading @@ -433,6 +436,7 @@ class BaseDatabaseSchemaEditor(object): actions.append(( self.sql_alter_column_null % { "column": self.quote_name(new_field.column), "type": new_type, }, [], )) Loading Loading @@ -460,6 +464,14 @@ class BaseDatabaseSchemaEditor(object): } ) def _type_for_alter(self, field): """ Returns a field's type suitable for ALTER COLUMN. By default it just returns field.db_type(). To be overriden by backend specific subclasses """ return field.db_type(connection=self.connection) def _create_index_name(self, model, column_names, suffix=""): "Generates a unique name for an index/unique constraint." # If there is just one column in the index, use a default algorithm from Django Loading
tests/modeltests/schema/tests.py +2 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ class SchemaTests(TestCase): connection.rollback() # Delete any tables made for our models cursor = connection.cursor() connection.disable_constraint_checking() for model in self.models: # Remove any M2M tables first for field in model._meta.local_many_to_many: Loading @@ -59,6 +60,7 @@ class SchemaTests(TestCase): connection.rollback() else: connection.commit() connection.enable_constraint_checking() # Unhook our models for model in self.models: model._meta.managed = False Loading