@@ -123,6 +123,24 @@ def _alter_column_type_sql(self, model, old_field, new_field, new_type):
123
123
new_type = self ._set_field_new_type_null_status (old_field , new_type )
124
124
return super ()._alter_column_type_sql (model , old_field , new_field , new_type )
125
125
126
+ def alter_unique_together (self , model , old_unique_together , new_unique_together ):
127
+ """
128
+ Deal with a model changing its unique_together. The input
129
+ unique_togethers must be doubly-nested, not the single-nested
130
+ ["foo", "bar"] format.
131
+ """
132
+ olds = {tuple (fields ) for fields in old_unique_together }
133
+ news = {tuple (fields ) for fields in new_unique_together }
134
+ # Deleted uniques
135
+ for fields in olds .difference (news ):
136
+ self ._delete_composed_index (model , fields , {'unique' : True }, self .sql_delete_index )
137
+ # Created uniques
138
+ for fields in news .difference (olds ):
139
+ columns = [model ._meta .get_field (field ).column for field in fields ]
140
+ condition = ' AND ' .join (["[%s] IS NOT NULL" % col for col in columns ])
141
+ sql = self ._create_unique_sql (model , columns , condition = condition )
142
+ self .execute (sql )
143
+
126
144
def _alter_field (self , model , old_field , new_field , old_type , new_type ,
127
145
old_db_params , new_db_params , strict = False ):
128
146
"""Actually perform a "physical" (non-ManyToMany) field update."""
@@ -223,12 +241,18 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
223
241
for constraint_name in constraint_names :
224
242
self .execute (self ._delete_constraint_sql (self .sql_delete_check , model , constraint_name ))
225
243
# Have they renamed the column?
244
+ old_create_indexes_sql = []
226
245
if old_field .column != new_field .column :
246
+ # remove old indices
247
+ old_create_indexes_sql = self ._model_indexes_sql (model )
248
+ self ._delete_indexes (model , old_field , new_field )
249
+
227
250
self .execute (self ._rename_field_sql (model ._meta .db_table , old_field , new_field , new_type ))
228
251
# Rename all references to the renamed column.
229
252
for sql in self .deferred_sql :
230
253
if isinstance (sql , Statement ):
231
254
sql .rename_column_references (model ._meta .db_table , old_field .column , new_field .column )
255
+
232
256
# Next, start accumulating actions to do
233
257
actions = []
234
258
null_actions = []
@@ -286,6 +310,7 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
286
310
actions = [(", " .join (sql ), sum (params , []))]
287
311
# Apply those actions
288
312
for sql , params in actions :
313
+ self ._delete_indexes (model , old_field , new_field )
289
314
self .execute (
290
315
self .sql_alter_column % {
291
316
"table" : self .quote_name (model ._meta .db_table ),
@@ -438,6 +463,14 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
438
463
"changes" : changes_sql ,
439
464
}
440
465
self .execute (sql , params )
466
+
467
+ for o in old_create_indexes_sql :
468
+ o = str (o )
469
+ if old_field .column not in o :
470
+ continue
471
+ o = o .replace ('[%s]' % old_field .column , '[%s]' % new_field .column )
472
+ self .execute (o )
473
+
441
474
# Reset connection if required
442
475
if self .connection .features .connection_persists_old_columns :
443
476
self .connection .close ()
@@ -446,11 +479,14 @@ def _delete_indexes(self, model, old_field, new_field):
446
479
index_columns = []
447
480
if old_field .db_index and new_field .db_index :
448
481
index_columns .append ([old_field .column ])
449
- else :
450
- for fields in model ._meta .index_together :
451
- columns = [model ._meta .get_field (field ).column for field in fields ]
452
- if old_field .column in columns :
453
- index_columns .append (columns )
482
+ for fields in model ._meta .index_together :
483
+ columns = [model ._meta .get_field (field ).column for field in fields ]
484
+ if old_field .column in columns :
485
+ index_columns .append (columns )
486
+
487
+ for fields in model ._meta .unique_together :
488
+ columns = [model ._meta .get_field (field ).column for field in fields ]
489
+ index_columns .append (columns )
454
490
if index_columns :
455
491
for columns in index_columns :
456
492
index_names = self ._constraint_names (model , columns , index = True )
@@ -605,7 +641,10 @@ def create_model(self, model):
605
641
# created afterwards, like geometry fields with some backends)
606
642
for fields in model ._meta .unique_together :
607
643
columns = [model ._meta .get_field (field ).column for field in fields ]
608
- self .deferred_sql .append (self ._create_unique_sql (model , columns ))
644
+ condition = ' AND ' .join (["[%s] IS NOT NULL" % col for col in columns ])
645
+ sql_without_condition = self ._create_unique_sql (model , columns )
646
+ sql_with_condition = self ._create_unique_sql (model , columns , condition = condition )
647
+ self .deferred_sql .append (sql_with_condition )
609
648
# Make the table
610
649
sql = self .sql_create_table % {
611
650
"table" : self .quote_name (model ._meta .db_table ),
0 commit comments