编写自定义 django-admin 命令 — Django 文档

来自菜鸟教程
Django/docs/2.2.x/howto/custom-management-commands
跳转至:导航、​搜索

编写自定义 django-admin 命令

应用程序可以使用 manage.py 注册自己的操作。 例如,您可能想要为正在分发的 Django 应用程序添加 manage.py 操作。 在本文档中,我们将为 教程 中的 polls 应用程序构建自定义 closepoll 命令。

为此,只需向应用程序添加一个 management/commands 目录。 Django 将为该目录中名称不以下划线开头的每个 Python 模块注册一个 manage.py 命令。 例如:

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

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

_private.py 模块将不能作为管理命令使用。

closepoll.py 模块只有一个要求——它必须定义一个类 Command 来扩展 BaseCommand 或其 子类 之一。

独立脚本

自定义管理命令在运行独立脚本命令方面十分有用,也可用于 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_ids', nargs='+', type=int)

    def handle(self, *args, **options):
        for poll_id in options['poll_ids']:
            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_ids> 调用新的自定义命令。

handle()方法取1个或多个poll_ids,将每个poll.opened设置为False。 如果用户引用了任何不存在的民意调查,则会引发 CommandErrorpoll.opened 属性在 tutorial 中不存在,在本例中被添加到 polls.models.Question 中。

接受可选参数

相同的 closepoll 可以很容易地修改以删除给定的轮询,而不是通过接受额外的命令行选项来关闭它。 这些自定义选项可以添加到 add_arguments() 方法中,如下所示:

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

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

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

选项(在我们的示例中为 delete)在 handle 方法的 options dict 参数中可用。 有关 add_argument 用法的更多信息,请参阅 argparse Python 文档。

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


管理命令和本地化

默认情况下,管理命令以当前活动的 locale 执行。

如果出于某种原因,您的自定义管理命令必须在没有活动区域设置的情况下运行(例如,为了防止将翻译的内容插入到数据库中),请使用 句柄上的 @no_translations 装饰器停用翻译( ) 方法:

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

class Command(BaseCommand):
    ...

    @no_translations
    def handle(self, *args, **options):
        ...

由于翻译停用需要访问配置的设置,装饰器不能用于没有配置设置的命令。

2.1 版更改: @no_translations 装饰器是新的。 在旧版本中,除非命令的 leave_locale_alone 属性(现已删除)设置为 True,否则在运行命令之前会停用翻译。


测试

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


覆盖命令

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

换句话说,要覆盖命令,新命令必须具有相同的名称,并且其应用程序必须在 :setting:`INSTALLED_APPS` 中被覆盖命令的应用程序之前。

通过在您项目的应用程序之一中创建新命令(在 中的第三方应用程序之前排序):setting:`INSTALLED_APPS`[ X247X]) 导入被覆盖命令的 Command


命令对象

class BaseCommand

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

如果您想访问解析命令行参数的所有机制并确定要调用的代码作为响应,请使用此类; 如果您不需要更改任何该行为,请考虑使用其 子类 之一。

继承 BaseCommand 类需要您实现 handle() 方法。

属性

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

BaseCommand.help
命令的简短描述,当用户运行命令 python manage.py help <command> 时,将打印在帮助消息中。
BaseCommand.missing_args_message
如果您的命令定义了强制位置参数,您可以自定义在缺少参数的情况下返回的消息错误。 默认由 argparse 输出(“参数太少”)。
BaseCommand.output_transaction
一个布尔值,指示命令是否输出 SQL 语句; 如果是 True,输出将自动用 BEGIN;COMMIT; 包裹。 默认值为 False
BaseCommand.requires_migrations_checks
一个布尔值; 如果 True,如果磁盘上的迁移集与数据库中的迁移不匹配,则该命令会打印警告。 警告不会阻止命令执行。 默认值为 False
BaseCommand.requires_system_checks
一个布尔值; 如果是 True,则在执行命令之前将检查整个 Django 项目是否存在潜在问题。 默认值为 True
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().__init__(*args, **kwargs)
        # ...
BaseCommand.create_parser(prog_name, subcommand, **kwargs)

返回一个 CommandParser 实例,它是一个 ArgumentParser 子类,其中包含一些针对 Django 的自定义。

您可以通过覆盖此方法并使用 ArgumentParser 参数的 kwargs 调用 super() 来自定义实例。

2.2 版更改:添加了 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 的字符串(如果 output_transactionTrue,则由 BEGIN;COMMIT; 包裹)。

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

使用系统检查框架检查整个 Django 项目是否存在潜在问题。 严重的问题作为 CommandError 提出; 警告输出到 stderr; 次要通知输出到标准输出。

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


BaseCommand 子类

class AppCommand

管理命令接受一个或多个应用标签参数,并对每项应用做某些事。

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

AppCommand.handle_app_config(app_config, **options)
app_config 执行命令的操作,这将是一个 AppConfig 实例,对应于命令行上给出的应用程序标签。
class LabelCommand

一个管理命令,从命令行接受一个或任意多个参数(labels),并针对每项做些事情。

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

LabelCommand.label
描述传递给命令的任意参数的字符串。 该字符串用于命令的用法文本和错误消息。 默认为 'label'
LabelCommand.handle_label(label, **options)
label 执行命令的操作,这将是命令行上给出的字符串。


命令异常

exception CommandError

异常类说明在运行管理命令时出错了。

如果在从命令行控制台执行管理命令期间引发此异常,它将被捕获并转换为打印良好的错误消息到适当的输出流(即 stderr); 因此,引发此异常(带有对错误的合理描述)是表明命令执行过程中出现问题的首选方式。

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