summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--django/db/backends/base/schema.py16
-rw-r--r--docs/releases/6.0.1.txt4
-rw-r--r--tests/schema/tests.py34
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):