Skip to content

Commit

Permalink
[4.2.x] Fixed #34250 -- Fixed renaming model with m2m relation to a m…
Browse files Browse the repository at this point in the history
…odel with the same name.

Backport of ff3a283 from main
  • Loading branch information
DevilsAutumn authored and felixxm committed Feb 14, 2023
1 parent b7aab1f commit 5cde08f
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 14 deletions.
20 changes: 6 additions & 14 deletions django/db/migrations/operations/models.py
Expand Up @@ -407,20 +407,12 @@ def database_forwards(self, app_label, schema_editor, from_state, to_state):
or not new_field.remote_field.through._meta.auto_created
):
continue
# Rename the M2M table that's based on this model's name.
old_m2m_model = old_field.remote_field.through
new_m2m_model = new_field.remote_field.through
schema_editor.alter_db_table(
new_m2m_model,
old_m2m_model._meta.db_table,
new_m2m_model._meta.db_table,
)
# Rename the column in the M2M table that's based on this
# model's name.
schema_editor.alter_field(
new_m2m_model,
old_m2m_model._meta.get_field(old_model._meta.model_name),
new_m2m_model._meta.get_field(new_model._meta.model_name),
# Rename columns and the M2M table.
schema_editor._alter_many_to_many(
new_model,
old_field,
new_field,
strict=False,
)

def database_backwards(self, app_label, schema_editor, from_state, to_state):
Expand Down
69 changes: 69 additions & 0 deletions tests/migrations/test_operations.py
Expand Up @@ -1058,6 +1058,75 @@ def test_rename_model_with_m2m(self):
Pony._meta.get_field("riders").remote_field.through.objects.count(), 2
)

def test_rename_model_with_m2m_models_in_different_apps_with_same_name(self):
app_label_1 = "test_rmw_m2m_1"
app_label_2 = "test_rmw_m2m_2"
project_state = self.apply_operations(
app_label_1,
ProjectState(),
operations=[
migrations.CreateModel(
"Rider",
fields=[
("id", models.AutoField(primary_key=True)),
],
),
],
)
project_state = self.apply_operations(
app_label_2,
project_state,
operations=[
migrations.CreateModel(
"Rider",
fields=[
("id", models.AutoField(primary_key=True)),
("riders", models.ManyToManyField(f"{app_label_1}.Rider")),
],
),
],
)
m2m_table = f"{app_label_2}_rider_riders"
self.assertColumnExists(m2m_table, "from_rider_id")
self.assertColumnExists(m2m_table, "to_rider_id")

Rider_1 = project_state.apps.get_model(app_label_1, "Rider")
Rider_2 = project_state.apps.get_model(app_label_2, "Rider")
rider_2 = Rider_2.objects.create()
rider_2.riders.add(Rider_1.objects.create())
# Rename model.
project_state_2 = project_state.clone()
project_state = self.apply_operations(
app_label_2,
project_state,
operations=[migrations.RenameModel("Rider", "Pony")],
atomic=connection.features.supports_atomic_references_rename,
)

m2m_table = f"{app_label_2}_pony_riders"
self.assertColumnExists(m2m_table, "pony_id")
self.assertColumnExists(m2m_table, "rider_id")

Rider_1 = project_state.apps.get_model(app_label_1, "Rider")
Rider_2 = project_state.apps.get_model(app_label_2, "Pony")
rider_2 = Rider_2.objects.create()
rider_2.riders.add(Rider_1.objects.create())
self.assertEqual(Rider_1.objects.count(), 2)
self.assertEqual(Rider_2.objects.count(), 2)
self.assertEqual(
Rider_2._meta.get_field("riders").remote_field.through.objects.count(), 2
)
# Reversal.
self.unapply_operations(
app_label_2,
project_state_2,
operations=[migrations.RenameModel("Rider", "Pony")],
atomic=connection.features.supports_atomic_references_rename,
)
m2m_table = f"{app_label_2}_rider_riders"
self.assertColumnExists(m2m_table, "to_rider_id")
self.assertColumnExists(m2m_table, "from_rider_id")

def test_rename_model_with_db_table_rename_m2m(self):
app_label = "test_rmwdbrm2m"
project_state = self.apply_operations(
Expand Down

0 comments on commit 5cde08f

Please sign in to comment.