django.core.management-command

    1. ``django-admin`` or ``manage.py`` loads the command class
       and calls its ``run_from_argv()`` method.

    2. The ``run_from_argv()`` method calls ``create_parser()`` to get
       an ``ArgumentParser`` for the arguments, parses them, performs
       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``.

    4. If ``handle()`` or ``execute()`` raised any exception (e.g.
       ``CommandError``), ``run_from_argv()`` will  instead print an error
       message to ``stderr``.

这是命令执行的流程。

  1. 首先import相应的command类,然后调用run_from_argv( ), 传入sys.argv当作参数。

  2. 然后调用create_parser( )方法,返回parser。

  3. 解析参数,并且将解析后的参数传入execute( ) 方法。

  4. 执行handle( ),将返回的结果输出。


第一步会在以后讲,现在主要看后面处理的流程代码,是由BaseCommand实现。

因为BaseCommand类的实现代码比较多,就挑选相应的方法解析。

def run_from_argv(self, argv):
        """
        Set up any environment changes requested (e.g., Python path
        and Django settings), then run this command. If the
        command raises a ``CommandError``, intercept it and print it sensibly
        to stderr. If the ``--traceback`` option is present or the raised
        ``Exception`` is not ``CommandError``, raise it.
        """
        self._called_from_command_line = True
        parser = self.create_parser(argv[0], argv[1])

        if self.use_argparse:
            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', ())
        else:
            options, args = parser.parse_args(argv[2:])
            cmd_options = vars(options)
        handle_default_options(options)
        try:
            self.execute(*args, **cmd_options)
        except Exception as e:
            if options.traceback or not isinstance(e, CommandError):
                raise

            # 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))
            sys.exit(1)

run_from_argv( )首先调用parser = self.create_parser(argv[0], argv[1])方法。

获得parser后,通过判断self.use_argparse,来确定返回的类型。因为目前有两种解析参数的方式,一种是optparse,将在django2.0后废弃。另一种是argparser。这是为了向后保持兼容性。

argv参数格式极为sys.argv,执行命令时的参数。举例:python manage runserver 0.0.0.0:8000

那么argv = ['manage',  'runserver', '0.0.0.0:8000']。


现在看create_parser方法的具体代码。

def create_parser(self, prog_name, subcommand):
        """
        Create and return the ``ArgumentParser`` which will be used to
        parse the arguments to this command.

        """
        if not self.use_argparse:
            # Backwards compatibility: use deprecated optparse module
            warnings.warn("OptionParser usage for Django management commands "
                          "is deprecated, use ArgumentParser instead",
                          RemovedInDjango20Warning)
            parser = OptionParser(prog=prog_name,
                                usage=self.usage(subcommand),
                                version=self.get_version())
            parser.add_option('-v', '--verbosity', action='store', dest='verbosity', default='1',
                type='choice', choices=['0', '1', '2', '3'],
                help='Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output')
            parser.add_option('--settings',
                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.'
                ),
            )
            parser.add_option('--pythonpath',
                help='A directory to add to the Python path, e.g. "/home/djangoprojects/myproject".'),
            parser.add_option('--traceback', action='store_true',
                help='Raise on CommandError exceptions')
            parser.add_option('--no-color', action='store_true', dest='no_color', default=False,
                help="Don't colorize the command output.")
            for opt in self.option_list:
                parser.add_option(opt)
        else:
            parser = CommandParser(self, prog="%s %s" % (os.path.basename(prog_name), subcommand),
                description=self.help or None)
            parser.add_argument('--version', action='version', version=self.get_version())
            parser.add_argument('-v', '--verbosity', action='store', dest='verbosity', default='1',
                type=int, choices=[0, 1, 2, 3],
                help='Verbosity level; 0=minimal output, 1=normal output, 2=verbose output, 3=very verbose output')
            parser.add_argument('--settings',
                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.'
                ),
            )
            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.")
            if self.args:
                # Keep compatibility and always accept positional arguments, like optparse when args is set
                parser.add_argument('args', nargs='*')
            self.add_arguments(parser)
        return parser


首先判断self.use_argparse,

@property
    def use_argparse(self):
        return not bool(self.option_list)

通过判断option_list属性否为为空, option_list在BaseCommand类中, 默认为空元祖 。

option_list = ()


因为django目前倾向于使用argpase,所以这里只说明一下使用argparse的这一部分。

这里又出现了一个新的类CommandParser。

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
        super(CommandParser, self).__init__(**kwargs)

    def parse_args(self, args=None, namespace=None):
        # Catch missing argument for a better error message
        if (hasattr(self.cmd, 'missing_args_message') and
                not (args or any(not arg.startswith('-') for arg in args))):
            self.error(self.cmd.missing_args_message)
        return super(CommandParser, self).parse_args(args, namespace)

    def error(self, message):
        if self.cmd._called_from_command_line:
            super(CommandParser, self).error(message)
        else:
            raise CommandError("Error: %s" % message)

CommandParser只是重写了parse_args方法。增加missing_args_message这种情况。

那么它是怎么判断参数缺少的情况:

not (args or any(not arg.startswith('-') for arg in args))

这条语句放回true就表示参数缺少。

那么

args or any(not arg.startswith('-') for arg in args)

就应该返回false。这里使用了or操作符,如果args不为空的话,就返回args,那么上条语句就相当于返回true了。

如果args为空,就会返回后面的

any(not arg.startswith('-') for arg in args)

。那么args= (),

not arg.startswith('-') for arg in args)

返回仍旧是一个(),这样就没什么意思了。



还重写了error()方法,只是改变抛出异常。

self.cmd._called_from_command_line,在run_from_args( )就直接赋值为true。


再接着看create_parser方法,实例化CommandParser后, 添加'--version', '--verbosity', '--settings', '--pythonpath', '--traceback', '--no-color'可选参数。

注意下self.args这个属性,如果self.args不为空, 那么后面的参数都会被收集在args中。

parser.add_argument('args', nargs='*')

这是为了向后保持兼容性,而设定的。如果设置args属性,就会导致后面的self.add_arguments(parser)无作用。

def add_arguments(self, parser):
        """
        Entry point for subclassed commands to add custom arguments.
        """
        pass

add_arguments( )方法,由子类实现,用于添加自定义的参数。


现在回到run_from_argv()方法中, 创建完parser后,就是解析参数了。

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', ())

vars将返回的NameSpace对象options,转化为字典cmd_options 。

单独提出args这个参数。

接着调用handle_default_options(options)方法。

def handle_default_options(options):
    """
    Include any default options that all commands should accept here
    so that ManagementUtility can handle them before searching for
    user commands.

    """
    if options.settings:
        os.environ['DJANGO_SETTINGS_MODULE'] = options.settings
    if options.pythonpath:
        sys.path.insert(0, options.pythonpath)

主要是负责settings和pythonpath这两个参数的处理。settings制定配置文件,通过改变环境变量DJANGO_SETTINGS_MODULE。pythonpath则增加搜索包和模块的路径,通过sys.path变量改变。


接着调用self.execute(*args, **cmd_options)方法。

def execute(self, *args, **options):
        """
        Try to execute this command, performing system checks if needed (as
        controlled by attributes ``self.requires_system_checks`` and
        ``self.requires_model_validation``, except if force-skipped).
        """
        if options.get('no_color'):
            self.style = no_style()
            self.stderr.style_func = None
        if options.get('stdout'):
            self.stdout = OutputWrapper(options['stdout'])
        if options.get('stderr'):
            self.stderr = OutputWrapper(options.get('stderr'), self.stderr.style_func)

        if self.can_import_settings:
            from django.conf import settings  # NOQA

        saved_locale = None
        if not self.leave_locale_alone:
            # Only mess with locales if we can assume we have a working
            # settings file, because django.utils.translation requires settings
            # (The final saying about whether the i18n machinery is active will be
            # found in the value of the USE_I18N setting)
            if not self.can_import_settings:
                raise CommandError("Incompatible values of 'leave_locale_alone' "
                                   "(%s) and 'can_import_settings' (%s) command "
                                   "options." % (self.leave_locale_alone,
                                                 self.can_import_settings))
            # Switch to US English, because django-admin creates database
            # content like permissions, and those shouldn't contain any
            # translations.
            from django.utils import translation
            saved_locale = translation.get_language()
            translation.activate('en-us')

        try:
            if (self.requires_system_checks and
                    not options.get('skip_validation') and  # Remove at the end of deprecation for `skip_validation`.
                    not options.get('skip_checks')):
                self.check()
            output = self.handle(*args, **options)
            if output:
                if self.output_transaction:
                    # This needs to be imported here, because it relies on
                    # settings.
                    from django.db import connections, DEFAULT_DB_ALIAS
                    connection = connections[options.get('database', DEFAULT_DB_ALIAS)]
                    if connection.ops.start_transaction_sql():
                        self.stdout.write(self.style.SQL_KEYWORD(connection.ops.start_transaction_sql()))
                self.stdout.write(output)
                if self.output_transaction:
                    self.stdout.write('\n' + self.style.SQL_KEYWORD(connection.ops.end_transaction_sql()))
        finally:
            if saved_locale is not None:
                translation.activate(saved_locale)


前面处理no_color,stdout,stderr的参数和self.can_import_settings属性。

然后根据self.leave_locale_alone属性,配置translation。

然后根据参数skip_validation, skip_checks,和self.requires_system_checks属性,调用self.check( )检查错误。

最后调用handle()方法,将返回的结果输出。


整个命令的执行过程就是上面说的。

对于我们要自定义命令,则主要通过设置一些类属性。

实现add_arguments( )添加自定义的参数。

实现handle( )方法,实现自己的逻辑。


类属性:

``args``
        A string listing the arguments accepted by the command,
        suitable for use in help messages; e.g., a command which takes
        a list of application names might set this to '<app_label
        app_label ...>'.

    ``can_import_settings``
        A boolean indicating whether the command needs to be able to
        import Django settings; if ``True``, ``execute()`` will verify
        that this is possible before proceeding. Default value is
        ``True``.

    ``help``
        A short description of the command, which will be printed in
        help messages.

    ``option_list``
        This is the list of ``optparse`` options which will be fed
        into the command's ``OptionParser`` for parsing arguments.
        Deprecated and will be removed in Django 2.0.

    ``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``.

    ``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.

    ``requires_model_validation``
        DEPRECATED - This value will only be used if requires_system_checks
        has not been provided. Defining both ``requires_system_checks`` and
        ``requires_model_validation`` will result in an error.

        A boolean; if ``True``, validation of installed models will be
        performed prior to executing the command. Default value is
        ``True``. To validate an individual application's models
        rather than all applications' models, call
        ``self.validate(app_config)`` from ``handle()``, where ``app_config``
        is the application's configuration provided by the app registry.

    ``leave_locale_alone``
        A boolean indicating whether the locale set in settings should be
        preserved during the execution of the command instead of being
        forcibly set to 'en-us'.

        Default value is ``False``.

        Make sure you know what you are doing if you decide to change the value
        of this option in your custom command if it creates database content
        that is locale-sensitive and such content shouldn't contain any
        translations (like it happens e.g. with django.contrim.auth
        permissions) as making the locale differ from the de facto default
        'en-us' might cause unintended effects.

        This option can't be False when the can_import_settings option is set
        to False too because attempting to set the locale needs access to
        settings. This condition will generate a CommandError.
    """


实现add_arguments( ):

注意如果指定了args参数,就会导致add_arguments( )添加的参数没用。


实现handle(self, *args, **options)方法:

如果制定了args属性,则options只有 --version,--verbosity,--settings,--pythonpath值几个属性。

args参数则是命令行其余的参数。


如果没有指定args属性,则args为空元组()。options则为解析参数后的字典。





转载于:https://my.oschina.net/u/569730/blog/360017

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值