@@ -123,6 +123,70 @@ 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
+ sql = self ._create_unique_sql (model , columns , condition = condition )
163
+ output .append (sql )
164
+
165
+ for index in model ._meta .indexes :
166
+ output .append (index .create_sql (model , self ))
167
+ return output
168
+
169
+ def _alter_many_to_many (self , model , old_field , new_field , strict ):
170
+ """Alter M2Ms to repoint their to= endpoints."""
171
+
172
+ for idx in self ._constraint_names (old_field .remote_field .through , index = True , unique = True ):
173
+ self .execute (self .sql_delete_index % {'name' : idx , 'table' : old_field .remote_field .through ._meta .db_table })
174
+
175
+ return super ()._alter_many_to_many (model , old_field , new_field , strict )
176
+
177
+ def alter_db_table (self , model , old_db_table , new_db_table ):
178
+ index_names = self ._constraint_names (model , index = True )
179
+ for index_name in index_names :
180
+ self .execute (self ._delete_constraint_sql (self .sql_delete_index , model , index_name ))
181
+
182
+ model ._meta .db_table = old_db_table
183
+ index_names = self ._constraint_names (model , index = True )
184
+ for index_name in index_names :
185
+ self .execute (self ._delete_constraint_sql (self .sql_delete_index , model , index_name ))
186
+ model ._meta .db_table = new_db_table
187
+
188
+ return super ().alter_db_table (model , old_db_table , new_db_table )
189
+
126
190
def _alter_field (self , model , old_field , new_field , old_type , new_type ,
127
191
old_db_params , new_db_params , strict = False ):
128
192
"""Actually perform a "physical" (non-ManyToMany) field update."""
@@ -223,12 +287,18 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
223
287
for constraint_name in constraint_names :
224
288
self .execute (self ._delete_constraint_sql (self .sql_delete_check , model , constraint_name ))
225
289
# Have they renamed the column?
290
+ old_create_indexes_sql = []
226
291
if old_field .column != new_field .column :
292
+ # remove old indices
293
+ old_create_indexes_sql = self ._model_indexes_sql (model )
294
+ self ._delete_indexes (model , old_field , new_field )
295
+
227
296
self .execute (self ._rename_field_sql (model ._meta .db_table , old_field , new_field , new_type ))
228
297
# Rename all references to the renamed column.
229
298
for sql in self .deferred_sql :
230
299
if isinstance (sql , Statement ):
231
300
sql .rename_column_references (model ._meta .db_table , old_field .column , new_field .column )
301
+
232
302
# Next, start accumulating actions to do
233
303
actions = []
234
304
null_actions = []
@@ -286,6 +356,7 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
286
356
actions = [(", " .join (sql ), sum (params , []))]
287
357
# Apply those actions
288
358
for sql , params in actions :
359
+ self ._delete_indexes (model , old_field , new_field )
289
360
self .execute (
290
361
self .sql_alter_column % {
291
362
"table" : self .quote_name (model ._meta .db_table ),
@@ -438,6 +509,14 @@ def _alter_field(self, model, old_field, new_field, old_type, new_type,
438
509
"changes" : changes_sql ,
439
510
}
440
511
self .execute (sql , params )
512
+
513
+ for o in old_create_indexes_sql :
514
+ o = str (o )
515
+ if old_field .column not in o :
516
+ continue
517
+ o = o .replace ('[%s]' % old_field .column , '[%s]' % new_field .column )
518
+ self .execute (o )
519
+
441
520
# Reset connection if required
442
521
if self .connection .features .connection_persists_old_columns :
443
522
self .connection .close ()
@@ -446,11 +525,15 @@ def _delete_indexes(self, model, old_field, new_field):
446
525
index_columns = []
447
526
if old_field .db_index and new_field .db_index :
448
527
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 )
528
+ for fields in model ._meta .index_together :
529
+ columns = [model ._meta .get_field (field ).column for field in fields ]
530
+ if old_field .column in columns :
531
+ index_columns .append (columns )
532
+
533
+ for fields in model ._meta .unique_together :
534
+ columns = [model ._meta .get_field (field ).column for field in fields ]
535
+ if old_field .column in columns :
536
+ index_columns .append (columns )
454
537
if index_columns :
455
538
for columns in index_columns :
456
539
index_names = self ._constraint_names (model , columns , index = True )
@@ -601,11 +684,6 @@ def create_model(self, model):
601
684
if autoinc_sql :
602
685
self .deferred_sql .extend (autoinc_sql )
603
686
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
687
# Make the table
610
688
sql = self .sql_create_table % {
611
689
"table" : self .quote_name (model ._meta .db_table ),
0 commit comments