Implement primary key changing
This commit is contained in:
parent
d865503389
commit
cd583d6dbd
|
@ -99,7 +99,12 @@ class DatabaseIntrospection(BaseDatabaseIntrospection):
|
||||||
for row in rows:
|
for row in rows:
|
||||||
if row[2] in multicol_indexes:
|
if row[2] in multicol_indexes:
|
||||||
continue
|
continue
|
||||||
indexes[row[4]] = {'primary_key': (row[2] == 'PRIMARY'), 'unique': not bool(row[1])}
|
if row[4] not in indexes:
|
||||||
|
indexes[row[4]] = {'primary_key': False, 'unique': False}
|
||||||
|
if row[2] == 'PRIMARY':
|
||||||
|
indexes[row[4]]['primary_key'] = True
|
||||||
|
if not bool(row[1]):
|
||||||
|
indexes[row[4]]['unique'] = True
|
||||||
return indexes
|
return indexes
|
||||||
|
|
||||||
def get_constraints(self, cursor, table_name):
|
def get_constraints(self, cursor, table_name):
|
||||||
|
|
|
@ -21,3 +21,6 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
|
||||||
|
|
||||||
alter_string_set_null = 'MODIFY %(column)s %(type)s NULL;'
|
alter_string_set_null = 'MODIFY %(column)s %(type)s NULL;'
|
||||||
alter_string_drop_null = 'MODIFY %(column)s %(type)s NOT NULL;'
|
alter_string_drop_null = 'MODIFY %(column)s %(type)s NOT NULL;'
|
||||||
|
|
||||||
|
sql_create_pk = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s PRIMARY KEY (%(columns)s)"
|
||||||
|
sql_delete_pk = "ALTER TABLE %(table)s DROP PRIMARY KEY"
|
||||||
|
|
|
@ -24,8 +24,6 @@ class BaseDatabaseSchemaEditor(object):
|
||||||
- Repointing of FKs
|
- Repointing of FKs
|
||||||
- Repointing of M2Ms
|
- Repointing of M2Ms
|
||||||
- Check constraints (PosIntField)
|
- Check constraints (PosIntField)
|
||||||
- PK changing
|
|
||||||
- db_index on alter field
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Overrideable SQL templates
|
# Overrideable SQL templates
|
||||||
|
@ -57,8 +55,8 @@ class BaseDatabaseSchemaEditor(object):
|
||||||
sql_create_index = "CREATE INDEX %(name)s ON %(table)s (%(columns)s)%(extra)s;"
|
sql_create_index = "CREATE INDEX %(name)s ON %(table)s (%(columns)s)%(extra)s;"
|
||||||
sql_delete_index = "DROP INDEX %(name)s"
|
sql_delete_index = "DROP INDEX %(name)s"
|
||||||
|
|
||||||
sql_create_pk = "ALTER TABLE %(table)s ADD CONSTRAINT %(constraint)s PRIMARY KEY (%(columns)s)"
|
sql_create_pk = "ALTER TABLE %(table)s ADD CONSTRAINT %(name)s PRIMARY KEY (%(columns)s)"
|
||||||
sql_delete_pk = "ALTER TABLE %(table)s DROP CONSTRAINT %(constraint)s"
|
sql_delete_pk = "ALTER TABLE %(table)s DROP CONSTRAINT %(name)s"
|
||||||
|
|
||||||
def __init__(self, connection):
|
def __init__(self, connection):
|
||||||
self.connection = connection
|
self.connection = connection
|
||||||
|
@ -135,9 +133,10 @@ class BaseDatabaseSchemaEditor(object):
|
||||||
elif field.unique:
|
elif field.unique:
|
||||||
sql += " UNIQUE"
|
sql += " UNIQUE"
|
||||||
# If we were told to include a default value, do so
|
# If we were told to include a default value, do so
|
||||||
if include_default:
|
default_value = self.effective_default(field)
|
||||||
|
if include_default and default_value is not None:
|
||||||
sql += " DEFAULT %s"
|
sql += " DEFAULT %s"
|
||||||
params += [self.effective_default(field)]
|
params += [default_value]
|
||||||
# Return the sql
|
# Return the sql
|
||||||
return sql, params
|
return sql, params
|
||||||
|
|
||||||
|
@ -491,6 +490,32 @@ class BaseDatabaseSchemaEditor(object):
|
||||||
"extra": "",
|
"extra": "",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
# Changed to become primary key?
|
||||||
|
# Note that we don't detect unsetting of a PK, as we assume another field
|
||||||
|
# will always come along and replace it.
|
||||||
|
if not old_field.primary_key and new_field.primary_key:
|
||||||
|
# First, drop the old PK
|
||||||
|
constraint_names = self._constraint_names(model, primary_key=True)
|
||||||
|
if strict and len(constraint_names) != 1:
|
||||||
|
raise ValueError("Found wrong number (%s) of PK constraints for %s" % (
|
||||||
|
len(constraint_names),
|
||||||
|
model._meta.db_table,
|
||||||
|
))
|
||||||
|
for constraint_name in constraint_names:
|
||||||
|
self.execute(
|
||||||
|
self.sql_delete_pk % {
|
||||||
|
"table": self.quote_name(model._meta.db_table),
|
||||||
|
"name": constraint_name,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
# Make the new one
|
||||||
|
self.execute(
|
||||||
|
self.sql_create_pk % {
|
||||||
|
"table": self.quote_name(model._meta.db_table),
|
||||||
|
"name": self._create_index_name(model, [new_field.column], suffix="_pk"),
|
||||||
|
"columns": self.quote_name(new_field.column),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
def _type_for_alter(self, field):
|
def _type_for_alter(self, field):
|
||||||
"""
|
"""
|
||||||
|
@ -518,16 +543,16 @@ class BaseDatabaseSchemaEditor(object):
|
||||||
index_name = '%s%s' % (table_name[:(self.connection.features.max_index_name_length - len(part))], part)
|
index_name = '%s%s' % (table_name[:(self.connection.features.max_index_name_length - len(part))], part)
|
||||||
return index_name
|
return index_name
|
||||||
|
|
||||||
def _constraint_names(self, model, column_names, unique=None, primary_key=None, index=None):
|
def _constraint_names(self, model, column_names=None, unique=None, primary_key=None, index=None):
|
||||||
"Returns all constraint names matching the columns and conditions"
|
"Returns all constraint names matching the columns and conditions"
|
||||||
column_names = set(column_names)
|
column_names = set(column_names) if column_names else None
|
||||||
constraints = self.connection.introspection.get_constraints(self.connection.cursor(), model._meta.db_table)
|
constraints = self.connection.introspection.get_constraints(self.connection.cursor(), model._meta.db_table)
|
||||||
result = []
|
result = []
|
||||||
for name, infodict in constraints.items():
|
for name, infodict in constraints.items():
|
||||||
if column_names == infodict['columns']:
|
if column_names is None or column_names == infodict['columns']:
|
||||||
if unique is not None and infodict['unique'] != unique:
|
if unique is not None and infodict['unique'] != unique:
|
||||||
continue
|
continue
|
||||||
if primary_key is not None and infodict['primary_key'] != unique:
|
if primary_key is not None and infodict['primary_key'] != primary_key:
|
||||||
continue
|
continue
|
||||||
if index is not None and infodict['index'] != index:
|
if index is not None and infodict['index'] != index:
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -478,3 +478,37 @@ class SchemaTests(TestCase):
|
||||||
"slug",
|
"slug",
|
||||||
connection.introspection.get_indexes(connection.cursor(), Book._meta.db_table),
|
connection.introspection.get_indexes(connection.cursor(), Book._meta.db_table),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_primary_key(self):
|
||||||
|
"""
|
||||||
|
Tests altering of the primary key
|
||||||
|
"""
|
||||||
|
# Create the table
|
||||||
|
editor = connection.schema_editor()
|
||||||
|
editor.start()
|
||||||
|
editor.create_model(Tag)
|
||||||
|
editor.commit()
|
||||||
|
# Ensure the table is there and has the right PK
|
||||||
|
self.assertTrue(
|
||||||
|
connection.introspection.get_indexes(connection.cursor(), Tag._meta.db_table)['id']['primary_key'],
|
||||||
|
)
|
||||||
|
# Alter to change the PK
|
||||||
|
new_field = SlugField(primary_key=True)
|
||||||
|
new_field.set_attributes_from_name("slug")
|
||||||
|
editor = connection.schema_editor()
|
||||||
|
editor.start()
|
||||||
|
editor.delete_field(Tag, Tag._meta.get_field_by_name("id")[0])
|
||||||
|
editor.alter_field(
|
||||||
|
Tag,
|
||||||
|
Tag._meta.get_field_by_name("slug")[0],
|
||||||
|
new_field,
|
||||||
|
)
|
||||||
|
editor.commit()
|
||||||
|
# Ensure the PK changed
|
||||||
|
self.assertNotIn(
|
||||||
|
'id',
|
||||||
|
connection.introspection.get_indexes(connection.cursor(), Tag._meta.db_table),
|
||||||
|
)
|
||||||
|
self.assertTrue(
|
||||||
|
connection.introspection.get_indexes(connection.cursor(), Tag._meta.db_table)['slug']['primary_key'],
|
||||||
|
)
|
||||||
|
|
Loading…
Reference in New Issue