Fixed #29026 -- Added --scriptable option to makemigrations.
This commit is contained in:
parent
274771df91
commit
6f78cb6b13
|
@ -57,9 +57,20 @@ class Command(BaseCommand):
|
|||
'--check', action='store_true', dest='check_changes',
|
||||
help='Exit with a non-zero status if model changes are missing migrations.',
|
||||
)
|
||||
parser.add_argument(
|
||||
'--scriptable', action='store_true', dest='scriptable',
|
||||
help=(
|
||||
'Divert log output and input prompts to stderr, writing only '
|
||||
'paths of generated migration files to stdout.'
|
||||
),
|
||||
)
|
||||
|
||||
@property
|
||||
def log_output(self):
|
||||
return self.stderr if self.scriptable else self.stdout
|
||||
|
||||
def log(self, msg):
|
||||
self.stdout.write(msg)
|
||||
self.log_output.write(msg)
|
||||
|
||||
@no_translations
|
||||
def handle(self, *app_labels, **options):
|
||||
|
@ -73,6 +84,10 @@ class Command(BaseCommand):
|
|||
raise CommandError('The migration name must be a valid Python identifier.')
|
||||
self.include_header = options['include_header']
|
||||
check_changes = options['check_changes']
|
||||
self.scriptable = options['scriptable']
|
||||
# If logs and prompts are diverted to stderr, remove the ERROR style.
|
||||
if self.scriptable:
|
||||
self.stderr.style_func = None
|
||||
|
||||
# Make sure the app they asked for exists
|
||||
app_labels = set(app_labels)
|
||||
|
@ -147,7 +162,7 @@ class Command(BaseCommand):
|
|||
questioner = InteractiveMigrationQuestioner(
|
||||
specified_apps=app_labels,
|
||||
dry_run=self.dry_run,
|
||||
prompt_output=self.stdout,
|
||||
prompt_output=self.log_output,
|
||||
)
|
||||
else:
|
||||
questioner = NonInteractiveMigrationQuestioner(
|
||||
|
@ -226,6 +241,8 @@ class Command(BaseCommand):
|
|||
self.log(' %s\n' % self.style.MIGRATE_LABEL(migration_string))
|
||||
for operation in migration.operations:
|
||||
self.log(' - %s' % operation.describe())
|
||||
if self.scriptable:
|
||||
self.stdout.write(migration_string)
|
||||
if not self.dry_run:
|
||||
# Write the migrations file to the disk.
|
||||
migrations_directory = os.path.dirname(writer.path)
|
||||
|
@ -254,7 +271,7 @@ class Command(BaseCommand):
|
|||
if it's safe; otherwise, advises on how to fix it.
|
||||
"""
|
||||
if self.interactive:
|
||||
questioner = InteractiveMigrationQuestioner(prompt_output=self.stdout)
|
||||
questioner = InteractiveMigrationQuestioner(prompt_output=self.log_output)
|
||||
else:
|
||||
questioner = MigrationQuestioner(defaults={'ask_merge': True})
|
||||
|
||||
|
@ -327,6 +344,8 @@ class Command(BaseCommand):
|
|||
fh.write(writer.as_string())
|
||||
if self.verbosity > 0:
|
||||
self.log('\nCreated new merge migration %s' % writer.path)
|
||||
if self.scriptable:
|
||||
self.stdout.write(writer.path)
|
||||
elif self.verbosity == 3:
|
||||
# Alternatively, makemigrations --merge --dry-run --verbosity 3
|
||||
# will log the merge migrations rather than saving the file
|
||||
|
|
|
@ -825,6 +825,13 @@ Generate migration files without Django version and timestamp header.
|
|||
Makes ``makemigrations`` exit with a non-zero status when model changes without
|
||||
migrations are detected.
|
||||
|
||||
.. django-admin-option:: --scriptable
|
||||
|
||||
.. versionadded:: 4.1
|
||||
|
||||
Diverts log output and input prompts to ``stderr``, writing only paths of
|
||||
generated migration files to ``stdout``.
|
||||
|
||||
``migrate``
|
||||
-----------
|
||||
|
||||
|
|
|
@ -210,6 +210,10 @@ Management Commands
|
|||
* :option:`makemigrations --no-input` now logs default answers and reasons why
|
||||
migrations cannot be created.
|
||||
|
||||
* The new :option:`makemigrations --scriptable` options diverts log output and
|
||||
input prompts to ``stderr``, writing only paths of generated migration files
|
||||
to ``stdout``.
|
||||
|
||||
Migrations
|
||||
~~~~~~~~~~
|
||||
|
||||
|
|
|
@ -1667,6 +1667,47 @@ class MakeMigrationsTests(MigrationTestBase):
|
|||
self.assertIn("model_name='sillymodel',", out.getvalue())
|
||||
self.assertIn("name='silly_char',", out.getvalue())
|
||||
|
||||
def test_makemigrations_scriptable(self):
|
||||
"""
|
||||
With scriptable=True, log output is diverted to stderr, and only the
|
||||
paths of generated migration files are written to stdout.
|
||||
"""
|
||||
out = io.StringIO()
|
||||
err = io.StringIO()
|
||||
with self.temporary_migration_module(
|
||||
module='migrations.migrations.test_migrations',
|
||||
) as migration_dir:
|
||||
call_command(
|
||||
'makemigrations',
|
||||
'migrations',
|
||||
scriptable=True,
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
)
|
||||
initial_file = os.path.join(migration_dir, '0001_initial.py')
|
||||
self.assertEqual(out.getvalue(), f'{initial_file}\n')
|
||||
self.assertIn(' - Create model ModelWithCustomBase\n', err.getvalue())
|
||||
|
||||
@mock.patch('builtins.input', return_value='Y')
|
||||
def test_makemigrations_scriptable_merge(self, mock_input):
|
||||
out = io.StringIO()
|
||||
err = io.StringIO()
|
||||
with self.temporary_migration_module(
|
||||
module='migrations.test_migrations_conflict',
|
||||
) as migration_dir:
|
||||
call_command(
|
||||
'makemigrations',
|
||||
'migrations',
|
||||
merge=True,
|
||||
name='merge',
|
||||
scriptable=True,
|
||||
stdout=out,
|
||||
stderr=err,
|
||||
)
|
||||
merge_file = os.path.join(migration_dir, '0003_merge.py')
|
||||
self.assertEqual(out.getvalue(), f'{merge_file}\n')
|
||||
self.assertIn(f'Created new merge migration {merge_file}', err.getvalue())
|
||||
|
||||
def test_makemigrations_migrations_modules_path_not_exist(self):
|
||||
"""
|
||||
makemigrations creates migrations when specifying a custom location
|
||||
|
|
Loading…
Reference in New Issue