diff --git a/django/core/management/commands/makemigrations.py b/django/core/management/commands/makemigrations.py index d8eef481bd..9077660467 100644 --- a/django/core/management/commands/makemigrations.py +++ b/django/core/management/commands/makemigrations.py @@ -146,7 +146,12 @@ class Command(BaseCommand): 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) + questioner = NonInteractiveMigrationQuestioner( + specified_apps=app_labels, + dry_run=self.dry_run, + verbosity=self.verbosity, + log=self.log, + ) # Set up autodetector autodetector = MigrationAutodetector( loader.project_state(), diff --git a/django/db/migrations/questioner.py b/django/db/migrations/questioner.py index 9284e6be41..17883ca76c 100644 --- a/django/db/migrations/questioner.py +++ b/django/db/migrations/questioner.py @@ -258,15 +258,47 @@ class InteractiveMigrationQuestioner(MigrationQuestioner): class NonInteractiveMigrationQuestioner(MigrationQuestioner): + def __init__( + self, defaults=None, specified_apps=None, dry_run=None, verbosity=1, + log=None, + ): + self.verbosity = verbosity + self.log = log + super().__init__( + defaults=defaults, specified_apps=specified_apps, dry_run=dry_run, + ) + + def log_lack_of_migration(self, field_name, model_name, reason): + if self.verbosity > 0: + self.log( + f"Field '{field_name}' on model '{model_name}' not migrated: " + f"{reason}." + ) def ask_not_null_addition(self, field_name, model_name): # We can't ask the user, so act like the user aborted. + self.log_lack_of_migration( + field_name, + model_name, + 'it is impossible to add a non-nullable field without specifying ' + 'a default', + ) sys.exit(3) def ask_not_null_alteration(self, field_name, model_name): # We can't ask the user, so set as not provided. + self.log( + f"Field '{field_name}' on model '{model_name}' given a default of " + f"NOT PROVIDED and must be corrected." + ) return NOT_PROVIDED def ask_auto_now_add_addition(self, field_name, model_name): # We can't ask the user, so act like the user aborted. + self.log_lack_of_migration( + field_name, + model_name, + "it is impossible to add a field with 'auto_now_add=True' without " + "specifying a default", + ) sys.exit(3) diff --git a/docs/releases/4.1.txt b/docs/releases/4.1.txt index e74e3e07f1..af4b6d83a3 100644 --- a/docs/releases/4.1.txt +++ b/docs/releases/4.1.txt @@ -163,7 +163,8 @@ Logging Management Commands ~~~~~~~~~~~~~~~~~~~ -* ... +* :option:`makemigrations --no-input` now logs default answers and reasons why + migrations cannot be created. Migrations ~~~~~~~~~~ diff --git a/tests/migrations/test_commands.py b/tests/migrations/test_commands.py index 3e76332630..7b173c68bc 100644 --- a/tests/migrations/test_commands.py +++ b/tests/migrations/test_commands.py @@ -1361,7 +1361,14 @@ class MakeMigrationsTests(MigrationTestBase): with self.assertRaises(SystemExit): with self.temporary_migration_module(module="migrations.test_migrations_no_default"): - call_command("makemigrations", "migrations", interactive=False) + with captured_stdout() as out: + call_command('makemigrations', 'migrations', interactive=False) + self.assertIn( + "Field 'silly_int' on model 'sillymodel' not migrated: it is " + "impossible to add a non-nullable field without specifying a " + "default.", + out.getvalue(), + ) def test_makemigrations_interactive_not_null_addition(self): """ @@ -1417,10 +1424,15 @@ class MakeMigrationsTests(MigrationTestBase): class Meta: app_label = "migrations" - out = io.StringIO() with self.temporary_migration_module(module="migrations.test_migrations"): - call_command("makemigrations", "migrations", interactive=False, stdout=out) + with captured_stdout() as out: + call_command('makemigrations', 'migrations', interactive=False) self.assertIn("Alter field slug on author", out.getvalue()) + self.assertIn( + "Field 'slug' on model 'author' given a default of NOT PROVIDED " + "and must be corrected.", + out.getvalue(), + ) def test_makemigrations_interactive_not_null_alteration(self): """ @@ -1884,8 +1896,14 @@ class MakeMigrationsTests(MigrationTestBase): app_label = 'migrations' with self.temporary_migration_module(module='migrations.test_auto_now_add'): - with self.assertRaises(SystemExit): + with self.assertRaises(SystemExit), captured_stdout() as out: call_command('makemigrations', 'migrations', interactive=False) + self.assertIn( + "Field 'creation_date' on model 'entry' not migrated: it is " + "impossible to add a field with 'auto_now_add=True' without " + "specifying a default.", + out.getvalue(), + ) def test_makemigrations_interactive_unique_callable_default_addition(self): """