---
title: "编写自定义 django-admin 命令"
version: 6.1
locale: zh-hans
source: https://docs.djangoproject.com/zh-hans/6.1/howto/custom-management-commands/
canonical: https://djangodocs.dev/zh-hans/6.1/howto/custom-management-commands/
---
# 编写自定义 `django-admin` 命令

应用程序可以用 `manage.py` 注册自己的动作。例如，你可能想为你正在分发的 Django 应用添加一个 `manage.py` 动作。在本文档中，我们将为 [教程](/zh-hans/6.1/intro/tutorial01/) 中的 `polls` 应用程序构建一个自定义的 `closepoll` 命令。

要做到这一点，将一个 `management/commands` 目录添加到应用程序中。Django 将为该目录中名称不以下划线开头的每个 Python 模块注册一个 `manage.py` 命令。例如：

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

在这个例子中，`closepoll` 命令将提供给任何在 [`INSTALLED_APPS`](/zh-hans/6.1/ref/settings/#std-setting-INSTALLED_APPS) 中包含 `polls` 应用程序的项目。

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

`closepoll.py` 模块只有一个要求——必须定义 `Command` 类，该类继承自 [`BaseCommand`](#django.core.management.BaseCommand) 或其 [子类](#ref-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_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)
            )
```

> **Note**
>
> 当你使用管理命令并希望提供控制台输出时，你需要 write 至 `self.stdout` 和 `self.stderr`，而不是直接 print 至 `stdout` 和 `stderr`。利用这些代理，测试自定义命令会更方便。还需要注意的是，你不需要用换行符来结束消息，它会自动添加，除非你指定了 `ending` 参数：
>
> ```
> self.stdout.write("Unterminated line", ending="")
> ```

这个新的自定义命令能用 `python manage.py closepoll <poll_ids>` 调用。

`handle()` 方法接受一个或多个 `poll_ids`，并将每个 `poll.opened` 设置为 `False`。若用户传入了不存在的 polls，将会抛出 [`CommandError`](#django.core.management.CommandError)。[教程](/zh-hans/6.1/intro/tutorial02/) 中不存在 `poll.opened` 属性，只是这里为了本例添加至 `polls.models.Question` 的。

## 接受可选参数

同样的 `closepoll` 可以很容易地修改，通过接受额外的命令行选项来删除一个给定的投票，而不是关闭它。这些自定义选项可以在 [`add_arguments()`](#django.core.management.BaseCommand.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 字典参数传入的。参见 Python 文档查询 [`argparse`](https://docs.python.org/3/library/argparse.html#module-argparse)，获取更多 `add_argument` 的用法。

为了能添加自定义命令选项，所以的 [管理命令](/zh-hans/6.1/ref/django-admin/) 都能接受一些默认选项，例如 [`--verbosity`](/zh-hans/6.1/ref/django-admin/#cmdoption-verbosity) 和 [`--traceback`](/zh-hans/6.1/ref/django-admin/#cmdoption-traceback)。

## 管理命令和本地化

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

如果由于某些原因，你的自定义管理命令必须在没有活动的 locale 的情况下运行（例如，为了防止翻译的内容被插入数据库），请在你的 [`handle()`](#django.core.management.BaseCommand.handle) 方法上使用 `@no_translations` 装饰器停用翻译：

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

class Command(BaseCommand):
    ...

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

由于禁用翻译需要使用配置文件，故装饰器不能用于那些不加载配置文件的命令。

## 测试

关于如何测试自定义管理命令的内容可在 [测试文档](/zh-hans/6.1/topics/testing/tools/#topics-testing-management-commands) 中找到。

## 覆盖命令

Django 先注册内置命令，然后按相反的顺序在 [`INSTALLED_APPS`](/zh-hans/6.1/ref/settings/#std-setting-INSTALLED_APPS) 查找命令。在查找时，如果一个命令和已注册的命令重名，这个新发现的命令会覆盖第一个命令。

换句话说，为了覆盖一个命令，新命令必须有同样的名字并且它的 app 在 [`INSTALLED_APPS`](/zh-hans/6.1/ref/settings/#std-setting-INSTALLED_APPS) 中必须排在被覆盖命令 app 的前面。

第三方应用提供的管理命令若被不小心重写，能通过在你项目中的某个应用（在 [`INSTALLED_APPS`](/zh-hans/6.1/ref/settings/#std-setting-INSTALLED_APPS) 配置在第三方应用之前）创建新命令的方式为其取个别名，另其能被调用。这个应用需要导入被重写的 `Command`。

## 命令对象

#### `class BaseCommand`

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

如果你想处理解析命令行参数，决定调用什么代码的过程，使用这个类。如果你不需要改变任何行为，考虑直接使用它的某个 [子类](#ref-basecommand-subclasses)。

继承 [`BaseCommand`](#django.core.management.BaseCommand) 要求你重写 [`handle()`](#django.core.management.BaseCommand.handle) 方法。

### 属性

能被你派生的子类设置的，且能被 [`BaseCommand`](#django.core.management.BaseCommand) 的 [子类](#ref-basecommand-subclasses) 使用的属性。

#### `BaseCommand.help`

命令简介，当用户运行命令 `python manage.py help <command>` 时包含在打印的帮助信息内。

#### `BaseCommand.missing_args_message`

如果你的命令定义了必须的位置参数，你可以在缺失参数时返回自定义错误消息。默认输出由 [`argparse`](https://docs.python.org/3/library/argparse.html#module-argparse) 打印("too few arguments")。

#### `BaseCommand.output_transaction`

一个布尔值，决定命令是否暑输出 SQL 语句；若为 `True`，输出内容会自动以 `BEGIN;` 和 `COMMIT;` 包裹。默认值为 `False`。

#### `BaseCommand.requires_migrations_checks`

一个布尔值；若为 `True`，命令会在硬盘上存储的 migrations 与 数据库中的不一致时打印警告。警告不会阻止命令执行。默认值为 `False`。

#### `BaseCommand.requires_system_checks`

一个标签的列表或元组，例如 `[Tags.staticfiles, Tags.models]` 。系统会在执行命令 [registered in the chosen tags](/zh-hans/6.1/topics/checks/#registering-labeling-checks) 之前检查是否有错误。值 `'__all__'` 可用于指定应执行所有系统检查。  默认值为 `'__all__'` 。

#### `BaseCommand.style`

一个实例属性，用于向 `stdout` 或 `stderr` 输出彩色内容。例如:

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

参考 [语法着色](/zh-hans/6.1/ref/django-admin/#syntax-coloring) 了解如何修改调色板与现成的样式（使用本节介绍的大写字母版本的 "roles"）。

如果运行命令时传递了 [`--no-color`](/zh-hans/6.1/ref/django-admin/#cmdoption-no-color) 选项，所有的 `self.style()` 调用会返回未染色的原始字符串。

#### `BaseCommand.suppressed_base_arguments`

要在帮助输出中取消显示的默认命令选项。这应该是一个选项名称的集合（例如，`'--verbosity'`）。取消显示的选项仍然传递其默认值。

### 方法

[`BaseCommand`](#django.core.management.BaseCommand) 有很多方法可供重写，不过仅有 [`handle()`](#django.core.management.BaseCommand.handle) 是必须实现的。

> **子类中实现构造器**
>
> 若 [`BaseCommand`](#django.core.management.BaseCommand) 的子类实现了 `__init__` 方法，那么就必须调用 [`BaseCommand`](#django.core.management.BaseCommand) 的 `__init__`:
>
> ```
> class Command(BaseCommand):
>     def __init__(self, *args, **kwargs):
>         super().__init__(*args, **kwargs)
>         # ...
> ```

#### `BaseCommand.create_parser(prog_name, subcommand, **kwargs)`

返回一个 `CommandParser` 实例，它是 [`ArgumentParser`](https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser) 的子类，包含一些针对 Django 的个性化设计。

你可以自定义这个实例，通过重写此方法，并为  `super()` 方法传入值为 [`ArgumentParser`](https://docs.python.org/3/library/argparse.html#argparse.ArgumentParser) 的 `kwargs` 参数。

#### `BaseCommand.add_arguments(parser)`

添加命令行转换参数的入口，用于处理传给命令行的参数。自定义命令需要重写此方法，并同时添加命令接受的位置参数和可选参数。直接使用 `BaseCommand` 的子类时无需调用 `super()`。

#### `BaseCommand.get_version()`

返回 Django 版本，内置 Django 命令必须正确返回。用户实现的命令可以重写此方法返回自己的版本。

#### `BaseCommand.execute(*args, **options)`

尝试执行此命令，根据 [`requires_system_checks`](#django.core.management.BaseCommand.requires_system_checks) 属性控制是否需要执行系统检查。如果命令引发 [`CommandError`](#django.core.management.CommandError)，它将被拦截并打印到 `stderr`。

> **在你的代码中调用管理命令**
>
> 执行命令时，不要从代码直接调用 `execute()` 方法。而是使用 [`call_command()`](/zh-hans/6.1/ref/django-admin/#django.core.management.call_command)。

#### `BaseCommand.handle(*args, **options)`

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

此方法可能会返回一个字符串，输出至 `stdout` （若 [`output_transaction`](#django.core.management.BaseCommand.output_transaction) 为 `True`，则由 `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`](#django.core.management.CommandError) 引发；警告将输出到 `stderr`；较小的通知将输出到 `stdout`。

如果 `app_configs` 和 `tags` 都是 `None`，则执行除了部署和数据库相关检查之外的所有系统检查。`tags` 可以是一个检查标签的列表，比如 `compatibility` 或 `models`。

你可以传递 `include_deployment_checks=True` 以执行部署检查，并在 `databases` 中传递数据库别名列表以对其运行数据库相关检查。

#### `BaseCommand.get_check_kwargs(options)`

为调用 [`check()`](#django.core.management.BaseCommand.check) 提供 kwargs 参数，包括将 [`requires_system_checks`](#django.core.management.BaseCommand.requires_system_checks) 的值转换为 `tag` 关键字参数。

重写此方法可修改提供给 [`check()`](#django.core.management.BaseCommand.check) 的参数值。例如，要启用数据库相关检查，你可以按如下方式重写 `get_check_kwargs()`：

```
def get_check_kwargs(self, options):
    kwargs = super().get_check_kwargs(options)
    return {**kwargs, "databases": [options["database"]]}
```

### `BaseCommand` 的子类

#### `class AppCommand`

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

子类必须实现 [`handle_app_config()`](#django.core.management.AppCommand.handle_app_config)，而不是 [`handle()`](#django.core.management.BaseCommand.handle)。此方法会为每个应用调用一次。

#### `AppCommand.handle_app_config(app_config, **options)`

为 `app_config` 运行命令，这会是 [`AppConfig`](/zh-hans/6.1/ref/applications/#django.apps.AppConfig) 的实例，并指向命令行给定的应用标签。

#### `class LabelCommand`

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

子类必须实现 [`handle_label()`](#django.core.management.LabelCommand.handle_label)，而不是 [`handle()`](#django.core.management.BaseCommand.handle)。此方法会为每个应用调用一次。

#### `LabelCommand.label`

一个字符串，介绍了传递给命令的任意多个参数。此字符串用于命令的用法文本和错误消息。默认为 `'label'`。

#### `LabelCommand.handle_label(label, **options)`

运行 `label` 指定的命令动作，由命令行传入的字符串指定。

### 命令异常

#### `exception CommandError(returncode=1)`

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

如果在从命令行控制台执行管理命令期间引发此异常，它将被捕获并转换为适当输出流（即 `stderr`）上的精美打印的错误消息；因此，引发此异常（附带一个合理的错误描述）是指示命令执行出现问题的首选方式。它接受可选的 `returncode` 参数，以自定义管理命令退出时使用的退出状态，使用 [`sys.exit()`](https://docs.python.org/3/library/sys.html#sys.exit)。

如果管理命令是由 [`call_command()`](/zh-hans/6.1/ref/django-admin/#django.core.management.call_command) 调用的，是否捕获异常取决于你。
