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

Changed --noinput option in makemigrations to suppress all user prompts,
not just when combined with --merge.
This commit is contained in:
Marten Kenbeek 2015-03-07 13:53:13 +01:00 committed by Markus Holtermann
parent 794c3f74c3
commit e272904ff7
4 changed files with 97 additions and 4 deletions

View File

@ -9,6 +9,7 @@ from django.db.migrations.autodetector import MigrationAutodetector
from django.db.migrations.loader import MigrationLoader from django.db.migrations.loader import MigrationLoader
from django.db.migrations.questioner import ( from django.db.migrations.questioner import (
InteractiveMigrationQuestioner, MigrationQuestioner, InteractiveMigrationQuestioner, MigrationQuestioner,
NonInteractiveMigrationQuestioner,
) )
from django.db.migrations.state import ProjectState from django.db.migrations.state import ProjectState
from django.db.migrations.writer import MigrationWriter from django.db.migrations.writer import MigrationWriter
@ -93,11 +94,15 @@ class Command(BaseCommand):
if self.merge and conflicts: if self.merge and conflicts:
return self.handle_merge(loader, 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 # Set up autodetector
autodetector = MigrationAutodetector( autodetector = MigrationAutodetector(
loader.project_state(), loader.project_state(),
ProjectState.from_apps(apps), 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 # If they want to make an empty migration, make one for each app

View File

@ -180,3 +180,14 @@ class InteractiveMigrationQuestioner(MigrationQuestioner):
"Do you want to merge these migration branches? [y/N]", "Do you want to merge these migration branches? [y/N]",
False, 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

View File

@ -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 to the app(s) specified and any dependencies needed (the table at the other end
of a ``ForeignKey``, for example). 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 .. django-admin-option:: --empty
The ``--empty`` option will cause ``makemigrations`` to output an empty The ``--empty`` option will cause ``makemigrations`` to output an empty
@ -666,9 +671,7 @@ written.
.. django-admin-option:: --merge .. django-admin-option:: --merge
The ``--merge`` option enables fixing of migration conflicts. The The ``--merge`` option enables fixing of migration conflicts.
:djadminopt:`--noinput` option may be provided to suppress user prompts during
a merge.
.. django-admin-option:: --name, -n .. django-admin-option:: --name, -n

View File

@ -531,6 +531,80 @@ class MakeMigrationsTests(MigrationTestBase):
questioner.input = old_input questioner.input = old_input
self.assertIn("Created new merge migration", force_text(out.getvalue())) 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): def test_makemigrations_handle_merge(self):
""" """
Makes sure that makemigrations properly merges the conflicting migrations with --noinput. Makes sure that makemigrations properly merges the conflicting migrations with --noinput.