写自定义django-admin命令

应用可以通过manage.py注册它们自己的动作。 例如,你可能想为你正在发布的Django应用添加一个manage.py动作。 在本页文档中,我们将为tutorial中的 polls应用构建一个自定义的 closepoll命令。

要做到这点,只需向该应用添加一个management/commands目录。 Django将为该目录中名字没有以下划线开始的每个Python模块注册一个manage.py命令。 像这样:

polls/
    __init__.py
    models.py
    management/
        __init__.py
        commands/
            __init__.py
            _private.py
            closepoll.py
    tests.py
    views.py

在Python 2上,请确保management__init__.py两个目录都包含management/commands 文件,否则将检测不到你的命令。

在这个例子中,polls命令对任何项目都可使用,只要它们在INSTALLED_APPS里包含closepoll应用。

_private.py将不可以作为一个管理命令使用。

Command模块只有一个要求 – 它必须定义一个closepoll.py类并扩展自BaseCommand或其 subclasses

独立的脚本

自定义的管理命令主要用于运行独立的脚本或者UNIX crontab和Windows周期任务控制面板周期性执行的脚本。

要实现这个命令,需将polls/management/commands/closepoll.py编辑成这样:

from django.core.management.base import BaseCommand, CommandError
from polls.models import Question as Poll

class Command(BaseCommand):
    help = 'Closes the specified poll for voting'

    def add_arguments(self, parser):
        parser.add_argument('poll_id', nargs='+', type=int)

    def handle(self, *args, **options):
        for poll_id in options['poll_id']:
            try:
                poll = Poll.objects.get(pk=poll_id)
            except Poll.DoesNotExist:
                raise CommandError('Poll "%s" does not exist' % poll_id)

            poll.opened = False
            poll.save()

            self.stdout.write(self.style.SUCCESS('Successfully closed poll "%s"' % poll_id))

当你使用管理命令并希望提供控制台输出时,你应该写到stderrself.stderr,而不能直接打印到 self.stdoutstdout 通过使用这些代理方法,测试你自定义的命令将变得非常容易。 还请注意,你不需要在消息的末尾加上一个换行符,它将被自动添加,除非你指定ending参数:

self.stdout.write("Unterminated line", ending='')

新的自定义命令可以使用python manage.py closepoll <poll_id>调用。

False接收一个或多个poll_ids并为他们中的每个设置 handle()poll.opened 如果用户访问任何不存在的polls,将引发一个CommandError poll.opened属性在tutorial中不存在,并添加到此示例的polls.models.Question中。

接受可选参数

通过接收额外的命令行选项,可以简单地修改closepoll来删除一个给定的poll而不是关闭它。 这些自定义的选项可以像下面这样添加到 add_arguments()方法中:

class Command(BaseCommand):
    def add_arguments(self, parser):
        # Positional arguments
        parser.add_argument('poll_id', nargs='+', type=int)

        # Named (optional) arguments
        parser.add_argument(
            '--delete',
            action='store_true',
            dest='delete',
            default=False,
            help='Delete poll instead of closing it',
        )

    def handle(self, *args, **options):
        # ...
        if options['delete']:
            poll.delete()
        # ...

选项(在我们的例子中为delete)在handle方法的options字典参数中可以访问到。 更多关于add_argument用法的信息,请参考argparse的Python 文档。

除了可以添加自定义的命令行选项, management commands还可以接收一些默认的选项,例如--verbosity--traceback

管理命令和区域设置

默认情况下,BaseCommand.execute()方法使转换失效,因为某些与Django一起的命令完成的任务要求一个与项目无关的语言字符串(例如,面向用户的内容渲染和数据库填入)。

如果,出于某些原因,你的自定义的管理命令需要使用一个固定的区域设置,你需要在你的handle()方法中利用I18N支持代码提供的函数手工地启用和停用它:

from django.core.management.base import BaseCommand, CommandError
from django.utils import translation

class Command(BaseCommand):
    ...

    def handle(self, *args, **options):

        # Activate a fixed locale, e.g. Russian
        translation.activate('ru')

        # Or you can activate the LANGUAGE_CODE # chosen in the settings:
        from django.conf import settings
        translation.activate(settings.LANGUAGE_CODE)

        # Your command logic here
        ...

        translation.deactivate()

另一个需要可能是你的命令只是简单地应该使用设置中设置的区域设置且Django应该保持不让它停用。 你可以使用BaseCommand.leave_locale_alone选项实现这个功能。

虽然上面描述的场景可以工作,但是考虑到系统管理命令对于运行非统一的区域设置通常必须非常小心,所以你可能需要:

  • 确保运行命令时USE_I18N设置永远为True(this is a good example of the potential problems stemming from a dynamic runtime environment that Django commands avoid offhand by deactivating translations)。
  • 查看命令的代码及其所需的代码,以便在更改区域设置时评估其行为差异,并评估其对可预测命令行为的影响。

测试¶ T0>

关于如何测试自定义管理命令的信息可以在testing docs中找到。

覆盖命令

Django注册内置命令,然后反向搜索INSTALLED_APPS中的命令。 在搜索过程中,如果命令名称与已注册的命令重复,则新发现的命令将覆盖第一个命令。

换句话说,要覆盖一个命令,新命令必须具有相同的名称,其应用程序必须位于INSTALLED_APPS中的覆盖命令的应用程序之前。

通过在您的项目应用程序(在INSTALLED_APPS中的第三方应用程序之前订购)中创建一个新命令,可以以新名称提供来自第三方应用程序的无意中覆盖的管理命令导入覆盖命令的Command

命令对象

class BaseCommand[source]

所有管理命令最终继承的基类。

如果您希望访问解析命令行参数的所有机制并制定响应中调用的代码,请使用此类;如果您不需要更改任何这种行为,请考虑使用其中的subclasses

继承BaseCommand类要求你实现handle()方法。

属性¶ T0>

所有的属性都可以在你派生的类中设置,并在BaseCommandsubclasses中使用。

BaseCommand.help

命令的简短描述,当用户运行python manage.py help <command>命令时将在帮助信息中打印出来。

BaseCommand.missing_args_message

如果你的命令定义了必需的位置参数,你可以自定义参数缺失时返回的错误信息。 默认是由argparse输出的 (“too few arguments”)。

BaseCommand.output_transaction

一个布尔值,指示命令是否输出SQL语句;如果True,输出将自动用BEGIN;COMMIT;打包。 默认为False

BaseCommand.requires_migrations_checks
Django中的新功能1.10。

一个布尔值如果True,则如果磁盘上的一组迁移与数据库中的迁移不匹配,则该命令将显示警告。 警告不会阻止命令执行。 默认为False

BaseCommand.requires_system_checks

一个布尔值如果True,则在执行命令之前,将检查整个Django项目的潜在问题。 默认值为True

BaseCommand.leave_locale_alone

一个布尔值,指示设置中的区域设置在执行命令过程中是否应该保持而不是强制设成‘en-us’。

默认为False

如果您决定在自定义命令中更改此选项的值,如果创建了对区域设置敏感的数据库内容,并且此类内容不应包含任何翻译(例如,如django.contrib.auth权限)使得区域设置与事实上的默认“en-us”不同可能会导致意外的影响。 有关详细信息,请参阅上面的管理命令和语言环境部分。

BaseCommand.style

在写入stdoutstderr时有助于创建彩色输出的实例属性。 像这样:

self.stdout.write(self.style.SUCCESS('...'))

请参阅Syntax coloring,了解如何修改调色板并查看可用的样式(使用该部分中描述的“role”的大写版本)。

如果您在运行命令时传递--no-color选项,则所有self.style()调用将返回原始字符串。

方法¶ T0>

BaseCommand有几个方法可以被覆盖,但是只有handle()是必须实现的。

在子类中实现构造函数

如果你在BaseCommand的子类中实现__init__,你必须调用BaseCommand__init__

class Command(BaseCommand):
    def __init__(self, *args, **kwargs):
        super(Command, self).__init__(*args, **kwargs)
        # ...
BaseCommand.add_arguments(parser)[source]

添加解析器参数的入口,以处理传递给命令的命令行参数。 自定义的命令应该覆盖这个方法以添加命令行接收的位置参数和可选参数。 当直接继承BaseCommand时不需要调用super()

BaseCommand.get_version()[source]

返回Django的版本,对于所有内建的Django命令应该都是正确的。 用户提供的命令可以覆盖这个方法以返回它们自己的版本。

BaseCommand.execute(*args, **options)[source]

执行这个命令,如果需要则作系统检查(通过 requires_system_checks属性控制)。 如果该命令引发一个CommandError,它将被截断并打印到标准错误输出。

在你的代码中调用管理命令

不应该在你的代码中直接调用execute()来执行一个命令。 请改用call_command()

BaseCommand.handle(*args, **options)[source]

命令的真正逻辑。 子类必须实现这个方法。

It may return a Unicode string which will be printed to stdout (wrapped by BEGIN; and COMMIT; if output_transaction is True).

BaseCommand.check(app_configs=None, tags=None, display_num_errors=False)[source]

利用系统的检测框架检测全部Django项目的潜在问题。 严重的问题被提出为CommandError;警告输出到stderr;小通知输出到stdout。

如果tagsapp_configs都为None,将进行所有的系统检查。 compatibility可以是一个要检查的标签列表,比如tagsmodels

BaseCommand子类

class AppCommand

这个管理命令接收一个或多个安装的应用标签作为参数,并对它们每一个都做一些动作。

子类不用实现handle(),但必须实现handle_app_config(),它将会为每个应用调用一次。

AppCommand.handle_app_config(app_config, **options)

app_config完成命令行的动作,其中app_config是AppConfig的实例,对应于在命令行上给出的应用标签。

class LabelCommand

这个管理命令接收命令行上的一个或多个参数(标签),并对它们每一个都做一些动作。

子类不用实现handle(),但必须实现handle_label(),它将会为每个标签调用一次。

LabelCommand.label

描述传递给命令的任意参数的字符串。 字符串用于命令的使用文本和错误消息。 默认为'label'

LabelCommand.handle_label(label, **options)

label完成命令行的动作,label是命令行给出的字符串。

命令异常

exception CommandError[source]

异常类,表示执行一个管理命令时出现问题。

如果在从命令行控制台执行管理命令时引发了这个异常,它将被捕获并变成一个精心打印的错误消息到适当的输出流(即stderr);因此,引发此异常(对错误的明智描述)是指示执行命令时出现问题的首选方法。

如果通过call_command()从代码调用管理命令,则由需要来捕获异常。