mirror of
https://github.com/django/django.git
synced 2024-12-22 09:05:43 +00:00
Fixed #35920 -- Observed requires_system_checks in migrate and runserver.
Before, the full suite of system checks was run by these commands regardless if requires_system_checks had been overridden. Co-authored-by: Simon Charette <charette.s@gmail.com>
This commit is contained in:
parent
a16eedcf9c
commit
2ce4545de1
@ -450,10 +450,8 @@ class BaseCommand:
|
||||
self.stderr = OutputWrapper(options["stderr"])
|
||||
|
||||
if self.requires_system_checks and not options["skip_checks"]:
|
||||
if self.requires_system_checks == ALL_CHECKS:
|
||||
self.check()
|
||||
else:
|
||||
self.check(tags=self.requires_system_checks)
|
||||
check_kwargs = self.get_check_kwargs(options)
|
||||
self.check(**check_kwargs)
|
||||
if self.requires_migrations_checks:
|
||||
self.check_migrations()
|
||||
output = self.handle(*args, **options)
|
||||
@ -468,6 +466,11 @@ class BaseCommand:
|
||||
self.stdout.write(output)
|
||||
return output
|
||||
|
||||
def get_check_kwargs(self, options):
|
||||
if self.requires_system_checks == ALL_CHECKS:
|
||||
return {}
|
||||
return {"tags": self.requires_system_checks}
|
||||
|
||||
def check(
|
||||
self,
|
||||
app_configs=None,
|
||||
|
@ -19,14 +19,8 @@ class Command(BaseCommand):
|
||||
help = (
|
||||
"Updates database schema. Manages both apps with migrations and those without."
|
||||
)
|
||||
requires_system_checks = []
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument(
|
||||
"--skip-checks",
|
||||
action="store_true",
|
||||
help="Skip system checks.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"app_label",
|
||||
nargs="?",
|
||||
@ -95,12 +89,13 @@ class Command(BaseCommand):
|
||||
help="Delete nonexistent migrations from the django_migrations table.",
|
||||
)
|
||||
|
||||
def get_check_kwargs(self, options):
|
||||
kwargs = super().get_check_kwargs(options)
|
||||
return {**kwargs, "databases": [options["database"]]}
|
||||
|
||||
@no_translations
|
||||
def handle(self, *args, **options):
|
||||
database = options["database"]
|
||||
if not options["skip_checks"]:
|
||||
self.check(databases=[database])
|
||||
|
||||
self.verbosity = options["verbosity"]
|
||||
self.interactive = options["interactive"]
|
||||
|
||||
|
@ -27,8 +27,6 @@ naiveip_re = _lazy_re_compile(
|
||||
class Command(BaseCommand):
|
||||
help = "Starts a lightweight web server for development."
|
||||
|
||||
# Validation is called explicitly each time the server is reloaded.
|
||||
requires_system_checks = []
|
||||
stealth_options = ("shutdown_message",)
|
||||
suppressed_base_arguments = {"--verbosity", "--traceback"}
|
||||
|
||||
@ -61,11 +59,6 @@ class Command(BaseCommand):
|
||||
dest="use_reloader",
|
||||
help="Tells Django to NOT use the auto-reloader.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--skip-checks",
|
||||
action="store_true",
|
||||
help="Skip system checks.",
|
||||
)
|
||||
|
||||
def execute(self, *args, **options):
|
||||
if options["no_color"]:
|
||||
@ -79,6 +72,10 @@ class Command(BaseCommand):
|
||||
"""Return the default WSGI handler for the runner."""
|
||||
return get_internal_wsgi_application()
|
||||
|
||||
def get_check_kwargs(self, options):
|
||||
"""Validation is called explicitly each time the server reloads."""
|
||||
return {"tags": set()}
|
||||
|
||||
def handle(self, *args, **options):
|
||||
if not settings.DEBUG and not settings.ALLOWED_HOSTS:
|
||||
raise CommandError("You must set settings.ALLOWED_HOSTS if DEBUG is False.")
|
||||
@ -132,7 +129,9 @@ class Command(BaseCommand):
|
||||
|
||||
if not options["skip_checks"]:
|
||||
self.stdout.write("Performing system checks...\n\n")
|
||||
self.check(display_num_errors=True)
|
||||
check_kwargs = super().get_check_kwargs(options)
|
||||
check_kwargs["display_num_errors"] = True
|
||||
self.check(**check_kwargs)
|
||||
# Need to check migrations here, so can't use the
|
||||
# requires_migrations_check attribute.
|
||||
self.check_migrations()
|
||||
|
@ -319,6 +319,21 @@ the :meth:`~BaseCommand.handle` method must be implemented.
|
||||
checks, and list of database aliases in the ``databases`` to run database
|
||||
related checks against them.
|
||||
|
||||
.. method:: BaseCommand.get_check_kwargs(options)
|
||||
|
||||
.. versionadded:: 5.2
|
||||
|
||||
Supplies kwargs for the call to :meth:`check`, including transforming the
|
||||
value of :attr:`requires_system_checks` to the ``tag`` kwarg.
|
||||
|
||||
Override this method to change the values supplied to :meth:`check`. For
|
||||
example, to opt into database related checks you can override
|
||||
``get_check_kwargs()`` as follows::
|
||||
|
||||
def get_check_kwargs(self, options):
|
||||
kwargs = super().get_check_kwargs(options)
|
||||
return {**kwargs, "databases": [options["database"]]}
|
||||
|
||||
.. _ref-basecommand-subclasses:
|
||||
|
||||
``BaseCommand`` subclasses
|
||||
|
@ -279,6 +279,10 @@ Management Commands
|
||||
``Command.autodetector`` attribute for subclasses to override in order to use
|
||||
a custom autodetector class.
|
||||
|
||||
* The new :meth:`.BaseCommand.get_check_kwargs` method can be overridden in
|
||||
custom commands to control the running of system checks, e.g. to opt into
|
||||
database-dependent checks.
|
||||
|
||||
Migrations
|
||||
~~~~~~~~~~
|
||||
|
||||
|
@ -20,6 +20,8 @@ from user_commands.utils import AssertFormatterFailureCaughtContext
|
||||
|
||||
from django import conf, get_version
|
||||
from django.conf import settings
|
||||
from django.core.checks import Error, Tags, register
|
||||
from django.core.checks.registry import registry
|
||||
from django.core.management import (
|
||||
BaseCommand,
|
||||
CommandError,
|
||||
@ -27,7 +29,7 @@ from django.core.management import (
|
||||
color,
|
||||
execute_from_command_line,
|
||||
)
|
||||
from django.core.management.base import LabelCommand
|
||||
from django.core.management.base import LabelCommand, SystemCheckError
|
||||
from django.core.management.commands.loaddata import Command as LoaddataCommand
|
||||
from django.core.management.commands.runserver import Command as RunserverCommand
|
||||
from django.core.management.commands.testserver import Command as TestserverCommand
|
||||
@ -1733,7 +1735,53 @@ class ManageRunserver(SimpleTestCase):
|
||||
stdout=self.output,
|
||||
)
|
||||
self.assertIn("Performing system checks...", self.output.getvalue())
|
||||
mocked_check.assert_called()
|
||||
mocked_check.assert_has_calls(
|
||||
[mock.call(tags=set()), mock.call(display_num_errors=True)]
|
||||
)
|
||||
|
||||
def test_custom_system_checks(self):
|
||||
original_checks = registry.registered_checks.copy()
|
||||
|
||||
@register(Tags.signals)
|
||||
def my_check(app_configs, **kwargs):
|
||||
return [Error("my error")]
|
||||
|
||||
class CustomException(Exception):
|
||||
pass
|
||||
|
||||
self.addCleanup(setattr, registry, "registered_checks", original_checks)
|
||||
|
||||
class CustomRunserverCommand(RunserverCommand):
|
||||
"""Rather than mock run(), raise immediately after system checks run."""
|
||||
|
||||
def check_migrations(self, *args, **kwargs):
|
||||
raise CustomException
|
||||
|
||||
class CustomRunserverCommandWithSignalsChecks(CustomRunserverCommand):
|
||||
requires_system_checks = [Tags.signals]
|
||||
|
||||
command = CustomRunserverCommandWithSignalsChecks()
|
||||
with self.assertRaises(SystemCheckError):
|
||||
call_command(
|
||||
command,
|
||||
use_reloader=False,
|
||||
skip_checks=False,
|
||||
stdout=StringIO(),
|
||||
stderr=StringIO(),
|
||||
)
|
||||
|
||||
class CustomMigrateCommandWithSecurityChecks(CustomRunserverCommand):
|
||||
requires_system_checks = [Tags.security]
|
||||
|
||||
command = CustomMigrateCommandWithSecurityChecks()
|
||||
with self.assertRaises(CustomException):
|
||||
call_command(
|
||||
command,
|
||||
use_reloader=False,
|
||||
skip_checks=False,
|
||||
stdout=StringIO(),
|
||||
stderr=StringIO(),
|
||||
)
|
||||
|
||||
|
||||
class ManageRunserverMigrationWarning(TestCase):
|
||||
|
@ -8,6 +8,8 @@ from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
from django.apps import apps
|
||||
from django.core.checks import Error, Tags, register
|
||||
from django.core.checks.registry import registry
|
||||
from django.core.management import CommandError, call_command
|
||||
from django.core.management.base import SystemCheckError
|
||||
from django.core.management.commands.makemigrations import (
|
||||
@ -96,6 +98,7 @@ class MigrateTests(MigrationTestBase):
|
||||
self.assertTableNotExists("migrations_tribble")
|
||||
self.assertTableNotExists("migrations_book")
|
||||
|
||||
@mock.patch("django.core.management.base.BaseCommand.check")
|
||||
@override_settings(
|
||||
INSTALLED_APPS=[
|
||||
"django.contrib.auth",
|
||||
@ -103,10 +106,33 @@ class MigrateTests(MigrationTestBase):
|
||||
"migrations.migrations_test_apps.migrated_app",
|
||||
]
|
||||
)
|
||||
def test_migrate_with_system_checks(self):
|
||||
def test_migrate_with_system_checks(self, mocked_check):
|
||||
out = io.StringIO()
|
||||
call_command("migrate", skip_checks=False, no_color=True, stdout=out)
|
||||
self.assertIn("Apply all migrations: migrated_app", out.getvalue())
|
||||
mocked_check.assert_called_once()
|
||||
|
||||
def test_migrate_with_custom_system_checks(self):
|
||||
original_checks = registry.registered_checks.copy()
|
||||
|
||||
@register(Tags.signals)
|
||||
def my_check(app_configs, **kwargs):
|
||||
return [Error("my error")]
|
||||
|
||||
self.addCleanup(setattr, registry, "registered_checks", original_checks)
|
||||
|
||||
class CustomMigrateCommandWithSignalsChecks(MigrateCommand):
|
||||
requires_system_checks = [Tags.signals]
|
||||
|
||||
command = CustomMigrateCommandWithSignalsChecks()
|
||||
with self.assertRaises(SystemCheckError):
|
||||
call_command(command, skip_checks=False, stderr=io.StringIO())
|
||||
|
||||
class CustomMigrateCommandWithSecurityChecks(MigrateCommand):
|
||||
requires_system_checks = [Tags.security]
|
||||
|
||||
command = CustomMigrateCommandWithSecurityChecks()
|
||||
call_command(command, skip_checks=False, stdout=io.StringIO())
|
||||
|
||||
@override_settings(
|
||||
INSTALLED_APPS=[
|
||||
|
Loading…
Reference in New Issue
Block a user