Fixed #31402 -- Added migrate --check option.

Command exits with non-zero status if unapplied migrations exist.
This commit is contained in:
Gordon Pendleton 2020-03-26 05:08:58 -04:00 committed by Mariusz Felisiak
parent 4216225480
commit d0da2820ca
4 changed files with 53 additions and 0 deletions

View File

@ -1,3 +1,4 @@
import sys
import time import time
from importlib import import_module from importlib import import_module
@ -62,6 +63,10 @@ class Command(BaseCommand):
'--run-syncdb', action='store_true', '--run-syncdb', action='store_true',
help='Creates tables for apps without migrations.', help='Creates tables for apps without migrations.',
) )
parser.add_argument(
'--check', action='store_true', dest='check_unapplied',
help='Exits with a non-zero status if unapplied migrations exist.',
)
@no_translations @no_translations
def handle(self, *args, **options): def handle(self, *args, **options):
@ -143,6 +148,7 @@ class Command(BaseCommand):
targets = executor.loader.graph.leaf_nodes() targets = executor.loader.graph.leaf_nodes()
plan = executor.migration_plan(targets) plan = executor.migration_plan(targets)
exit_dry = plan and options['check_unapplied']
if options['plan']: if options['plan']:
self.stdout.write('Planned operations:', self.style.MIGRATE_LABEL) self.stdout.write('Planned operations:', self.style.MIGRATE_LABEL)
@ -154,7 +160,11 @@ class Command(BaseCommand):
message, is_error = self.describe_operation(operation, backwards) message, is_error = self.describe_operation(operation, backwards)
style = self.style.WARNING if is_error else None style = self.style.WARNING if is_error else None
self.stdout.write(' ' + message, style) self.stdout.write(' ' + message, style)
if exit_dry:
sys.exit(1)
return return
if exit_dry:
sys.exit(1)
# At this point, ignore run_syncdb if there aren't any apps to sync. # At this point, ignore run_syncdb if there aren't any apps to sync.
run_syncdb = options['run_syncdb'] and executor.loader.unmigrated_apps run_syncdb = options['run_syncdb'] and executor.loader.unmigrated_apps

View File

@ -865,6 +865,13 @@ with hundreds of models.
Suppresses all user prompts. An example prompt is asking about removing stale Suppresses all user prompts. An example prompt is asking about removing stale
content types. content types.
.. django-admin-option:: --check
.. versionadded:: 3.1
Makes ``migrate`` exit with a non-zero status when unapplied migrations are
detected.
``runserver`` ``runserver``
------------- -------------

View File

@ -300,6 +300,9 @@ Management Commands
enabled for all configured :setting:`DATABASES` by passing the ``database`` enabled for all configured :setting:`DATABASES` by passing the ``database``
tag to the command. tag to the command.
* The new :option:`migrate --check` option makes the command exit with a
non-zero status when unapplied migrations are detected.
Migrations Migrations
~~~~~~~~~~ ~~~~~~~~~~

View File

@ -249,6 +249,39 @@ class MigrateTests(MigrationTestBase):
with self.assertRaisesMessage(CommandError, "Conflicting migrations detected"): with self.assertRaisesMessage(CommandError, "Conflicting migrations detected"):
call_command("migrate", "migrations") call_command("migrate", "migrations")
@override_settings(MIGRATION_MODULES={
'migrations': 'migrations.test_migrations',
})
def test_migrate_check(self):
with self.assertRaises(SystemExit):
call_command('migrate', 'migrations', '0001', check_unapplied=True, verbosity=0)
self.assertTableNotExists('migrations_author')
self.assertTableNotExists('migrations_tribble')
self.assertTableNotExists('migrations_book')
@override_settings(MIGRATION_MODULES={
'migrations': 'migrations.test_migrations_plan',
})
def test_migrate_check_plan(self):
out = io.StringIO()
with self.assertRaises(SystemExit):
call_command(
'migrate',
'migrations',
'0001',
check_unapplied=True,
plan=True,
stdout=out,
no_color=True,
)
self.assertEqual(
'Planned operations:\n'
'migrations.0001_initial\n'
' Create model Salamander\n'
' Raw Python operation -> Grow salamander tail.\n',
out.getvalue(),
)
@override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"}) @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"})
def test_showmigrations_list(self): def test_showmigrations_list(self):
""" """