13

标题确实说明了一切,但我目前有这个,但它不起作用:

class Command(BaseCommand):
    help = ("Functions related to downloading, parsing, and indexing the  "
            "content")

    def add_arguments(self, parser):
        subparsers = parser.add_subparsers()

        download_parser = subparsers.add_parser(
            'download',
            help='Using a local CSV, download the XML data for content. '
                 'Output is sent to the log.'
        )
        download_parser.add_argument(
            '--start_line',
            type=int,
            default=0,
            help='The line in the file where you wish to start processing.'
        )

        # Add an argparse parser for parsing the content. Yes, this is
        # a bit confusing.
        content_parser_parser = subparsers.add_parser(
            'parse',
            help="Look at the file system and parse everything you see so that "
                 "we have content in the databse."
        )
        content_parser_parser.add_argument(
            '--start_item',
            type=int,
            default=0,
            help="Assuming the content is sorted by file name, this item is "
                 "the one to start on."
        )

我的具体想法是创建一个具有子命令的命令,用于下载 XML 内容或将其解析到数据库中。

4

2 回答 2

18

Django 2.1 及更高版本

在 Django 2.1 及更高版本中,添加子命令很简单:

from django.core.management.base import BaseCommand

class Command(BaseCommand):

    def add_arguments(self, parser):
        subparsers = parser.add_subparsers(title="subcommands",
                                           dest="subcommand",
                                           required=True)

然后,您可以使用subparser与编写使用argparse. 例如,如果你想要一个名为的子命令foo,它可以接受--bar参数:

foo = subparsers.add_parser("foo")
foo.set_defaults(subcommand=fooVal)
foo.add_argument("--bar")

该值fooVal是您决定subcommand在用户​​指定子命令时应将选项设置为的值foo。我经常将它设置为可调用的。

旧版本的 Django

这是可能的,但需要一些工作:

from django.core.management.base import BaseCommand, CommandParser

class Command(BaseCommand):

    [...]

    def add_arguments(self, parser):
        cmd = self

        class SubParser(CommandParser):

            def __init__(self, **kwargs):
                super(SubParser, self).__init__(cmd, **kwargs)

        subparsers = parser.add_subparsers(title="subcommands",
                                           dest="subcommand",
                                           required=True,
                                           parser_class=SubParser)

add_subparsers默认情况下,当您调用时,argparse会创建一个新的解析器,该解析器与您调用的解析器属于同一类add_subparser。碰巧您进入的解析器parser是一个CommandParser实例(在django.core.management.base中定义)。该类在 之前需要一个参数(而默认解析器类仅由CommandParser提供):cmd**kwargsargparse**kwargs

def __init__(self, cmd, **kwargs):

因此,当您尝试添加子解析器时,它会失败,因为仅调用构造函数**kwargs并且cmd缺少参数。

上面的代码通过传入parser_class一个添加缺少参数的类来解决这个问题。

需要考虑的事项:

  1. 在上面的代码中,我创建了一个新类,因为名称parser_class表明应该在那里传递的是一个真正的类。但是,这也有效:

    def add_arguments(self, parser):
        cmd = self
        subparsers = parser.add_subparsers(
            title="subcommands",
            dest="subcommand",
            required=True,
            parser_class=lambda **kw: CommandParser(cmd, **kw))
    

    现在我没有遇到任何问题,但未来的更改argparse可能会使使用 lambda 而不是真正的类失败。由于该论点被称为parser_class而不是类似的东西,parser_maker否则parser_manufacture我会认为这样的改变是公平的游戏。

  2. 我们不能只传递一个股票argparse类而不是传递一个自定义类parser_class吗?不会有直接的问题,但会产生意想不到的后果。中的注释CommandParser表明argparse'sstick 解析器的行为对于 Django 命令是不可取的。特别是,该类的文档字符串指出

    """
    Customized ArgumentParser class to improve some error messages and prevent
    SystemExit in several occasions, as SystemExit is unacceptable when a
    command is called programmatically.
    """
    

    这是Jerzyk 的答案所面临的问题。此处的解决方案通过派生CommandParser并提供 Django 所需的正确行为来避免该问题。

于 2016-05-24T13:06:29.557 回答
3

你可以添加它,它非常简单:

class Command(BaseCommand):
    help = 'dump/restore/diff'

    def add_arguments(self, parser):
        parser.add_argument('-s', '--server', metavar='server', type=str, 
                            help='server address')
        parser.add_argument('-d', '--debug', help='Print lots of debugging') 

        subparsers = parser.add_subparsers(metavar='command',
                                           dest='command',
                                           help='sub-command help')
        subparsers.required = True

        parent_parser = argparse.ArgumentParser(add_help=False)
        parent_parser.add_argument('machine', metavar='device', type=str)
        parent_parser.add_argument('-e', '--errors', action='store_true')

        parser_dump = subparsers.add_parser('dump', parents=[parent_parser],
                                            cmd=self)
        parser_dump.add_argument('-i', '--indent', metavar='indent', type=int,                                   
                                  default=None, help='file indentation')

        parser_restore = subparsers.add_parser('restore',             
                                               parents=[parent_parser],
                                               cmd=self)
        parser_restore.add_argument('infile', nargs='?', 
                                    type=argparse.FileType('r'), 
                                    default=sys.stdin)

        parser_diff = subparsers.add_parser('diff', parents=[parent_parser], 
                                            cmd=self)
        parser_diff.add_argument('infile', nargs='?', 
                                 type=argparse.FileType('r'),
                                 default=sys.stdin)
于 2016-06-05T02:26:35.517 回答