# Django 2.2.9.dev20191202104323 文档

# 编写自定义 `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` 命令将对 `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))

注解

当你使用管理命令时，期望在终端输出内容，你需要 write 至 `self.stdout` 和 `self.stderr`，而不是直接 print 至 `stdout` 和 `stderr`。利用这些代理，测试定义命令会更方便。也请注意下，你不需要为消息添加一个新行字符，它会自动添加，除非你指定了 `ending` 参数:

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

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

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

## 接受可选参数

`closepoll` 通过简单地添加几个命令选项，就能删除指定 poll，替代之前的关闭行为。这些自定义选项能被加入至 `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`，获取更多 `add_argument` 的用法。

为了能添加自定义命令选项，所以的 管理命令 都能接受一些默认选项，例如 `--verbosity` 和 `--traceback`。

## 管理命令和地点(locales)

默认情况下，管理命令以当前激活地点执行。

如果出于某些原因，自定义管理命令必须不以某个地点执行（比如，为了避免将已翻译的内容存入数据库），可在 `handle()` 方法中利用 `@no_translations` 装饰器禁用翻译行为:

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

    class Command(BaseCommand):
        ...

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

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

Changed in Django 2.1:

`@no_tanslations` 装饰器是新的。在旧版本中，运行命令时，翻译行为默认是关闭的，除非命令的 `leave_locale_alone` 属性（现已移除）被置为 `True`。

## 测试

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

## 覆盖命令

Django 先注册内置命令，然后按相反的顺序在 `INSTALLED_APPS` 查找命令。在查找时，如果一个命令和已注册的命令重名，这个新发现的命令会覆盖第一个命令。

换句话说，为了覆盖一个命令，新命令必须有同样的名字并且它的 app 在 `INSTALLED_APPS` 中必须排在被覆盖命令 app 的前面。

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

## 命令对象

class `BaseCommand`

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

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

继承 `BaseCommand` 要求你重写 `handle()` 方法。

### 属性

能被你派生的子类设置的，且能被 `BaseCommand` 的 子类 使用的属性。

`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`

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

`BaseCommand.``requires_system_checks`

  一个布尔值；若为 `True`，会在执行命令前完整地检验 Django 项目，确定是否有潜在的问题。默认值为 `True`。

`BaseCommand.``style`

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

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

  参考 Syntax coloring 了解如何修改调色板与现成的样式（使用本节介绍的大写字母版本的 "roles"）。

  如果运行命令时传递了 `--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 的个性化设计。

  你可以自定义这个实例，通过重写此方法，并为 `super()` 方法传入值为 `ArgumentParser` 的 `kwargs` 参数。

  Changed in Django 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_transaction` 为 `True`，则由 `BEGIN;` 和 `COMMIT` 包裹）。

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

  利用系统检查框架校验 Django 项目是否存在隐含的问题。严重的问题会抛出 `CommandError`；警告会输出至 stderr；次要通知输出至 stdout。

  若 `app_configs` 和 `tags` 均为 `None`，所以的系统检查项都会被运行。`tags` 能是一个检查标签的列表，例如 `compatibility` 或 `models`。

### `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()` 调用的，是否捕获异常取决于你。