From 7f4c9222dfe2f28ff8a7ffc56c28ccbadf19cf6f Mon Sep 17 00:00:00 2001 From: Iuri de Silvio Date: Fri, 24 Jul 2020 18:08:40 -0300 Subject: [PATCH] Fixed #31825 -- Made RenameField operation a noop for fields with db_column. --- AUTHORS | 1 + django/db/backends/base/schema.py | 9 ++++++ django/db/backends/sqlite3/schema.py | 2 ++ tests/migrations/test_operations.py | 42 ++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+) diff --git a/AUTHORS b/AUTHORS index 73f0d6c8df..16272bdf28 100644 --- a/AUTHORS +++ b/AUTHORS @@ -386,6 +386,7 @@ answer newbie questions, and generally made Django that much better: Ilya Semenov Ingo Klöcker I.S. van Oostveen + Iuri de Silvio ivan.chelubeev@gmail.com Ivan Sagalaev (Maniac) Jaap Roes diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index c8dc8e3a70..d988fca903 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -538,6 +538,8 @@ class BaseDatabaseSchemaEditor: If `strict` is True, raise errors if the old column does not match `old_field` precisely. """ + if not self._field_should_be_altered(old_field, new_field): + return # Ensure this field is even column-based old_db_params = old_field.db_parameters(connection=self.connection) old_type = old_db_params['type'] @@ -1034,6 +1036,13 @@ class BaseDatabaseSchemaEditor: output.append(self._create_index_sql(model, [field])) return output + def _field_should_be_altered(self, old_field, new_field): + # Don't alter when changing only a field name. + return ( + old_field.column != new_field.column or + old_field.deconstruct()[1:] != new_field.deconstruct()[1:] + ) + def _field_should_be_indexed(self, model, field): return field.db_index and not field.unique diff --git a/django/db/backends/sqlite3/schema.py b/django/db/backends/sqlite3/schema.py index aca5d61b39..146ad18f84 100644 --- a/django/db/backends/sqlite3/schema.py +++ b/django/db/backends/sqlite3/schema.py @@ -99,6 +99,8 @@ class DatabaseSchemaEditor(BaseDatabaseSchemaEditor): super().alter_db_table(model, old_db_table, new_db_table) def alter_field(self, model, old_field, new_field, strict=False): + if not self._field_should_be_altered(old_field, new_field): + return old_field_name = old_field.name table_name = model._meta.db_table _, old_column_name = old_field.get_attname_column() diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index 588186fae4..6abbd34ab8 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -1632,6 +1632,48 @@ class OperationTests(OperationTestBase): self.assertEqual(definition[1], []) self.assertEqual(definition[2], {'model_name': "Pony", 'old_name': "pink", 'new_name': "blue"}) + def test_rename_field_with_db_column(self): + project_state = self.apply_operations('test_rfwdbc', ProjectState(), operations=[ + migrations.CreateModel('Pony', fields=[ + ('id', models.AutoField(primary_key=True)), + ('field', models.IntegerField(db_column='db_field')), + ('fk_field', models.ForeignKey( + 'Pony', + models.CASCADE, + db_column='db_fk_field', + )), + ]), + ]) + new_state = project_state.clone() + operation = migrations.RenameField('Pony', 'field', 'renamed_field') + operation.state_forwards('test_rfwdbc', new_state) + self.assertIn('renamed_field', new_state.models['test_rfwdbc', 'pony'].fields) + self.assertNotIn('field', new_state.models['test_rfwdbc', 'pony'].fields) + self.assertColumnExists('test_rfwdbc_pony', 'db_field') + with connection.schema_editor() as editor: + with self.assertNumQueries(0): + operation.database_forwards('test_rfwdbc', editor, project_state, new_state) + self.assertColumnExists('test_rfwdbc_pony', 'db_field') + with connection.schema_editor() as editor: + with self.assertNumQueries(0): + operation.database_backwards('test_rfwdbc', editor, new_state, project_state) + self.assertColumnExists('test_rfwdbc_pony', 'db_field') + + new_state = project_state.clone() + operation = migrations.RenameField('Pony', 'fk_field', 'renamed_fk_field') + operation.state_forwards('test_rfwdbc', new_state) + self.assertIn('renamed_fk_field', new_state.models['test_rfwdbc', 'pony'].fields) + self.assertNotIn('fk_field', new_state.models['test_rfwdbc', 'pony'].fields) + self.assertColumnExists('test_rfwdbc_pony', 'db_fk_field') + with connection.schema_editor() as editor: + with self.assertNumQueries(0): + operation.database_forwards('test_rfwdbc', editor, project_state, new_state) + self.assertColumnExists('test_rfwdbc_pony', 'db_fk_field') + with connection.schema_editor() as editor: + with self.assertNumQueries(0): + operation.database_backwards('test_rfwdbc', editor, new_state, project_state) + self.assertColumnExists('test_rfwdbc_pony', 'db_fk_field') + def test_rename_missing_field(self): state = ProjectState() state.add_model(ModelState('app', 'model', []))