如何创建自定义 django-admin 命令

应用程序可以使用 manage.py 注册自己的操作。例如,你可能希望为正在分发的 Django 应用程序添加一个 manage.py 操作。在本指南中,我们将为 教程中的 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

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

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

模块 closepoll.py 只有一个要求——它必须定义一个扩展 BaseCommand 或其 子类 之一的类 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_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() 采用一个或多个 poll_ids,并将每个 poll.opened 设置为 False。如果用户引用了任何不存在的投票,则会引发 CommandError。属性 poll.opened教程 中不存在,并已添加到 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 字典参数中可用。有关 add_argument 用法的更多信息,请参阅 argparse Python 文档。

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

管理命令和语言环境

默认情况下,管理命令使用当前活动语言环境执行。

如果由于某种原因,您的自定义管理命令必须在没有活动语言环境的情况下运行(例如,为了防止翻译内容插入到数据库中),请使用 @no_translations 装饰器在 handle() 方法上停用翻译

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


class Command(BaseCommand):
    ...

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

由于停用翻译需要访问已配置的设置,因此该装饰器不能用于在没有已配置设置的情况下工作的命令。

测试

有关如何测试自定义管理命令的信息,请参阅 测试文档

覆盖命令

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

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

可以通过在项目的某个应用(在 INSTALLED_APPS 中按顺序位于第三方应用之前)中创建新命令来让无意中被覆盖的第三方应用的管理命令在新名称下可用,该命令导入被覆盖命令的 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

标签列表或元组,例如 [Tags.staticfiles, Tags.models]。在所选标签中注册的系统检查将在执行命令之前检查错误。值 '__all__' 可用于指定应执行所有系统检查。默认值为 '__all__'

BaseCommand.style

写入 stdoutstderr 时有助于创建彩色输出的实例属性。例如

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

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

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

BaseCommand.suppressed_base_arguments

在帮助输出中要禁止的默认命令选项。这应为一组选项名称(例如 '--verbosity')。仍然会传递禁止选项的默认值。

方法

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 进行了少量自定义。

你可以通过覆盖此方法并使用 kwargs 调用 super() 来自定义实例,其中包含 ArgumentParser 参数。

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, include_deployment_checks=False, fail_level=checks.ERROR, databases=None)

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

如果 app_configstags 均为 None,则执行所有系统检查,但部署和数据库相关检查除外。 tags 可以是检查标签列表,例如 compatibilitymodels

你可以传递 include_deployment_checks=True 来执行部署检查,并在 databases 中列出数据库别名列表,以针对它们运行数据库相关检查。

BaseCommand 子类

class AppCommand

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

子类必须实现 handle_app_config(),而不是实现 handle(),该方法将针对每个应用程序调用一次。

AppCommand.handle_app_config(app_config, **options)

app_config 执行命令操作,它将是与命令行中给出的应用程序标签相对应的 AppConfig 实例。

class LabelCommand

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

子类必须实现 handle_label(),而不是实现 handle(),该方法将针对每个标签调用一次。

LabelCommand.label

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

LabelCommand.handle_label(label, **options)

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

命令异常

异常 CommandError(returncode=1)

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

如果在从命令行控制台执行管理命令期间引发此异常,它将被捕获并转换为针对适当输出流(即 stderr)的格式良好的打印错误消息;因此,引发此异常(并附有错误的合理描述)是指示命令执行过程中出现问题的方式。它接受可选的 returncode 参数,以使用 sys.exit() 自定义管理命令退出的退出状态。

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

返回顶部