Fixed #22676 -- makemigrations --dry-run should not ask for defaults

Made the fix in InteractiveMigrationQuestioner class code, rather than
MigrationAutodetector, because --dry-run shouldn't affect whether
MigrationAutodetector will detect non-nullable fields, but the
questioner should skip the question and returns a None for default
(since that won't be used anyway) if --dry-run is used.
This commit is contained in:
Moayad Mardini 2014-05-22 12:42:46 +03:00 committed by Tim Graham
parent 9fb0f5dddc
commit 63fc91b3ca
5 changed files with 79 additions and 33 deletions

View File

@ -77,7 +77,7 @@ class Command(BaseCommand):
autodetector = MigrationAutodetector(
loader.project_state(),
ProjectState.from_apps(apps),
InteractiveMigrationQuestioner(specified_apps=app_labels),
InteractiveMigrationQuestioner(specified_apps=app_labels, dry_run=self.dry_run),
)
# If they want to make an empty migration, make one for each app

View File

@ -18,9 +18,10 @@ class MigrationQuestioner(object):
interactive subclass is what the command-line arguments will use.
"""
def __init__(self, defaults=None, specified_apps=None):
def __init__(self, defaults=None, specified_apps=None, dry_run=None):
self.defaults = defaults or {}
self.specified_apps = specified_apps or set()
self.dry_run = dry_run
def ask_initial(self, app_label):
"Should we create an initial migration for the app?"
@ -93,37 +94,39 @@ class InteractiveMigrationQuestioner(MigrationQuestioner):
def ask_not_null_addition(self, field_name, model_name):
"Adding a NOT NULL field to a model"
choice = self._choice_input(
"You are trying to add a non-nullable field '%s' to %s without a default;\n" % (field_name, model_name) +
"we can't do that (the database needs something to populate existing rows).\n" +
"Please select a fix:",
[
"Provide a one-off default now (will be set on all existing rows)",
"Quit, and let me add a default in models.py",
]
)
if choice == 2:
sys.exit(3)
else:
print("Please enter the default value now, as valid Python")
print("The datetime module is available, so you can do e.g. datetime.date.today()")
while True:
if six.PY3:
# Six does not correctly abstract over the fact that
# py3 input returns a unicode string, while py2 raw_input
# returns a bytestring.
code = input(">>> ")
else:
code = input(">>> ").decode(sys.stdin.encoding)
if not code:
print("Please enter some code, or 'exit' (with no quotes) to exit.")
elif code == "exit":
sys.exit(1)
else:
try:
return eval(code, {}, {"datetime": datetime_safe})
except (SyntaxError, NameError) as e:
print("Invalid input: %s" % e)
if not self.dry_run:
choice = self._choice_input(
"You are trying to add a non-nullable field '%s' to %s without a default;\n" % (field_name, model_name) +
"we can't do that (the database needs something to populate existing rows).\n" +
"Please select a fix:",
[
"Provide a one-off default now (will be set on all existing rows)",
"Quit, and let me add a default in models.py",
]
)
if choice == 2:
sys.exit(3)
else:
print("Please enter the default value now, as valid Python")
print("The datetime module is available, so you can do e.g. datetime.date.today()")
while True:
if six.PY3:
# Six does not correctly abstract over the fact that
# py3 input returns a unicode string, while py2 raw_input
# returns a bytestring.
code = input(">>> ")
else:
code = input(">>> ").decode(sys.stdin.encoding)
if not code:
print("Please enter some code, or 'exit' (with no quotes) to exit.")
elif code == "exit":
sys.exit(1)
else:
try:
return eval(code, {}, {"datetime": datetime_safe})
except (SyntaxError, NameError) as e:
print("Invalid input: %s" % e)
return None
def ask_rename(self, model_name, old_name, new_name, field_instance):
"Was this field really renamed?"

View File

@ -6,6 +6,7 @@ import os
import shutil
from django.apps import apps
from django.db import models
from django.core.management import call_command, CommandError
from django.db.migrations import questioner
from django.test import override_settings, override_system_checks
@ -362,3 +363,22 @@ class MakeMigrationsTests(MigrationTestBase):
self.assertIn("Merging migrations", stdout.getvalue())
self.assertIn("Branch 0002_second", stdout.getvalue())
self.assertIn("Branch 0002_conflicting_second", stdout.getvalue())
@override_system_checks([])
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_no_default"})
def test_makemigrations_dry_run(self):
"""
Ticket #22676 -- `makemigrations --dry-run` should not ask for defaults.
"""
class SillyModel(models.Model):
silly_field = models.BooleanField(default=False)
silly_date = models.DateField() # Added field without a default
class Meta:
app_label = "migrations"
stdout = six.StringIO()
call_command("makemigrations", "migrations", dry_run=True, stdout=stdout)
# Output the expected changes directly, without asking for defaults
self.assertIn("Add field silly_date to sillymodel", stdout.getvalue())

View File

@ -0,0 +1,23 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
]
operations = [
migrations.CreateModel(
name='SillyModel',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('silly_field', models.BooleanField(default=False)),
],
options={
},
bases=(models.Model,),
),
]