18
18
)
19
19
from django import VERSION as django_version
20
20
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
22
22
from django .db .models .sql .where import AND
23
23
from django .db .transaction import TransactionManagementError
24
24
from django .utils .encoding import force_str
@@ -34,6 +34,13 @@ def __hash__(self):
34
34
def __eq__ (self , other ):
35
35
return self .template == other .template and str (self .parts ['name' ]) == str (other .parts ['name' ])
36
36
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 } ]' )
37
44
38
45
class DatabaseSchemaEditor (BaseDatabaseSchemaEditor ):
39
46
@@ -363,7 +370,30 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
363
370
self .execute (self ._delete_constraint_sql (self .sql_delete_check , model , constraint_name ))
364
371
# Have they renamed the column?
365
372
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 ))
366
393
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 } ]' ))
367
397
# Rename all references to the renamed column.
368
398
for sql in self .deferred_sql :
369
399
if isinstance (sql , DjStatement ):
@@ -410,6 +440,8 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
410
440
self ._delete_unique_constraints (model , old_field , new_field , strict )
411
441
# Drop indexes, SQL Server requires explicit deletion
412
442
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 ]), ()))
413
445
# Only if we have a default and there is a change from NULL to NOT NULL
414
446
four_way_default_alteration = (
415
447
new_field .has_default () and
@@ -557,6 +589,10 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
557
589
fragment , other_actions = self ._alter_column_type_sql (
558
590
new_rel .related_model , old_rel .field , new_rel .field , rel_type
559
591
)
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 ))
560
596
self .execute (
561
597
self .sql_alter_column % {
562
598
"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,
566
602
)
567
603
for sql , params in other_actions :
568
604
self .execute (sql , params )
605
+ # Restore related_model indexes
606
+ self .execute (self ._create_index_sql (new_rel .related_model , [new_rel .field ]))
569
607
# Does it have a foreign key?
570
608
if (new_field .remote_field and
571
609
(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,
608
646
609
647
def _delete_indexes (self , model , old_field , new_field ):
610
648
index_columns = []
649
+ if old_field .null != new_field .null :
650
+ index_columns .append ([old_field .column ])
611
651
if old_field .db_index and new_field .db_index :
612
652
index_columns .append ([old_field .column ])
613
653
for fields in model ._meta .index_together :
0 commit comments