Skip to main content

编写自定义 django-admin 命令

应用程序可以使用 manage.py 注册自己的操作。例如,您可能希望为要发布的Django应用程序添加 manage.py 操作。在本文中,我们将从 tutorialpolls 应用程序构建自定义 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上,确保如上所述在 managementmanagement/commands 目录中包括 __init__.py 文件,否则将不会检测到您的命令。

在此示例中,closepoll 命令将可用于包括 INSTALLED_APPS 中的 polls 应用程序的任何项目。

_private.py 模块将不可用作管理命令。

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

独立脚本

自定义管理命令对于运行独立脚本或从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))

注解

当您使用管理命令并希望提供控制台输出时,您应该写入 self.stdoutself.stderr,而不是直接打印到 stdoutstderr。通过使用这些代理,它变得更容易测试您的自定义命令。还请注意,您不需要使用换行符结束消息,它会自动添加,除非您指定 ending 参数:

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

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

handle() 方法需要一个或多个 poll_ids,并为每个 poll_ids 设置 poll.openedFalse。如果用户引用了任何不存在的轮询,则会引发 CommandErrorpoll.opened 属性不存在于 tutorial 中,并且被添加到本示例的 polls.models.Question

接受可选参数

相同的 closepoll 可以轻松地修改,以删除给定的轮询,而不是通过接受其他命令行选项关闭它。这些自定义选项可以像这样在 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方法的dict参数中可用。有关 add_argument 使用的更多信息,请参阅 argparse Python文档。

除了能够添加自定义命令行选项之外,所有 management commands 都可以接受一些默认选项,例如 --verbosity--traceback

管理命令和语言环境

默认情况下,BaseCommand.execute() 方法取消翻译,因为Django附带的一些命令执行需要项目中性字符串语言的几个任务(例如,面向用户的内容呈现和数据库填充)。

如果由于某些原因,您的自定义管理命令需要使用固定的语言环境,您应该使用I18N支持代码提供的功能在 handle() 方法中手动激活和停用它:

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

class Command(BaseCommand):
    ...
    can_import_settings = True

    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 (这是Django命令通过停用转换可避免被动态运行时环境引起的潜在问题的一个很好的例子)。

  • 查看您的命令的代码和它所调用的代码,当语言环境改变时评估它对行为的差异,并评估它对您的命令的可预测行为的影响。

测试

有关如何测试自定义管理命令的信息可以在 测试文档 中找到。

命令对象

class BaseCommand[源代码]

所有管理命令最终派生的基类。

如果您想访问解析命令行参数的所有机制,并计算出响应中调用哪些代码,请使用此类;如果你不需要改变任何行为,请考虑使用其中的一个 subclasses

BaseCommand 类进行子类化需要实现 handle() 方法。

属性

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

BaseCommand.can_import_settings

一个布尔值,指示命令是否需要能够导入Django设置;如果 Trueexecute() 将在继续之前验证这是可能的。默认值为 True

BaseCommand.help

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

BaseCommand.missing_args_message

如果您的命令定义了强制性位置参数,则可以自定义在缺少参数的情况下返回的消息错误。默认值由 argparse 输出(“太少参数”)。

BaseCommand.output_transaction

指示该命令是否输出SQL语句的布尔值;如果 True,输出将自动包裹与 BEGIN;COMMIT;。默认值为 False

BaseCommand.requires_migrations_checks
New in Django 1.10.

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

BaseCommand.requires_system_checks

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

BaseCommand.leave_locale_alone

一个布尔值,指示在执行命令期间是否应保留设置中设置的区域设置,而不是强制设置为“en-us”。

默认值为 False

如果您决定更改自定义命令中此选项的值,如果它创建的区域设置敏感的数据库内容,并且此类内容不应包含任何翻译,请确保您知道您正在做什么(例如,它发生,例如与 django.contrib.auth 权限)因为使得区域设置不同于事实上的默认“en-us”可能会导致意外的影响。有关更多详细信息,请参阅上文的 Management commands and locales 部分。

can_import_settings 选项设置为 False 时,此选项不能为 False,因为尝试设置区域设置需要访问设置。此条件将生成 CommandError

BaseCommand.style

在写入 stdoutstderr 时帮助创建彩色输出的实例属性。例如:

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

请参阅 语法着色 了解如何修改调色板并查看可用的样式(使用该部分中描述的“角色”的高级版本)。

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

方法

BaseCommand 有几个方法可以重写,但只有 handle() 方法必须实现。

在子类中实现构造函数

如果您在 BaseCommand 的子类中实施 __init__,则必须调用 BaseCommand__init__:

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

入口点添加解析器参数以处理传递给命令的命令行参数。自定义命令应覆盖此方法以添加命令接受的位置和可选参数。直接子类化 BaseCommand 时不需要调用 super()

BaseCommand.get_version()[源代码]

返回Django版本,应对所有内置的Django命令正确。用户提供的命令可以覆盖此方法以返回其自己的版本。

BaseCommand.execute(*args, **options)[源代码]

尝试执行此命令,如果需要,执行系统检查(由 requires_system_checks 属性控制)。如果命令产生 CommandError,它被拦截并打印到stderr。

在代码中调用管理命令

不应直接从代码中调用 execute() 来执行命令。请改用 call_command()

BaseCommand.handle(*args, **options)[源代码]

该命令的实际的逻辑。子类必须实现此方法。

它可以返回将被打印到 stdout 的Unicode字符串(如果 output_transactionTrue,则包括 BEGIN;COMMIT;)。

BaseCommand.check(app_configs=None, tags=None, display_num_errors=False)[源代码]

使用系统检查框架来检查整个Django项目的潜在问题。 CommandError 提出了严重的问题;警告输出到stderr;次要通知输出到stdout。

如果 app_configstags 都是 None,则执行所有系统检查。 tags 可以是检查标记列表,如 compatibilitymodels

BaseCommand 子类

class AppCommand

一个管理命令,它将一个或多个已安装的应用程序标签作为参数,并对每个应用程序执行某些操作。

而不是实现 handle(),子类必须实现 handle_app_config(),它将为每个应用程序调用一次。

AppCommand.handle_app_config(app_config, **options)

app_config 执行命令的操作,这将是对应于在命令行上给出的应用程序标签的 AppConfig 实例。

class LabelCommand

一个管理命令,它在命令行上接受一个或多个任意参数(标签),并对每个参数执行一些操作。

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

LabelCommand.handle_label(label, **options)

label 执行命令的操作,这将是在命令行中给出的字符串。

命令异常

exception CommandError[源代码]

异常类指示在执行管理命令时出现问题。

如果在从命令行控制台执行管理命令期间引发此异常,它将被捕获并变成对适当的输出流(即,stderr)的精确打印的错误消息;因此,提高此异常(具有错误的合理描述)是指示在命令的执行中出现错误的优选方式。

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