Commit a9e29fae authored by Markus Holtermann's avatar Markus Holtermann
Browse files

Fixed #24435 -- Prevented m2m field removal and addition in migrations when changing blank

Thanks Mark Tranchant for the report an Tim Graham for the test and
review.
parent 70123cf0
Loading
Loading
Loading
Loading
+11 −2
Original line number Diff line number Diff line
@@ -848,8 +848,16 @@ class MigrationAutodetector(object):
            old_field_dec = self.deep_deconstruct(old_field)
            new_field_dec = self.deep_deconstruct(new_field)
            if old_field_dec != new_field_dec:
                if (not isinstance(old_field, models.ManyToManyField) and
                        not isinstance(new_field, models.ManyToManyField)):
                both_m2m = (
                    isinstance(old_field, models.ManyToManyField) and
                    isinstance(new_field, models.ManyToManyField)
                )
                neither_m2m = (
                    not isinstance(old_field, models.ManyToManyField) and
                    not isinstance(new_field, models.ManyToManyField)
                )
                if both_m2m or neither_m2m:
                    # Either both fields are m2m or neither is
                    preserve_default = True
                    if (old_field.null and not new_field.null and not new_field.has_default() and
                            not isinstance(new_field, models.ManyToManyField)):
@@ -870,6 +878,7 @@ class MigrationAutodetector(object):
                        )
                    )
                else:
                    # We cannot alter between m2m and concrete fields
                    self._generate_removed_field(app_label, model_name, field_name)
                    self._generate_added_field(app_label, model_name, field_name)

+14 −0
Original line number Diff line number Diff line
@@ -127,6 +127,10 @@ class AutodetectorTests(TestCase):
        ("id", models.AutoField(primary_key=True)),
        ("publishers", models.ManyToManyField("testapp.Publisher")),
    ])
    author_with_m2m_blank = ModelState("testapp", "Author", [
        ("id", models.AutoField(primary_key=True)),
        ("publishers", models.ManyToManyField("testapp.Publisher", blank=True)),
    ])
    author_with_m2m_through = ModelState("testapp", "Author", [
        ("id", models.AutoField(primary_key=True)),
        ("publishers", models.ManyToManyField("testapp.Publisher", through="testapp.Contract")),
@@ -1263,6 +1267,16 @@ class AutodetectorTests(TestCase):
        self.assertOperationTypes(changes, 'testapp', 0, ["AddField"])
        self.assertOperationAttributes(changes, 'testapp', 0, 0, name="publishers")

    def test_alter_many_to_many(self):
        before = self.make_project_state([self.author_with_m2m, self.publisher])
        after = self.make_project_state([self.author_with_m2m_blank, self.publisher])
        autodetector = MigrationAutodetector(before, after)
        changes = autodetector._detect_changes()
        # Right number/type of migrations?
        self.assertNumberMigrations(changes, 'testapp', 1)
        self.assertOperationTypes(changes, 'testapp', 0, ["AlterField"])
        self.assertOperationAttributes(changes, 'testapp', 0, 0, name="publishers")

    def test_create_with_through_model(self):
        """
        Adding a m2m with a through model and the models that use it should be