diff --git a/django/db/migrations/autodetector.py b/django/db/migrations/autodetector.py index 0616ea4dbe..00e04fd799 100644 --- a/django/db/migrations/autodetector.py +++ b/django/db/migrations/autodetector.py @@ -122,6 +122,7 @@ class MigrationAutodetector: self.generated_operations = {} self.altered_indexes = {} self.altered_constraints = {} + self.renamed_fields = {} # Prepare some old/new state and model lists, separating # proxy models and ignoring unmigrated apps. @@ -169,6 +170,11 @@ class MigrationAutodetector: self.generate_altered_options() self.generate_altered_managers() + # Create the renamed fields and store them in self.renamed_fields. + # They are used by create_altered_indexes(), generate_altered_fields(), + # generate_removed_altered_index/unique_together(), and + # generate_altered_index/unique_together(). + self.create_renamed_fields() # Create the altered indexes and store them in self.altered_indexes. # This avoids the same computation in generate_removed_indexes() # and generate_added_indexes(). @@ -907,11 +913,12 @@ class MigrationAutodetector: ), ) - def generate_renamed_fields(self): + def create_renamed_fields(self): """Work out renamed fields.""" - self.renamed_fields = {} + self.renamed_operations = [] + old_field_keys = self.old_field_keys.copy() for app_label, model_name, field_name in sorted( - self.new_field_keys - self.old_field_keys + self.new_field_keys - old_field_keys ): old_model_name = self.renamed_models.get( (app_label, model_name), model_name @@ -922,7 +929,7 @@ class MigrationAutodetector: # Scan to see if this is actually a rename! field_dec = self.deep_deconstruct(field) for rem_app_label, rem_model_name, rem_field_name in sorted( - self.old_field_keys - self.new_field_keys + old_field_keys - self.new_field_keys ): if rem_app_label == app_label and rem_model_name == model_name: old_field = old_model_state.get_field(rem_field_name) @@ -947,37 +954,64 @@ class MigrationAutodetector: if self.questioner.ask_rename( model_name, rem_field_name, field_name, field ): - # A db_column mismatch requires a prior noop - # AlterField for the subsequent RenameField to be a - # noop on attempts at preserving the old name. - if old_field.db_column != field.db_column: - altered_field = field.clone() - altered_field.name = rem_field_name - self.add_operation( + self.renamed_operations.append( + ( + rem_app_label, + rem_model_name, + old_field.db_column, + rem_field_name, app_label, - operations.AlterField( - model_name=model_name, - name=rem_field_name, - field=altered_field, - ), + model_name, + field, + field_name, ) - self.add_operation( - app_label, - operations.RenameField( - model_name=model_name, - old_name=rem_field_name, - new_name=field_name, - ), ) - self.old_field_keys.remove( + old_field_keys.remove( (rem_app_label, rem_model_name, rem_field_name) ) - self.old_field_keys.add((app_label, model_name, field_name)) + old_field_keys.add((app_label, model_name, field_name)) self.renamed_fields[ app_label, model_name, field_name ] = rem_field_name break + def generate_renamed_fields(self): + """Generate RenameField operations.""" + for ( + rem_app_label, + rem_model_name, + rem_db_column, + rem_field_name, + app_label, + model_name, + field, + field_name, + ) in self.renamed_operations: + # A db_column mismatch requires a prior noop AlterField for the + # subsequent RenameField to be a noop on attempts at preserving the + # old name. + if rem_db_column != field.db_column: + altered_field = field.clone() + altered_field.name = rem_field_name + self.add_operation( + app_label, + operations.AlterField( + model_name=model_name, + name=rem_field_name, + field=altered_field, + ), + ) + self.add_operation( + app_label, + operations.RenameField( + model_name=model_name, + old_name=rem_field_name, + new_name=field_name, + ), + ) + self.old_field_keys.remove((rem_app_label, rem_model_name, rem_field_name)) + self.old_field_keys.add((app_label, model_name, field_name)) + def generate_added_fields(self): """Make AddField operations.""" for app_label, model_name, field_name in sorted(