Commit e272904f authored by Marten Kenbeek's avatar Marten Kenbeek Committed by Markus Holtermann
Browse files

Fixed #23407 -- Extended coverage of makemigrations --noinput option.

Changed --noinput option in makemigrations to suppress all user prompts,
not just when combined with --merge.
parent 794c3f74
Loading
Loading
Loading
Loading
+6 −1
Original line number Diff line number Diff line
@@ -9,6 +9,7 @@ from django.db.migrations.autodetector import MigrationAutodetector
from django.db.migrations.loader import MigrationLoader
from django.db.migrations.questioner import (
    InteractiveMigrationQuestioner, MigrationQuestioner,
    NonInteractiveMigrationQuestioner,
)
from django.db.migrations.state import ProjectState
from django.db.migrations.writer import MigrationWriter
@@ -93,11 +94,15 @@ class Command(BaseCommand):
        if self.merge and conflicts:
            return self.handle_merge(loader, conflicts)

        if self.interactive:
            questioner = InteractiveMigrationQuestioner(specified_apps=app_labels, dry_run=self.dry_run)
        else:
            questioner = NonInteractiveMigrationQuestioner(specified_apps=app_labels, dry_run=self.dry_run)
        # Set up autodetector
        autodetector = MigrationAutodetector(
            loader.project_state(),
            ProjectState.from_apps(apps),
            InteractiveMigrationQuestioner(specified_apps=app_labels, dry_run=self.dry_run),
            questioner,
        )

        # If they want to make an empty migration, make one for each app
+11 −0
Original line number Diff line number Diff line
@@ -180,3 +180,14 @@ class InteractiveMigrationQuestioner(MigrationQuestioner):
            "Do you want to merge these migration branches? [y/N]",
            False,
        )


class NonInteractiveMigrationQuestioner(MigrationQuestioner):

    def ask_not_null_addition(self, field_name, model_name):
        # We can't ask the user, so act like the user aborted.
        sys.exit(3)

    def ask_not_null_alteration(self, field_name, model_name):
        # We can't ask the user, so set as not provided.
        return NOT_PROVIDED
+6 −3
Original line number Diff line number Diff line
@@ -649,6 +649,11 @@ Providing one or more app names as arguments will limit the migrations created
to the app(s) specified and any dependencies needed (the table at the other end
of a ``ForeignKey``, for example).

.. versionchanged:: 1.9

The ``--noinput`` option may be provided to suppress all user prompts. If a suppressed
prompt cannot be resolved automatically, the command will exit with error code 3.

.. django-admin-option:: --empty

The ``--empty`` option will cause ``makemigrations`` to output an empty
@@ -666,9 +671,7 @@ written.

.. django-admin-option:: --merge

The ``--merge`` option enables fixing of migration conflicts. The
:djadminopt:`--noinput` option may be provided to suppress user prompts during
a merge.
The ``--merge`` option enables fixing of migration conflicts.

.. django-admin-option:: --name, -n

+74 −0
Original line number Diff line number Diff line
@@ -531,6 +531,80 @@ class MakeMigrationsTests(MigrationTestBase):
            questioner.input = old_input
        self.assertIn("Created new merge migration", force_text(out.getvalue()))

    def test_makemigrations_non_interactive_not_null_addition(self):
        """
        Tests that non-interactive makemigrations fails when a default is missing on a new not-null field.
        """
        class SillyModel(models.Model):
            silly_field = models.BooleanField(default=False)
            silly_int = models.IntegerField()

            class Meta:
                app_label = "migrations"

        out = six.StringIO()
        with self.assertRaises(SystemExit):
            with self.temporary_migration_module(module="migrations.test_migrations_no_default"):
                call_command("makemigrations", "migrations", interactive=False, stdout=out)

    def test_makemigrations_non_interactive_not_null_alteration(self):
        """
        Tests that non-interactive makemigrations fails when a default is missing on a field changed to not-null.
        """
        class Author(models.Model):
            name = models.CharField(max_length=255)
            slug = models.SlugField()
            age = models.IntegerField(default=0)

            class Meta:
                app_label = "migrations"

        out = six.StringIO()
        try:
            with self.temporary_migration_module(module="migrations.test_migrations"):
                call_command("makemigrations", "migrations", interactive=False, stdout=out)
        except CommandError:
            self.fail("Makemigrations failed while running non-interactive questioner.")
        self.assertIn("Alter field slug on author", force_text(out.getvalue()))

    def test_makemigrations_non_interactive_no_model_rename(self):
        """
        Makes sure that makemigrations adds and removes a possible model rename in non-interactive mode.
        """
        class RenamedModel(models.Model):
            silly_field = models.BooleanField(default=False)

            class Meta:
                app_label = "migrations"

        out = six.StringIO()
        try:
            with self.temporary_migration_module(module="migrations.test_migrations_no_default"):
                call_command("makemigrations", "migrations", interactive=False, stdout=out)
        except CommandError:
            self.fail("Makemigrations failed while running non-interactive questioner")
        self.assertIn("Delete model SillyModel", force_text(out.getvalue()))
        self.assertIn("Create model RenamedModel", force_text(out.getvalue()))

    def test_makemigrations_non_interactive_no_field_rename(self):
        """
        Makes sure that makemigrations adds and removes a possible field rename in non-interactive mode.
        """
        class SillyModel(models.Model):
            silly_rename = models.BooleanField(default=False)

            class Meta:
                app_label = "migrations"

        out = six.StringIO()
        try:
            with self.temporary_migration_module(module="migrations.test_migrations_no_default"):
                call_command("makemigrations", "migrations", interactive=False, stdout=out)
        except CommandError:
            self.fail("Makemigrations failed while running non-interactive questioner")
        self.assertIn("Remove field silly_field from sillymodel", force_text(out.getvalue()))
        self.assertIn("Add field silly_rename to sillymodel", force_text(out.getvalue()))

    def test_makemigrations_handle_merge(self):
        """
        Makes sure that makemigrations properly merges the conflicting migrations with --noinput.