Skip to content

Commit 6048db2

Browse files
committed
Fix migration alter field
Related to #14, #67
1 parent 3883ac3 commit 6048db2

File tree

2 files changed

+41
-10
lines changed

2 files changed

+41
-10
lines changed

mssql/schema.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
)
1919
from django import VERSION as django_version
2020
from django.db.models import Index, UniqueConstraint
21-
from django.db.models.fields import AutoField, BigAutoField
21+
from django.db.models.fields import AutoField, BigAutoField, TextField
2222
from django.db.models.sql.where import AND
2323
from django.db.transaction import TransactionManagementError
2424
from django.utils.encoding import force_str
@@ -34,6 +34,13 @@ def __hash__(self):
3434
def __eq__(self, other):
3535
return self.template == other.template and str(self.parts['name']) == str(other.parts['name'])
3636

37+
def rename_column_references(self, table, old_column, new_column):
38+
for part in self.parts.values():
39+
if hasattr(part, 'rename_column_references'):
40+
part.rename_column_references(table, old_column, new_column)
41+
condition = self.parts['condition']
42+
if condition:
43+
self.parts['condition'] = condition.replace(f'[{old_column}]', f'[{new_column}]')
3744

3845
class DatabaseSchemaEditor(BaseDatabaseSchemaEditor):
3946

@@ -363,7 +370,30 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
363370
self.execute(self._delete_constraint_sql(self.sql_delete_check, model, constraint_name))
364371
# Have they renamed the column?
365372
if old_field.column != new_field.column:
373+
sql_restore_index = ''
374+
# Drop unique indexes for table to be altered
375+
index_names = self._db_table_constraint_names(model._meta.db_table, index=True)
376+
for index_name in index_names:
377+
if(index_name.endswith('uniq')):
378+
with self.connection.cursor() as cursor:
379+
cursor.execute(f"""
380+
SELECT COL_NAME(ic.object_id,ic.column_id) AS column_name,
381+
filter_definition
382+
FROM sys.indexes AS i
383+
INNER JOIN sys.index_columns AS ic
384+
ON i.object_id = ic.object_id AND i.index_id = ic.index_id
385+
WHERE i.object_id = OBJECT_ID('{model._meta.db_table}')
386+
and i.name = '{index_name}'
387+
""")
388+
result = cursor.fetchall()
389+
columns_to_recreate_index = ', '.join(['%s' % self.quote_name(column[0]) for column in result])
390+
filter_definition = result[0][1]
391+
sql_restore_index += f'CREATE UNIQUE INDEX {index_name} ON {model._meta.db_table} ({columns_to_recreate_index}) WHERE {filter_definition};'
392+
self.execute(self._db_table_delete_constraint_sql(self.sql_delete_index, model._meta.db_table, index_name))
366393
self.execute(self._rename_field_sql(model._meta.db_table, old_field, new_field, new_type))
394+
# Restore indexes for altered table
395+
if(sql_restore_index):
396+
self.execute(sql_restore_index.replace(f'[{old_field.column}]', f'[{new_field.column}]'))
367397
# Rename all references to the renamed column.
368398
for sql in self.deferred_sql:
369399
if isinstance(sql, DjStatement):
@@ -410,6 +440,8 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
410440
self._delete_unique_constraints(model, old_field, new_field, strict)
411441
# Drop indexes, SQL Server requires explicit deletion
412442
self._delete_indexes(model, old_field, new_field)
443+
if not isinstance(new_field, TextField):
444+
post_actions.append((self._create_index_sql(model, [new_field]), ()))
413445
# Only if we have a default and there is a change from NULL to NOT NULL
414446
four_way_default_alteration = (
415447
new_field.has_default() and
@@ -557,6 +589,10 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
557589
fragment, other_actions = self._alter_column_type_sql(
558590
new_rel.related_model, old_rel.field, new_rel.field, rel_type
559591
)
592+
# Drop related_model indexes, so it can be altered
593+
index_names = self._db_table_constraint_names(old_rel.related_model._meta.db_table, index=True)
594+
for index_name in index_names:
595+
self.execute(self._db_table_delete_constraint_sql(self.sql_delete_index, old_rel.related_model._meta.db_table, index_name))
560596
self.execute(
561597
self.sql_alter_column % {
562598
"table": self.quote_name(new_rel.related_model._meta.db_table),
@@ -566,6 +602,8 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
566602
)
567603
for sql, params in other_actions:
568604
self.execute(sql, params)
605+
# Restore related_model indexes
606+
self.execute(self._create_index_sql(new_rel.related_model, [new_rel.field]))
569607
# Does it have a foreign key?
570608
if (new_field.remote_field and
571609
(fks_dropped or not old_field.remote_field or not old_field.db_constraint) and
@@ -608,6 +646,8 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
608646

609647
def _delete_indexes(self, model, old_field, new_field):
610648
index_columns = []
649+
if old_field.null != new_field.null:
650+
index_columns.append([old_field.column])
611651
if old_field.db_index and new_field.db_index:
612652
index_columns.append([old_field.column])
613653
for fields in model._meta.index_together:

testapp/settings.py

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,8 @@
6767
'migrations.test_executor.ExecutorTests.test_alter_id_type_with_fk',
6868
'migrations.test_operations.OperationTests.test_add_constraint_percent_escaping',
6969
'migrations.test_operations.OperationTests.test_alter_field_pk',
70-
'migrations.test_operations.OperationTests.test_alter_field_reloads_state_on_fk_target_changes',
7170
'migrations.test_operations.OperationTests.test_alter_field_reloads_state_on_fk_with_to_field_target_changes',
72-
'migrations.test_operations.OperationTests.test_alter_field_with_index',
7371
'migrations.test_operations.OperationTests.test_autofield_foreignfield_growth',
74-
'migrations.test_operations.OperationTests.test_rename_field_reloads_state_on_fk_target_changes',
75-
'migrations.test_operations.OperationTests.test_rename_m2m_model_after_rename_field',
76-
'migrations.test_operations.OperationTests.test_rename_model_with_superclass_fk',
77-
'migrations.test_operations.OperationTests.test_rename_referenced_field_state_forward',
7872
'schema.tests.SchemaTests.test_alter_auto_field_to_char_field',
7973
'schema.tests.SchemaTests.test_alter_auto_field_to_integer_field',
8074
'schema.tests.SchemaTests.test_alter_implicit_id_to_explicit',
@@ -107,8 +101,6 @@
107101
'migrations.test_commands.MigrateTests.test_migrate_syncdb_app_label',
108102
'migrations.test_commands.MigrateTests.test_migrate_syncdb_deferred_sql_executed_with_schemaeditor',
109103
'migrations.test_operations.OperationTests.test_alter_field_pk_fk',
110-
'migrations.test_operations.OperationTests.test_create_model_with_partial_unique_constraint',
111-
'migrations.test_operations.OperationTests.test_rename_field',
112104
'schema.tests.SchemaTests.test_add_foreign_key_quoted_db_table',
113105
'schema.tests.SchemaTests.test_unique_and_reverse_m2m',
114106
'schema.tests.SchemaTests.test_unique_no_unnecessary_fk_drops',
@@ -179,7 +171,6 @@
179171
'dbshell.tests.DbshellCommandTestCase.test_command_missing',
180172
'schema.tests.SchemaTests.test_char_field_pk_to_auto_field',
181173
'datetimes.tests.DateTimesTests.test_21432',
182-
'migrations.test_operations.OperationTests.test_rename_model_with_m2m',
183174

184175
# JSONFields
185176
'model_fields.test_jsonfield.TestQuerying.test_has_key_list',

0 commit comments

Comments
 (0)