From 2b7b199390336f7068ef8cb8d67b2e83fb6b818e Mon Sep 17 00:00:00 2001 From: Jeff Date: Wed, 23 May 2018 15:55:27 -0400 Subject: [PATCH] [2.1.x] Fixed #29000 -- Fixed RenameModel's renaming of a M2M column when run after RenameField. Regression in 45ded053b1f4320284aa5dac63052f6d1baefea9. Backport of fcc4e251dbc917118f73d7187ee2f4cbf3883f36 from master --- django/db/migrations/operations/fields.py | 13 ++++++----- tests/migrations/test_operations.py | 28 +++++++++++++++++++++++ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/django/db/migrations/operations/fields.py b/django/db/migrations/operations/fields.py index 96b0045744a..a41b3444e50 100644 --- a/django/db/migrations/operations/fields.py +++ b/django/db/migrations/operations/fields.py @@ -271,15 +271,10 @@ class RenameField(FieldOperation): # Rename the field fields = model_state.fields found = False + delay = True for index, (name, field) in enumerate(fields): if not found and name == self.old_name: fields[index] = (self.new_name, field) - # Delay rendering of relationships if it's not a relational - # field and not referenced by a foreign key. - delay = ( - not field.is_relation and - not is_referenced_by_foreign_key(state, self.model_name_lower, field, self.name) - ) found = True # Fix from_fields to refer to the new field. from_fields = getattr(field, 'from_fields', None) @@ -288,6 +283,12 @@ class RenameField(FieldOperation): self.new_name if from_field_name == self.old_name else from_field_name for from_field_name in from_fields ]) + # Delay rendering of relationships if it's not a relational + # field and not referenced by a foreign key. + delay = delay and ( + not field.is_relation and + not is_referenced_by_foreign_key(state, self.model_name_lower, field, self.name) + ) if not found: raise FieldDoesNotExist( "%s.%s has no field named '%s'" % (app_label, self.model_name, self.old_name) diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index b5c9fb1b3dd..d70feaacdb9 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -802,6 +802,34 @@ class OperationTests(OperationTestBase): self.assertEqual(PonyRider.objects.count(), 2) self.assertEqual(pony.riders.count(), 2) + def test_rename_m2m_model_after_rename_field(self): + """RenameModel renames a many-to-many column after a RenameField.""" + app_label = 'test_rename_multiple' + project_state = self.apply_operations(app_label, ProjectState(), operations=[ + migrations.CreateModel('Pony', fields=[ + ('id', models.AutoField(primary_key=True)), + ('name', models.CharField(max_length=20)), + ]), + migrations.CreateModel('Rider', fields=[ + ('id', models.AutoField(primary_key=True)), + ('pony', models.ForeignKey('test_rename_multiple.Pony', models.CASCADE)), + ]), + migrations.CreateModel('PonyRider', fields=[ + ('id', models.AutoField(primary_key=True)), + ('riders', models.ManyToManyField('Rider')), + ]), + migrations.RenameField(model_name='pony', old_name='name', new_name='fancy_name'), + migrations.RenameModel(old_name='Rider', new_name='Jockey'), + ], atomic=connection.features.supports_atomic_references_rename) + Pony = project_state.apps.get_model(app_label, 'Pony') + Jockey = project_state.apps.get_model(app_label, 'Jockey') + PonyRider = project_state.apps.get_model(app_label, 'PonyRider') + # No "no such column" error means the column was renamed correctly. + pony = Pony.objects.create(fancy_name='a good name') + jockey = Jockey.objects.create(pony=pony) + ponyrider = PonyRider.objects.create() + ponyrider.riders.add(jockey) + def test_add_field(self): """ Tests the AddField operation.