mirror of
https://github.com/django/django.git
synced 2026-02-09 02:49:25 +08:00
[6.0.x] Fixed #36800 -- Restored ManyToManyField renaming in BaseDatabaseSchemaEditor.alter_field().
Regression inf9a44cc0fa. Now that ManyToManyField is no longer concrete the decision of whether or not it should be altered, which is also relied on by field renaming, should take into consideration name changes even if it doesn't have a column associated with it, as auto-created many-to-many relationship table names are a base of it. Note that there is room for optimization here where a rename can be entirely avoided if ManyToManyField.db_table remains stable between .name changes, just like we do with Field.db_column remaining stable, but since this is a regression and meant to be backported the current patch focuses on correctness over further improvements. Thanks Josik for the report. Co-authored-by: Simon Charette <charette.s@gmail.com> Backport of6cc1231285from main.
This commit is contained in:
committed by
Jacob Walls
parent
be26ac85fd
commit
ba7f49c321
@@ -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
|
||||
|
||||
@@ -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`).
|
||||
|
||||
@@ -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):
|
||||
|
||||
Reference in New Issue
Block a user