diff options
| -rw-r--r-- | django/db/backends/base/schema.py | 16 | ||||
| -rw-r--r-- | docs/releases/6.0.1.txt | 4 | ||||
| -rw-r--r-- | tests/schema/tests.py | 34 |
3 files changed, 50 insertions, 4 deletions
diff --git a/django/db/backends/base/schema.py b/django/db/backends/base/schema.py index 96d555f862..65449afdc8 100644 --- a/django/db/backends/base/schema.py +++ b/django/db/backends/base/schema.py @@ -1668,7 +1668,7 @@ class BaseDatabaseSchemaEditor: _, 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 + # - changing only a field name (unless it's a many-to-many) # - changing an attribute that doesn't affect the schema # - changing an attribute in the provided set of ignored attributes # - adding only a db_column and the column name is not changed @@ -1686,7 +1686,7 @@ class BaseDatabaseSchemaEditor: ): old_kwargs.pop("to", None) new_kwargs.pop("to", None) - # db_default can take many form but result in the same SQL. + # db_default can take many forms but result in the same SQL. if ( old_kwargs.get("db_default") and new_kwargs.get("db_default") @@ -1694,11 +1694,19 @@ class BaseDatabaseSchemaEditor: ): old_kwargs.pop("db_default") new_kwargs.pop("db_default") - return ( + if ( old_field.concrete and new_field.concrete and (self.quote_name(old_field.column) != self.quote_name(new_field.column)) - ) or (old_path, old_args, old_kwargs) != (new_path, new_args, new_kwargs) + ): + return True + if ( + old_field.many_to_many + and new_field.many_to_many + and old_field.name != new_field.name + ): + return True + return (old_path, old_args, old_kwargs) != (new_path, new_args, new_kwargs) def _field_should_be_indexed(self, model, field): return field.db_index and not field.unique diff --git a/docs/releases/6.0.1.txt b/docs/releases/6.0.1.txt index 72c35b3e69..a84d49b07a 100644 --- a/docs/releases/6.0.1.txt +++ b/docs/releases/6.0.1.txt @@ -12,3 +12,7 @@ Bugfixes * Fixed a regression in Django 6.0 where :ttag:`querystring` mishandled multi-value :class:`~django.http.QueryDict` keys, both by only preserving the last value and by incorrectly handling ``None`` values (:ticket:`36783`). + +* Fixed a regression in Django 6.0 that prevented changing the name of a + :class:`~django.db.models.ManyToManyField` from taking effect when applying + migrations (:ticket:`36800`). diff --git a/tests/schema/tests.py b/tests/schema/tests.py index 88d4ebbc8b..c42758d827 100644 --- a/tests/schema/tests.py +++ b/tests/schema/tests.py @@ -2796,6 +2796,40 @@ class SchemaTests(TransactionTestCase): def test_m2m_repoint_inherited(self): self._test_m2m_repoint(InheritedManyToManyField) + def test_m2m_rename(self): + class LocalBook(Model): + authors = ManyToManyField("schema.Author") + + class Meta: + app_label = "schema" + apps = new_apps + + self.local_models = [LocalBook] + with connection.schema_editor() as editor: + editor.create_model(Author) + editor.create_model(LocalBook) + old_field = LocalBook._meta.get_field("authors") + new_field = ManyToManyField("schema.Author") + new_field.contribute_to_class(LocalBook, "writers") + with connection.schema_editor() as editor: + editor.alter_field(LocalBook, old_field, new_field, strict=True) + # Ensure old M2M is gone. + with self.assertRaises(DatabaseError): + self.column_classes( + LocalBook._meta.get_field("authors").remote_field.through + ) + if connection.features.supports_foreign_keys: + self.assertForeignKeyExists( + new_field.remote_field.through, + "author_id", + "schema_author", + ) + new_through_table = new_field.remote_field.through._meta.db_table + self.assertIn("writers", new_through_table) + self.assertNotIn("authors", new_through_table) + # Remove the old field from meta for tearDown(). + LocalBook._meta.local_many_to_many.remove(old_field) + @isolate_apps("schema") def test_m2m_rename_field_in_target_model(self): class LocalTagM2MTest(Model): |
