@@ -123,6 +123,56 @@ 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
+
144
+ def _model_indexes_sql (self , model ):
145
+ """
146
+ Return a list of all index SQL statements (field indexes,
147
+ index_together, Meta.indexes) for the specified model.
148
+ """
149
+ if not model ._meta .managed or model ._meta .proxy or model ._meta .swapped :
150
+ return []
151
+ output = []
152
+ for field in model ._meta .local_fields :
153
+ output .extend (self ._field_indexes_sql (model , field ))
154
+
155
+ for field_names in model ._meta .index_together :
156
+ fields = [model ._meta .get_field (field ) for field in field_names ]
157
+ output .append (self ._create_index_sql (model , fields , suffix = "_idx" ))
158
+
159
+ for field_names in model ._meta .unique_together :
160
+ columns = [model ._meta .get_field (field ).column for field in field_names ]
161
+ condition = ' AND ' .join (["[%s] IS NOT NULL" % col for col in columns ])
162
+ output .append (self ._create_unique_sql (model , columns , condition = condition ))
163
+
164
+ for index in model ._meta .indexes :
165
+ output .append (index .create_sql (model , self ))
166
+ return output
167
+
168
+ def _alter_many_to_many (self , model , old_field , new_field , strict ):
169
+ """Alter M2Ms to repoint their to= endpoints."""
170
+
171
+ for idx in self ._constraint_names (old_field .remote_field .through , index = True , unique = True ):
172
+ self .execute (self .sql_delete_index % {'name' : idx , 'table' : old_field .remote_field .through ._meta .db_table })
173
+
174
+ return super ()._alter_many_to_many (model , old_field , new_field , strict )
175
+
126
176
def _alter_field (self , model , old_field , new_field , old_type , new_type ,
127
177
old_db_params , new_db_params , strict = False ):
128
178
"""Actually perform a "physical" (non-ManyToMany) field update."""
@@ -223,12 +273,18 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
223
273
for constraint_name in constraint_names :
224
274
self .execute (self ._delete_constraint_sql (self .sql_delete_check , model , constraint_name ))
225
275
# Have they renamed the column?
276
+ old_create_indexes_sql = []
226
277
if old_field .column != new_field .column :
278
+ # remove old indices
279
+ old_create_indexes_sql = self ._model_indexes_sql (model )
280
+ self ._delete_indexes (model , old_field , new_field )
281
+
227
282
self .execute (self ._rename_field_sql (model ._meta .db_table , old_field , new_field , new_type ))
228
283
# Rename all references to the renamed column.
229
284
for sql in self .deferred_sql :
230
285
if isinstance (sql , Statement ):
231
286
sql .rename_column_references (model ._meta .db_table , old_field .column , new_field .column )
287
+
232
288
# Next, start accumulating actions to do
233
289
actions = []
234
290
null_actions = []
@@ -286,6 +342,7 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
286
342
actions = [(", " .join (sql ), sum (params , []))]
287
343
# Apply those actions
288
344
for sql , params in actions :
345
+ self ._delete_indexes (model , old_field , new_field )
289
346
self .execute (
290
347
self .sql_alter_column % {
291
348
"table" : self .quote_name (model ._meta .db_table ),
@@ -438,6 +495,14 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
438
495
"changes" : changes_sql ,
439
496
}
440
497
self .execute (sql , params )
498
+
499
+ for o in old_create_indexes_sql :
500
+ o = str (o )
501
+ if old_field .column not in o :
502
+ continue
503
+ o = o .replace ('[%s]' % old_field .column , '[%s]' % new_field .column )
504
+ self .execute (o )
505
+
441
506
# Reset connection if required
442
507
if self .connection .features .connection_persists_old_columns :
443
508
self .connection .close ()
@@ -446,11 +511,14 @@ def _delete_indexes(self, model, old_field, new_field):
446
511
index_columns = []
447
512
if old_field .db_index and new_field .db_index :
448
513
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 )
514
+ for fields in model ._meta .index_together :
515
+ columns = [model ._meta .get_field (field ).column for field in fields ]
516
+ if old_field .column in columns :
517
+ index_columns .append (columns )
518
+
519
+ for fields in model ._meta .unique_together :
520
+ columns = [model ._meta .get_field (field ).column for field in fields ]
521
+ index_columns .append (columns )
454
522
if index_columns :
455
523
for columns in index_columns :
456
524
index_names = self ._constraint_names (model , columns , index = True )
@@ -461,11 +529,6 @@ def _delete_unique_constraints(self, model, old_field, new_field, strict=False):
461
529
unique_columns = []
462
530
if old_field .unique and new_field .unique :
463
531
unique_columns .append ([old_field .column ])
464
- else :
465
- for fields in model ._meta .unique_together :
466
- columns = [model ._meta .get_field (field ).column for field in fields ]
467
- if old_field .column in columns :
468
- unique_columns .append (columns )
469
532
if unique_columns :
470
533
for columns in unique_columns :
471
534
constraint_names = self ._constraint_names (model , columns , unique = True )
@@ -601,11 +664,6 @@ def create_model(self, model):
601
664
if autoinc_sql :
602
665
self .deferred_sql .extend (autoinc_sql )
603
666
604
- # Add any unique_togethers (always deferred, as some fields might be
605
- # created afterwards, like geometry fields with some backends)
606
- for fields in model ._meta .unique_together :
607
- columns = [model ._meta .get_field (field ).column for field in fields ]
608
- self .deferred_sql .append (self ._create_unique_sql (model , columns ))
609
667
# Make the table
610
668
sql = self .sql_create_table % {
611
669
"table" : self .quote_name (model ._meta .db_table ),
0 commit comments