Commit 3818d964 authored by Tim Graham's avatar Tim Graham
Browse files

Fixed #22435 -- Prevented adding a ManyToManyField from prompting for a default.

Thanks andrewsg for the report.
parent 142c2721
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
import re
import datetime

from django.db import models
from django.db.migrations import operations
from django.db.migrations.migration import Migration
from django.db.migrations.questioner import MigrationQuestioner
@@ -299,7 +300,7 @@ class MigrationAutodetector(object):
            if found_rename:
                continue
            # You can't just add NOT NULL fields with no default
            if not field.null and not field.has_default():
            if not field.null and not field.has_default() and not isinstance(field, models.ManyToManyField):
                field = field.clone()
                field.default = self.questioner.ask_not_null_addition(field_name, model_name)
                self.add_to_migration(
+26 −0
Original line number Diff line number Diff line
@@ -37,6 +37,10 @@ class AutodetectorTests(TestCase):
    author_proxy_notproxy = ModelState("testapp", "AuthorProxy", [], {}, ("testapp.author", ))
    author_unmanaged = ModelState("testapp", "AuthorUnmanaged", [], {"managed": False}, ("testapp.author", ))
    author_unmanaged_managed = ModelState("testapp", "AuthorUnmanaged", [], {}, ("testapp.author", ))
    author_with_m2m = ModelState("testapp", "Author", [
        ("id", models.AutoField(primary_key=True)),
        ("publishers", models.ManyToManyField("testapp.Publisher")),
    ])
    publisher = ModelState("testapp", "Publisher", [("id", models.AutoField(primary_key=True)), ("name", models.CharField(max_length=100))])
    publisher_with_author = ModelState("testapp", "Publisher", [("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("testapp.Author")), ("name", models.CharField(max_length=100))])
    publisher_with_book = ModelState("testapp", "Publisher", [("id", models.AutoField(primary_key=True)), ("author", models.ForeignKey("otherapp.Book")), ("name", models.CharField(max_length=100))])
@@ -619,6 +623,28 @@ class AutodetectorTests(TestCase):
        self.assertEqual(action.__class__.__name__, "DeleteModel")
        self.assertEqual(action.name, "Publisher")

    def test_add_many_to_many(self):
        """
        Adding a ManyToManyField should not prompt for a default (#22435).
        """
        class CustomQuestioner(MigrationQuestioner):
            def ask_not_null_addition(self, field_name, model_name):
                raise Exception("Should not have prompted for not null addition")

        before = self.make_project_state([self.author_empty, self.publisher])
        # Add ManyToManyField to author model
        after = self.make_project_state([self.author_with_m2m, self.publisher])
        autodetector = MigrationAutodetector(before, after, CustomQuestioner())
        changes = autodetector._detect_changes()
        # Right number of migrations?
        self.assertEqual(len(changes['testapp']), 1)
        migration = changes['testapp'][0]
        # Right actions in right order?
        self.assertEqual(len(migration.operations), 1)
        action = migration.operations[0]
        self.assertEqual(action.__class__.__name__, "AddField")
        self.assertEqual(action.name, "publishers")

    def test_many_to_many_removed_before_through_model(self):
        """
        Removing a ManyToManyField and the "through" model in the same change must remove