2008-09-22 14:03:24 +08:00
|
|
|
"""
|
|
|
|
Base classes for writing management commands (named commands which can
|
2014-07-26 19:21:52 +08:00
|
|
|
be executed through ``django-admin`` or ``manage.py``).
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
2007-10-14 00:02:54 +08:00
|
|
|
import os
|
|
|
|
import sys
|
2013-10-16 22:24:59 +08:00
|
|
|
from argparse import ArgumentParser
|
2017-01-12 06:17:25 +08:00
|
|
|
from io import TextIOBase
|
2007-10-14 00:02:54 +08:00
|
|
|
|
2007-09-10 05:57:59 +08:00
|
|
|
import django
|
2014-01-20 10:45:21 +08:00
|
|
|
from django.core import checks
|
2016-02-12 18:02:36 +08:00
|
|
|
from django.core.exceptions import ImproperlyConfigured
|
2013-02-23 23:28:45 +08:00
|
|
|
from django.core.management.color import color_style, no_style
|
2016-02-12 18:02:36 +08:00
|
|
|
from django.db import DEFAULT_DB_ALIAS, connections
|
|
|
|
from django.db.migrations.exceptions import MigrationSchemaMissing
|
2007-08-16 14:06:55 +08:00
|
|
|
|
2011-12-23 06:38:02 +08:00
|
|
|
|
2007-08-16 14:06:55 +08:00
|
|
|
class CommandError(Exception):
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
|
|
|
Exception class indicating a problem while executing a management
|
|
|
|
command.
|
|
|
|
|
|
|
|
If this exception is raised during the execution of a management
|
|
|
|
command, it will be caught and turned into a nicely-printed error
|
|
|
|
message to the appropriate output stream (i.e., stderr); as a
|
|
|
|
result, raising this exception (with a sensible description of the
|
|
|
|
error) is the preferred way to indicate that something has gone
|
|
|
|
wrong in the execution of a command.
|
|
|
|
"""
|
2007-08-16 14:06:55 +08:00
|
|
|
pass
|
|
|
|
|
2011-12-23 06:38:02 +08:00
|
|
|
|
2014-10-20 19:10:53 +08:00
|
|
|
class SystemCheckError(CommandError):
|
|
|
|
"""
|
|
|
|
The system check framework detected unrecoverable errors.
|
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2013-10-16 22:24:59 +08:00
|
|
|
class CommandParser(ArgumentParser):
|
|
|
|
"""
|
|
|
|
Customized ArgumentParser class to improve some error messages and prevent
|
|
|
|
SystemExit in several occasions, as SystemExit is unacceptable when a
|
|
|
|
command is called programmatically.
|
|
|
|
"""
|
|
|
|
def __init__(self, cmd, **kwargs):
|
|
|
|
self.cmd = cmd
|
2017-01-21 21:13:44 +08:00
|
|
|
super().__init__(**kwargs)
|
2013-10-16 22:24:59 +08:00
|
|
|
|
|
|
|
def parse_args(self, args=None, namespace=None):
|
|
|
|
# Catch missing argument for a better error message
|
|
|
|
if (hasattr(self.cmd, 'missing_args_message') and
|
2014-12-07 05:00:09 +08:00
|
|
|
not (args or any(not arg.startswith('-') for arg in args))):
|
2014-06-15 00:12:43 +08:00
|
|
|
self.error(self.cmd.missing_args_message)
|
2017-01-21 21:13:44 +08:00
|
|
|
return super().parse_args(args, namespace)
|
2013-10-16 22:24:59 +08:00
|
|
|
|
|
|
|
def error(self, message):
|
2014-06-15 00:12:43 +08:00
|
|
|
if self.cmd._called_from_command_line:
|
2017-01-21 21:13:44 +08:00
|
|
|
super().error(message)
|
2014-06-15 00:12:43 +08:00
|
|
|
else:
|
|
|
|
raise CommandError("Error: %s" % message)
|
2013-10-16 22:24:59 +08:00
|
|
|
|
|
|
|
|
2007-09-22 00:19:20 +08:00
|
|
|
def handle_default_options(options):
|
|
|
|
"""
|
2008-09-22 14:03:24 +08:00
|
|
|
Include any default options that all commands should accept here
|
|
|
|
so that ManagementUtility can handle them before searching for
|
|
|
|
user commands.
|
2007-09-22 00:19:20 +08:00
|
|
|
"""
|
|
|
|
if options.settings:
|
|
|
|
os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
|
|
|
|
if options.pythonpath:
|
|
|
|
sys.path.insert(0, options.pythonpath)
|
2007-10-14 00:40:20 +08:00
|
|
|
|
2011-12-23 06:38:02 +08:00
|
|
|
|
2017-01-12 06:17:25 +08:00
|
|
|
class OutputWrapper(TextIOBase):
|
2012-05-19 19:51:54 +08:00
|
|
|
"""
|
|
|
|
Wrapper around stdout/stderr
|
|
|
|
"""
|
2014-10-22 02:11:31 +08:00
|
|
|
@property
|
|
|
|
def style_func(self):
|
|
|
|
return self._style_func
|
|
|
|
|
|
|
|
@style_func.setter
|
|
|
|
def style_func(self, style_func):
|
2015-07-22 05:24:32 +08:00
|
|
|
if style_func and self.isatty():
|
2014-10-22 02:11:31 +08:00
|
|
|
self._style_func = style_func
|
|
|
|
else:
|
|
|
|
self._style_func = lambda x: x
|
|
|
|
|
2012-05-26 17:43:37 +08:00
|
|
|
def __init__(self, out, style_func=None, ending='\n'):
|
2012-05-19 19:51:54 +08:00
|
|
|
self._out = out
|
|
|
|
self.style_func = None
|
2012-05-26 17:43:37 +08:00
|
|
|
self.ending = ending
|
2012-05-19 19:51:54 +08:00
|
|
|
|
|
|
|
def __getattr__(self, name):
|
|
|
|
return getattr(self._out, name)
|
|
|
|
|
2015-07-22 05:24:32 +08:00
|
|
|
def isatty(self):
|
|
|
|
return hasattr(self._out, 'isatty') and self._out.isatty()
|
|
|
|
|
2012-05-26 17:43:37 +08:00
|
|
|
def write(self, msg, style_func=None, ending=None):
|
2013-05-17 22:33:36 +08:00
|
|
|
ending = self.ending if ending is None else ending
|
2012-05-19 19:51:54 +08:00
|
|
|
if ending and not msg.endswith(ending):
|
|
|
|
msg += ending
|
2014-10-22 02:11:31 +08:00
|
|
|
style_func = style_func or self.style_func
|
2017-01-12 06:17:25 +08:00
|
|
|
self._out.write(style_func(msg))
|
2012-05-19 19:51:54 +08:00
|
|
|
|
|
|
|
|
2017-01-19 15:39:46 +08:00
|
|
|
class BaseCommand:
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
|
|
|
The base class from which all management commands ultimately
|
|
|
|
derive.
|
|
|
|
|
|
|
|
Use this class if you want access to all of the mechanisms which
|
|
|
|
parse the command-line arguments and work out what code to call in
|
|
|
|
response; if you don't need to change any of that behavior,
|
|
|
|
consider using one of the subclasses defined in this file.
|
|
|
|
|
|
|
|
If you are interested in overriding/customizing various aspects of
|
|
|
|
the command-parsing and -execution behavior, the normal flow works
|
|
|
|
as follows:
|
|
|
|
|
2014-07-26 19:21:52 +08:00
|
|
|
1. ``django-admin`` or ``manage.py`` loads the command class
|
2008-09-22 14:03:24 +08:00
|
|
|
and calls its ``run_from_argv()`` method.
|
|
|
|
|
|
|
|
2. The ``run_from_argv()`` method calls ``create_parser()`` to get
|
2013-10-16 22:24:59 +08:00
|
|
|
an ``ArgumentParser`` for the arguments, parses them, performs
|
2008-09-22 14:03:24 +08:00
|
|
|
any environment changes requested by options like
|
|
|
|
``pythonpath``, and then calls the ``execute()`` method,
|
|
|
|
passing the parsed arguments.
|
|
|
|
|
|
|
|
3. The ``execute()`` method attempts to carry out the command by
|
|
|
|
calling the ``handle()`` method with the parsed arguments; any
|
|
|
|
output produced by ``handle()`` will be printed to standard
|
|
|
|
output and, if the command is intended to produce a block of
|
|
|
|
SQL statements, will be wrapped in ``BEGIN`` and ``COMMIT``.
|
|
|
|
|
2012-05-27 02:50:44 +08:00
|
|
|
4. If ``handle()`` or ``execute()`` raised any exception (e.g.
|
|
|
|
``CommandError``), ``run_from_argv()`` will instead print an error
|
|
|
|
message to ``stderr``.
|
2008-09-22 14:03:24 +08:00
|
|
|
|
|
|
|
Thus, the ``handle()`` method is typically the starting point for
|
|
|
|
subclasses; many built-in commands and command types either place
|
|
|
|
all of their logic in ``handle()``, or perform some additional
|
|
|
|
parsing work in ``handle()`` and then delegate from it to more
|
|
|
|
specialized methods as needed.
|
|
|
|
|
|
|
|
Several attributes affect behavior at various steps along the way:
|
2010-03-18 21:24:11 +08:00
|
|
|
|
2008-09-22 14:03:24 +08:00
|
|
|
``help``
|
|
|
|
A short description of the command, which will be printed in
|
|
|
|
help messages.
|
|
|
|
|
|
|
|
``output_transaction``
|
|
|
|
A boolean indicating whether the command outputs SQL
|
|
|
|
statements; if ``True``, the output will automatically be
|
|
|
|
wrapped with ``BEGIN;`` and ``COMMIT;``. Default value is
|
|
|
|
``False``.
|
|
|
|
|
2016-02-12 18:02:36 +08:00
|
|
|
``requires_migrations_checks``
|
|
|
|
A boolean; if ``True``, the command prints a warning if the set of
|
|
|
|
migrations on disk don't match the migrations in the database.
|
|
|
|
|
2014-01-20 10:45:21 +08:00
|
|
|
``requires_system_checks``
|
|
|
|
A boolean; if ``True``, entire Django project will be checked for errors
|
|
|
|
prior to executing the command. Default value is ``True``.
|
|
|
|
To validate an individual application's models
|
|
|
|
rather than all applications' models, call
|
|
|
|
``self.check(app_configs)`` from ``handle()``, where ``app_configs``
|
|
|
|
is the list of application's configuration provided by the
|
|
|
|
app registry.
|
|
|
|
|
2013-02-04 07:53:48 +08:00
|
|
|
``leave_locale_alone``
|
|
|
|
A boolean indicating whether the locale set in settings should be
|
2015-01-04 03:27:18 +08:00
|
|
|
preserved during the execution of the command instead of translations
|
|
|
|
being deactivated.
|
2013-02-04 07:53:48 +08:00
|
|
|
|
|
|
|
Default value is ``False``.
|
|
|
|
|
|
|
|
Make sure you know what you are doing if you decide to change the value
|
2013-02-04 19:55:45 +08:00
|
|
|
of this option in your custom command if it creates database content
|
|
|
|
that is locale-sensitive and such content shouldn't contain any
|
2015-01-04 03:27:18 +08:00
|
|
|
translations (like it happens e.g. with django.contrib.auth
|
|
|
|
permissions) as activating any locale might cause unintended effects.
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
2007-08-16 14:06:55 +08:00
|
|
|
# Metadata about this command.
|
|
|
|
help = ''
|
|
|
|
|
|
|
|
# Configuration shortcuts that alter various logic.
|
2014-06-15 00:12:43 +08:00
|
|
|
_called_from_command_line = False
|
2011-12-23 06:38:02 +08:00
|
|
|
output_transaction = False # Whether to wrap the output in a "BEGIN; COMMIT;"
|
2013-02-04 07:53:48 +08:00
|
|
|
leave_locale_alone = False
|
2016-02-12 18:02:36 +08:00
|
|
|
requires_migrations_checks = False
|
2015-01-18 01:59:07 +08:00
|
|
|
requires_system_checks = True
|
2014-01-20 10:45:21 +08:00
|
|
|
|
2014-10-22 02:11:31 +08:00
|
|
|
def __init__(self, stdout=None, stderr=None, no_color=False):
|
|
|
|
self.stdout = OutputWrapper(stdout or sys.stdout)
|
|
|
|
self.stderr = OutputWrapper(stderr or sys.stderr)
|
|
|
|
if no_color:
|
|
|
|
self.style = no_style()
|
|
|
|
else:
|
|
|
|
self.style = color_style()
|
|
|
|
self.stderr.style_func = self.style.ERROR
|
2007-08-16 14:06:55 +08:00
|
|
|
|
2007-09-10 05:57:59 +08:00
|
|
|
def get_version(self):
|
|
|
|
"""
|
2015-11-14 00:06:22 +08:00
|
|
|
Return the Django version, which should be correct for all built-in
|
|
|
|
Django commands. User-supplied commands can override this method to
|
|
|
|
return their own version.
|
2007-09-10 05:57:59 +08:00
|
|
|
"""
|
|
|
|
return django.get_version()
|
|
|
|
|
2007-09-11 12:24:35 +08:00
|
|
|
def create_parser(self, prog_name, subcommand):
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
2013-10-16 22:24:59 +08:00
|
|
|
Create and return the ``ArgumentParser`` which will be used to
|
2008-09-22 14:03:24 +08:00
|
|
|
parse the arguments to this command.
|
|
|
|
"""
|
2016-03-29 06:33:29 +08:00
|
|
|
parser = CommandParser(
|
|
|
|
self, prog="%s %s" % (os.path.basename(prog_name), subcommand),
|
|
|
|
description=self.help or None,
|
|
|
|
)
|
2015-08-19 00:46:03 +08:00
|
|
|
parser.add_argument('--version', action='version', version=self.get_version())
|
2016-03-29 06:33:29 +08:00
|
|
|
parser.add_argument(
|
|
|
|
'-v', '--verbosity', action='store', dest='verbosity', default=1,
|
2015-08-19 00:46:03 +08:00
|
|
|
type=int, choices=[0, 1, 2, 3],
|
2016-03-29 06:33:29 +08:00
|
|
|
help='Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output',
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
'--settings',
|
2015-08-19 00:46:03 +08:00
|
|
|
help=(
|
|
|
|
'The Python path to a settings module, e.g. '
|
|
|
|
'"myproject.settings.main". If this isn\'t provided, the '
|
|
|
|
'DJANGO_SETTINGS_MODULE environment variable will be used.'
|
|
|
|
),
|
|
|
|
)
|
2016-03-29 06:33:29 +08:00
|
|
|
parser.add_argument(
|
|
|
|
'--pythonpath',
|
|
|
|
help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".',
|
|
|
|
)
|
|
|
|
parser.add_argument('--traceback', action='store_true', help='Raise on CommandError exceptions')
|
|
|
|
parser.add_argument(
|
|
|
|
'--no-color', action='store_true', dest='no_color', default=False,
|
|
|
|
help="Don't colorize the command output.",
|
|
|
|
)
|
2015-08-19 00:46:03 +08:00
|
|
|
self.add_arguments(parser)
|
2013-10-16 22:24:59 +08:00
|
|
|
return parser
|
|
|
|
|
|
|
|
def add_arguments(self, parser):
|
|
|
|
"""
|
|
|
|
Entry point for subclassed commands to add custom arguments.
|
|
|
|
"""
|
|
|
|
pass
|
2007-09-10 05:57:59 +08:00
|
|
|
|
2007-09-11 12:24:35 +08:00
|
|
|
def print_help(self, prog_name, subcommand):
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
|
|
|
Print the help message for this command, derived from
|
|
|
|
``self.usage()``.
|
|
|
|
"""
|
2007-09-11 12:24:35 +08:00
|
|
|
parser = self.create_parser(prog_name, subcommand)
|
2007-09-10 05:57:59 +08:00
|
|
|
parser.print_help()
|
|
|
|
|
2007-09-11 11:46:35 +08:00
|
|
|
def run_from_argv(self, argv):
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
|
|
|
Set up any environment changes requested (e.g., Python path
|
2012-05-27 02:50:44 +08:00
|
|
|
and Django settings), then run this command. If the
|
|
|
|
command raises a ``CommandError``, intercept it and print it sensibly
|
2013-05-19 00:04:45 +08:00
|
|
|
to stderr. If the ``--traceback`` option is present or the raised
|
|
|
|
``Exception`` is not ``CommandError``, raise it.
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
2014-06-15 00:12:43 +08:00
|
|
|
self._called_from_command_line = True
|
2007-09-11 12:24:35 +08:00
|
|
|
parser = self.create_parser(argv[0], argv[1])
|
2013-10-16 22:24:59 +08:00
|
|
|
|
2015-08-19 00:46:03 +08:00
|
|
|
options = parser.parse_args(argv[2:])
|
|
|
|
cmd_options = vars(options)
|
|
|
|
# Move positional args out of options to mimic legacy optparse
|
|
|
|
args = cmd_options.pop('args', ())
|
2007-09-22 00:19:20 +08:00
|
|
|
handle_default_options(options)
|
2012-05-27 02:50:44 +08:00
|
|
|
try:
|
2013-10-16 22:24:59 +08:00
|
|
|
self.execute(*args, **cmd_options)
|
2012-05-27 02:50:44 +08:00
|
|
|
except Exception as e:
|
2013-05-19 00:04:45 +08:00
|
|
|
if options.traceback or not isinstance(e, CommandError):
|
|
|
|
raise
|
|
|
|
|
2014-10-20 19:10:53 +08:00
|
|
|
# SystemCheckError takes care of its own formatting.
|
|
|
|
if isinstance(e, SystemCheckError):
|
|
|
|
self.stderr.write(str(e), lambda x: x)
|
|
|
|
else:
|
|
|
|
self.stderr.write('%s: %s' % (e.__class__.__name__, e))
|
2012-05-27 02:50:44 +08:00
|
|
|
sys.exit(1)
|
2014-12-29 01:10:12 +08:00
|
|
|
finally:
|
2016-09-29 21:05:23 +08:00
|
|
|
try:
|
|
|
|
connections.close_all()
|
|
|
|
except ImproperlyConfigured:
|
|
|
|
# Ignore if connections aren't setup at this point (e.g. no
|
|
|
|
# configured settings).
|
|
|
|
pass
|
2007-09-10 05:57:59 +08:00
|
|
|
|
2007-08-16 14:06:55 +08:00
|
|
|
def execute(self, *args, **options):
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
2014-01-20 10:45:21 +08:00
|
|
|
Try to execute this command, performing system checks if needed (as
|
2015-01-18 01:59:07 +08:00
|
|
|
controlled by the ``requires_system_checks`` attribute, except if
|
|
|
|
force-skipped).
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
2016-03-03 10:01:36 +08:00
|
|
|
if options['no_color']:
|
2013-02-23 23:28:45 +08:00
|
|
|
self.style = no_style()
|
2014-10-22 02:11:31 +08:00
|
|
|
self.stderr.style_func = None
|
|
|
|
if options.get('stdout'):
|
|
|
|
self.stdout = OutputWrapper(options['stdout'])
|
|
|
|
if options.get('stderr'):
|
2016-03-03 10:01:36 +08:00
|
|
|
self.stderr = OutputWrapper(options['stderr'], self.stderr.style_func)
|
2012-05-19 19:51:54 +08:00
|
|
|
|
2013-02-04 07:53:48 +08:00
|
|
|
saved_locale = None
|
|
|
|
if not self.leave_locale_alone:
|
2015-01-04 03:27:18 +08:00
|
|
|
# Deactivate translations, because django-admin creates database
|
2013-02-04 07:53:48 +08:00
|
|
|
# content like permissions, and those shouldn't contain any
|
|
|
|
# translations.
|
2012-05-27 02:50:44 +08:00
|
|
|
from django.utils import translation
|
2013-02-04 07:53:48 +08:00
|
|
|
saved_locale = translation.get_language()
|
2015-01-04 03:27:18 +08:00
|
|
|
translation.deactivate_all()
|
2011-12-11 18:03:09 +08:00
|
|
|
|
2007-08-16 14:06:55 +08:00
|
|
|
try:
|
2015-12-22 03:26:42 +08:00
|
|
|
if self.requires_system_checks and not options.get('skip_checks'):
|
2014-01-20 10:45:21 +08:00
|
|
|
self.check()
|
2016-02-12 18:02:36 +08:00
|
|
|
if self.requires_migrations_checks:
|
|
|
|
self.check_migrations()
|
2007-08-16 14:06:55 +08:00
|
|
|
output = self.handle(*args, **options)
|
|
|
|
if output:
|
|
|
|
if self.output_transaction:
|
2010-05-28 21:46:12 +08:00
|
|
|
connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
|
2016-02-14 01:14:36 +08:00
|
|
|
output = '%s\n%s\n%s' % (
|
|
|
|
self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()),
|
|
|
|
output,
|
|
|
|
self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()),
|
|
|
|
)
|
2010-06-05 13:26:04 +08:00
|
|
|
self.stdout.write(output)
|
2012-03-30 00:33:48 +08:00
|
|
|
finally:
|
2013-02-04 07:53:48 +08:00
|
|
|
if saved_locale is not None:
|
|
|
|
translation.activate(saved_locale)
|
2016-02-14 01:14:36 +08:00
|
|
|
return output
|
2007-08-16 14:06:55 +08:00
|
|
|
|
2016-03-19 19:15:09 +08:00
|
|
|
def _run_checks(self, **kwargs):
|
|
|
|
return checks.run_checks(**kwargs)
|
|
|
|
|
2014-09-13 02:50:36 +08:00
|
|
|
def check(self, app_configs=None, tags=None, display_num_errors=False,
|
2015-10-05 02:16:12 +08:00
|
|
|
include_deployment_checks=False, fail_level=checks.ERROR):
|
2007-08-16 14:06:55 +08:00
|
|
|
"""
|
2017-01-26 03:02:33 +08:00
|
|
|
Use the system check framework to validate entire Django project.
|
|
|
|
Raise CommandError for any serious message (error or critical errors).
|
|
|
|
If there are only light messages (like warnings), print them to stderr
|
|
|
|
and don't raise an exception.
|
2014-01-20 10:45:21 +08:00
|
|
|
"""
|
2016-03-19 19:15:09 +08:00
|
|
|
all_issues = self._run_checks(
|
2014-09-13 02:50:36 +08:00
|
|
|
app_configs=app_configs,
|
|
|
|
tags=tags,
|
|
|
|
include_deployment_checks=include_deployment_checks,
|
|
|
|
)
|
2014-01-20 10:45:21 +08:00
|
|
|
|
2014-10-20 19:10:53 +08:00
|
|
|
header, body, footer = "", "", ""
|
2014-01-23 03:09:02 +08:00
|
|
|
visible_issue_count = 0 # excludes silenced warnings
|
|
|
|
|
2014-01-20 10:45:21 +08:00
|
|
|
if all_issues:
|
|
|
|
debugs = [e for e in all_issues if e.level < checks.INFO and not e.is_silenced()]
|
|
|
|
infos = [e for e in all_issues if checks.INFO <= e.level < checks.WARNING and not e.is_silenced()]
|
|
|
|
warnings = [e for e in all_issues if checks.WARNING <= e.level < checks.ERROR and not e.is_silenced()]
|
2015-08-27 03:47:41 +08:00
|
|
|
errors = [e for e in all_issues if checks.ERROR <= e.level < checks.CRITICAL and not e.is_silenced()]
|
|
|
|
criticals = [e for e in all_issues if checks.CRITICAL <= e.level and not e.is_silenced()]
|
2014-01-20 10:45:21 +08:00
|
|
|
sorted_issues = [
|
|
|
|
(criticals, 'CRITICALS'),
|
|
|
|
(errors, 'ERRORS'),
|
|
|
|
(warnings, 'WARNINGS'),
|
|
|
|
(infos, 'INFOS'),
|
|
|
|
(debugs, 'DEBUGS'),
|
|
|
|
]
|
|
|
|
|
|
|
|
for issues, group_name in sorted_issues:
|
|
|
|
if issues:
|
2014-01-23 03:09:02 +08:00
|
|
|
visible_issue_count += len(issues)
|
2014-01-20 10:45:21 +08:00
|
|
|
formatted = (
|
2017-01-12 06:17:25 +08:00
|
|
|
self.style.ERROR(str(e))
|
2014-01-20 10:45:21 +08:00
|
|
|
if e.is_serious()
|
2017-01-12 06:17:25 +08:00
|
|
|
else self.style.WARNING(str(e))
|
2014-01-20 10:45:21 +08:00
|
|
|
for e in issues)
|
|
|
|
formatted = "\n".join(sorted(formatted))
|
2014-10-20 19:10:53 +08:00
|
|
|
body += '\n%s:\n%s\n' % (group_name, formatted)
|
|
|
|
|
|
|
|
if visible_issue_count:
|
|
|
|
header = "System check identified some issues:\n"
|
2014-01-20 10:45:21 +08:00
|
|
|
|
2007-08-27 08:30:37 +08:00
|
|
|
if display_num_errors:
|
2014-10-20 19:10:53 +08:00
|
|
|
if visible_issue_count:
|
|
|
|
footer += '\n'
|
|
|
|
footer += "System check identified %s (%s silenced)." % (
|
2014-01-23 03:09:02 +08:00
|
|
|
"no issues" if visible_issue_count == 0 else
|
|
|
|
"1 issue" if visible_issue_count == 1 else
|
|
|
|
"%s issues" % visible_issue_count,
|
|
|
|
len(all_issues) - visible_issue_count,
|
2014-01-20 10:45:21 +08:00
|
|
|
)
|
|
|
|
|
2015-10-05 02:16:12 +08:00
|
|
|
if any(e.is_serious(fail_level) and not e.is_silenced() for e in all_issues):
|
2014-10-20 19:10:53 +08:00
|
|
|
msg = self.style.ERROR("SystemCheckError: %s" % header) + body + footer
|
|
|
|
raise SystemCheckError(msg)
|
|
|
|
else:
|
|
|
|
msg = header + body + footer
|
|
|
|
|
|
|
|
if msg:
|
|
|
|
if visible_issue_count:
|
|
|
|
self.stderr.write(msg, lambda x: x)
|
|
|
|
else:
|
|
|
|
self.stdout.write(msg)
|
2007-08-16 14:06:55 +08:00
|
|
|
|
2016-02-12 18:02:36 +08:00
|
|
|
def check_migrations(self):
|
|
|
|
"""
|
|
|
|
Print a warning if the set of migrations on disk don't match the
|
|
|
|
migrations in the database.
|
|
|
|
"""
|
|
|
|
from django.db.migrations.executor import MigrationExecutor
|
|
|
|
try:
|
|
|
|
executor = MigrationExecutor(connections[DEFAULT_DB_ALIAS])
|
|
|
|
except ImproperlyConfigured:
|
|
|
|
# No databases are configured (or the dummy one)
|
|
|
|
return
|
|
|
|
except MigrationSchemaMissing:
|
|
|
|
self.stdout.write(self.style.NOTICE(
|
|
|
|
"\nNot checking migrations as it is not possible to access/create the django_migrations table."
|
|
|
|
))
|
|
|
|
return
|
|
|
|
|
|
|
|
plan = executor.migration_plan(executor.loader.graph.leaf_nodes())
|
|
|
|
if plan:
|
|
|
|
apps_waiting_migration = sorted(set(migration.app_label for migration, backwards in plan))
|
|
|
|
self.stdout.write(
|
|
|
|
self.style.NOTICE(
|
|
|
|
"\nYou have %(unpplied_migration_count)s unapplied migration(s). "
|
|
|
|
"Your project may not work properly until you apply the "
|
|
|
|
"migrations for app(s): %(apps_waiting_migration)s." % {
|
|
|
|
"unpplied_migration_count": len(plan),
|
|
|
|
"apps_waiting_migration": ", ".join(apps_waiting_migration),
|
|
|
|
}
|
|
|
|
)
|
|
|
|
)
|
|
|
|
self.stdout.write(self.style.NOTICE("Run 'python manage.py migrate' to apply them.\n"))
|
|
|
|
|
2007-08-16 14:06:55 +08:00
|
|
|
def handle(self, *args, **options):
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
|
|
|
The actual logic of the command. Subclasses must implement
|
|
|
|
this method.
|
|
|
|
"""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of BaseCommand must provide a handle() method')
|
2007-08-16 14:06:55 +08:00
|
|
|
|
2011-12-23 06:38:02 +08:00
|
|
|
|
2007-08-16 14:06:55 +08:00
|
|
|
class AppCommand(BaseCommand):
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
2013-12-25 06:43:47 +08:00
|
|
|
A management command which takes one or more installed application labels
|
|
|
|
as arguments, and does something with each of them.
|
2008-09-22 14:03:24 +08:00
|
|
|
|
|
|
|
Rather than implementing ``handle()``, subclasses must implement
|
2013-12-25 06:43:47 +08:00
|
|
|
``handle_app_config()``, which will be called once for each application.
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
2013-10-16 22:24:59 +08:00
|
|
|
missing_args_message = "Enter at least one application label."
|
|
|
|
|
|
|
|
def add_arguments(self, parser):
|
2016-03-29 06:33:29 +08:00
|
|
|
parser.add_argument('args', metavar='app_label', nargs='+', help='One or more application label.')
|
2007-08-16 14:06:55 +08:00
|
|
|
|
|
|
|
def handle(self, *app_labels, **options):
|
2013-12-24 19:25:17 +08:00
|
|
|
from django.apps import apps
|
2007-08-16 14:06:55 +08:00
|
|
|
try:
|
2013-12-24 19:25:17 +08:00
|
|
|
app_configs = [apps.get_app_config(app_label) for app_label in app_labels]
|
2013-12-14 18:11:52 +08:00
|
|
|
except (LookupError, ImportError) as e:
|
2007-08-16 14:06:55 +08:00
|
|
|
raise CommandError("%s. Are you sure your INSTALLED_APPS setting is correct?" % e)
|
|
|
|
output = []
|
2013-12-15 01:51:58 +08:00
|
|
|
for app_config in app_configs:
|
2013-12-25 06:43:47 +08:00
|
|
|
app_output = self.handle_app_config(app_config, **options)
|
2007-08-16 14:06:55 +08:00
|
|
|
if app_output:
|
|
|
|
output.append(app_output)
|
|
|
|
return '\n'.join(output)
|
|
|
|
|
2013-12-25 06:43:47 +08:00
|
|
|
def handle_app_config(self, app_config, **options):
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
2013-12-25 06:43:47 +08:00
|
|
|
Perform the command's actions for app_config, an AppConfig instance
|
|
|
|
corresponding to an application label given on the command line.
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
2015-01-18 01:32:20 +08:00
|
|
|
raise NotImplementedError(
|
|
|
|
"Subclasses of AppCommand must provide"
|
|
|
|
"a handle_app_config() method.")
|
2007-08-16 14:06:55 +08:00
|
|
|
|
2011-12-23 06:38:02 +08:00
|
|
|
|
2007-08-16 22:34:01 +08:00
|
|
|
class LabelCommand(BaseCommand):
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
|
|
|
A management command which takes one or more arbitrary arguments
|
|
|
|
(labels) on the command line, and does something with each of
|
|
|
|
them.
|
|
|
|
|
|
|
|
Rather than implementing ``handle()``, subclasses must implement
|
|
|
|
``handle_label()``, which will be called once for each label.
|
|
|
|
|
|
|
|
If the arguments should be names of installed applications, use
|
|
|
|
``AppCommand`` instead.
|
|
|
|
"""
|
2007-08-16 22:34:01 +08:00
|
|
|
label = 'label'
|
2013-10-16 22:24:59 +08:00
|
|
|
missing_args_message = "Enter at least one %s." % label
|
2007-08-17 03:14:09 +08:00
|
|
|
|
2013-10-16 22:24:59 +08:00
|
|
|
def add_arguments(self, parser):
|
|
|
|
parser.add_argument('args', metavar=self.label, nargs='+')
|
2007-08-16 22:34:01 +08:00
|
|
|
|
2013-10-16 22:24:59 +08:00
|
|
|
def handle(self, *labels, **options):
|
2007-08-16 22:34:01 +08:00
|
|
|
output = []
|
|
|
|
for label in labels:
|
|
|
|
label_output = self.handle_label(label, **options)
|
|
|
|
if label_output:
|
|
|
|
output.append(label_output)
|
|
|
|
return '\n'.join(output)
|
|
|
|
|
|
|
|
def handle_label(self, label, **options):
|
2008-09-22 14:03:24 +08:00
|
|
|
"""
|
|
|
|
Perform the command's actions for ``label``, which will be the
|
|
|
|
string as given on the command line.
|
|
|
|
"""
|
2013-09-07 02:24:52 +08:00
|
|
|
raise NotImplementedError('subclasses of LabelCommand must provide a handle_label() method')
|