Pārlūkot izejas kodu

Fixed #29783 -- Added app label validation to showmigrations command.

Junyoung 6 gadi atpakaļ
vecāks
revīzija
df448bfd02

+ 1 - 0
AUTHORS

@@ -449,6 +449,7 @@ answer newbie questions, and generally made Django that much better:
     Julia Matsieva <julia.matsieva@gmail.com>
     Julian Bez
     Julien Phalip <jphalip@gmail.com>
+    Junyoung Choi <cupjoo@gmail.com>
     junzhang.jn@gmail.com
     Jure Cuhalev <gandalf@owca.info>
     Justin Bronn <jbronn@gmail.com>

+ 14 - 6
django/core/management/commands/showmigrations.py

@@ -1,4 +1,7 @@
-from django.core.management.base import BaseCommand, CommandError
+import sys
+
+from django.apps import apps
+from django.core.management.base import BaseCommand
 from django.db import DEFAULT_DB_ALIAS, connections
 from django.db.migrations.loader import MigrationLoader
 
@@ -45,12 +48,15 @@ class Command(BaseCommand):
             return self.show_list(connection, options['app_label'])
 
     def _validate_app_names(self, loader, app_names):
-        invalid_apps = []
+        has_bad_names = False
         for app_name in app_names:
-            if app_name not in loader.migrated_apps:
-                invalid_apps.append(app_name)
-        if invalid_apps:
-            raise CommandError('No migrations present for: %s' % (', '.join(sorted(invalid_apps))))
+            try:
+                apps.get_app_config(app_name)
+            except LookupError as err:
+                self.stderr.write(str(err))
+                has_bad_names = True
+        if has_bad_names:
+            sys.exit(2)
 
     def show_list(self, connection, app_names=None):
         """
@@ -129,3 +135,5 @@ class Command(BaseCommand):
                 self.stdout.write("[X]  %s.%s%s" % (node.key[0], node.key[1], deps))
             else:
                 self.stdout.write("[ ]  %s.%s%s" % (node.key[0], node.key[1], deps))
+        if not plan:
+            self.stdout.write('(no migrations)', self.style.ERROR)

+ 23 - 22
tests/migrations/test_commands.py

@@ -381,8 +381,9 @@ class MigrateTests(MigrationTestBase):
 
     @override_settings(INSTALLED_APPS=['migrations.migrations_test_apps.unmigrated_app'])
     def test_showmigrations_unmigrated_app(self):
-        with self.assertRaisesMessage(CommandError, 'No migrations present for: unmigrated_app'):
-            call_command('showmigrations', 'unmigrated_app')
+        out = io.StringIO()
+        call_command('showmigrations', 'unmigrated_app', stdout=out, no_color=True)
+        self.assertEqual('unmigrated_app\n (no migrations)\n', out.getvalue().lower())
 
     @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_empty"})
     def test_showmigrations_plan_no_migrations(self):
@@ -390,12 +391,12 @@ class MigrateTests(MigrationTestBase):
         Tests --plan output of showmigrations command without migrations
         """
         out = io.StringIO()
-        call_command("showmigrations", format='plan', stdout=out)
-        self.assertEqual("", out.getvalue().lower())
+        call_command('showmigrations', format='plan', stdout=out, no_color=True)
+        self.assertEqual('(no migrations)\n', out.getvalue().lower())
 
         out = io.StringIO()
-        call_command("showmigrations", format='plan', stdout=out, verbosity=2)
-        self.assertEqual("", out.getvalue().lower())
+        call_command('showmigrations', format='plan', stdout=out, verbosity=2, no_color=True)
+        self.assertEqual('(no migrations)\n', out.getvalue().lower())
 
     @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations_squashed_complex"})
     def test_showmigrations_plan_squashed(self):
@@ -522,22 +523,10 @@ class MigrateTests(MigrationTestBase):
         )
 
     @override_settings(INSTALLED_APPS=['migrations.migrations_test_apps.unmigrated_app'])
-    def test_showmigrations_plan_app_label_error(self):
-        """
-        `showmigrations --plan app_label` raises an error when no app or
-        no migrations are present in provided app labels.
-        """
-        # App with no migrations.
-        with self.assertRaisesMessage(CommandError, 'No migrations present for: unmigrated_app'):
-            call_command('showmigrations', 'unmigrated_app', format='plan')
-        # Nonexistent app (wrong app label).
-        with self.assertRaisesMessage(CommandError, 'No migrations present for: nonexistent_app'):
-            call_command('showmigrations', 'nonexistent_app', format='plan')
-        # Multiple nonexistent apps; input order shouldn't matter.
-        with self.assertRaisesMessage(CommandError, 'No migrations present for: nonexistent_app1, nonexistent_app2'):
-            call_command('showmigrations', 'nonexistent_app1', 'nonexistent_app2', format='plan')
-        with self.assertRaisesMessage(CommandError, 'No migrations present for: nonexistent_app1, nonexistent_app2'):
-            call_command('showmigrations', 'nonexistent_app2', 'nonexistent_app1', format='plan')
+    def test_showmigrations_plan_app_label_no_migrations(self):
+        out = io.StringIO()
+        call_command('showmigrations', 'unmigrated_app', format='plan', stdout=out, no_color=True)
+        self.assertEqual('(no migrations)\n', out.getvalue())
 
     @override_settings(MIGRATION_MODULES={"migrations": "migrations.test_migrations"})
     def test_sqlmigrate_forwards(self):
@@ -1561,6 +1550,18 @@ class AppLabelErrorTests(TestCase):
         with self.assertRaisesMessage(CommandError, self.did_you_mean_auth_error):
             call_command('migrate', 'django.contrib.auth')
 
+    def test_showmigrations_nonexistent_app_label(self):
+        err = io.StringIO()
+        with self.assertRaises(SystemExit):
+            call_command('showmigrations', 'nonexistent_app', stderr=err)
+        self.assertIn(self.nonexistent_app_error, err.getvalue())
+
+    def test_showmigrations_app_name_specified_as_label(self):
+        err = io.StringIO()
+        with self.assertRaises(SystemExit):
+            call_command('showmigrations', 'django.contrib.auth', stderr=err)
+        self.assertIn(self.did_you_mean_auth_error, err.getvalue())
+
     def test_sqlmigrate_nonexistent_app_label(self):
         with self.assertRaisesMessage(CommandError, self.nonexistent_app_error):
             call_command('sqlmigrate', 'nonexistent_app', '0002')