diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index d988fca903..d833f508e5 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -1037,10 +1037,16 @@ class BaseDatabaseSchemaEditor: return output def _field_should_be_altered(self, old_field, new_field): - # Don't alter when changing only a field name. + _, old_path, old_args, old_kwargs = old_field.deconstruct() + _, new_path, new_args, new_kwargs = new_field.deconstruct() + # Don't alter when: + # - changing only a field name + # - adding only a db_column and the column name is not changed + old_kwargs.pop('db_column', None) + new_kwargs.pop('db_column', None) return ( old_field.column != new_field.column or - old_field.deconstruct()[1:] != new_field.deconstruct()[1:] + (old_path, old_args, old_kwargs) != (new_path, new_args, new_kwargs) ) def _field_should_be_indexed(self, model, field): diff --git a/tests/migrations/test_operations.py b/tests/migrations/test_operations.py index 6abbd34ab8..0ebb8ff0a3 100644 --- a/tests/migrations/test_operations.py +++ b/tests/migrations/test_operations.py @@ -1354,6 +1354,59 @@ class OperationTests(OperationTestBase): self.assertEqual(definition[1], []) self.assertEqual(sorted(definition[2]), ["field", "model_name", "name"]) + def test_alter_field_add_db_column_noop(self): + """ + AlterField operation is a noop when adding only a db_column and the + column name is not changed. + """ + app_label = 'test_afadbn' + project_state = self.set_up_test_model(app_label, related_model=True) + pony_table = '%s_pony' % app_label + new_state = project_state.clone() + operation = migrations.AlterField('Pony', 'weight', models.FloatField(db_column='weight')) + operation.state_forwards(app_label, new_state) + self.assertIsNone( + project_state.models[app_label, 'pony'].fields['weight'].db_column, + ) + self.assertEqual( + new_state.models[app_label, 'pony'].fields['weight'].db_column, + 'weight', + ) + self.assertColumnExists(pony_table, 'weight') + with connection.schema_editor() as editor: + with self.assertNumQueries(0): + operation.database_forwards(app_label, editor, project_state, new_state) + self.assertColumnExists(pony_table, 'weight') + with connection.schema_editor() as editor: + with self.assertNumQueries(0): + operation.database_backwards(app_label, editor, new_state, project_state) + self.assertColumnExists(pony_table, 'weight') + + rider_table = '%s_rider' % app_label + new_state = project_state.clone() + operation = migrations.AlterField( + 'Rider', + 'pony', + models.ForeignKey('Pony', models.CASCADE, db_column='pony_id'), + ) + operation.state_forwards(app_label, new_state) + self.assertIsNone( + project_state.models[app_label, 'rider'].fields['pony'].db_column, + ) + self.assertIs( + new_state.models[app_label, 'rider'].fields['pony'].db_column, + 'pony_id', + ) + self.assertColumnExists(rider_table, 'pony_id') + with connection.schema_editor() as editor: + with self.assertNumQueries(0): + operation.database_forwards(app_label, editor, project_state, new_state) + self.assertColumnExists(rider_table, 'pony_id') + with connection.schema_editor() as editor: + with self.assertNumQueries(0): + operation.database_forwards(app_label, editor, new_state, project_state) + self.assertColumnExists(rider_table, 'pony_id') + def test_alter_field_pk(self): """ Tests the AlterField operation on primary keys (for things like PostgreSQL's SERIAL weirdness)