• 3.2
  • 5.0
  • 6.1
  • Версия документации: 3.1

Writing custom django-admin commands

Проект может быть расширен собственными командами для manage.py. Например, вы можете добавить действие для развёртывания. Здесь же мы будем реализовывать действие closepoll для приложения polls из tutorial.

To do this, add a management/commands directory to the application. Django will register a manage.py command for each Python module in that directory whose name doesn’t begin with an underscore. For example:

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

В этом примере команда closepoll будет доступна для любого проекта, который импортирует приложение polls в INSTALED_APPS.

Модуль _private.py не доступен как команда для manage.py.

Для модуля closepoll.py должно быть соблюдено лишь одно требование - наличие в нём класса Command, который унаследован от BaseCommand или его потомков.

Автономные скрипты

Собственные команды могут быть полезны для реализации отдельных скриптов, например, для планировщика Windows или crontab.

Для реализации команды отредактируйте 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.stdout и self.stderr вместо stdout и stderr. Использование такого перенаправления может помочь при тестировании скрипта. Кстати, стоит помнить, что символ перевода строки ставится по умолчанию, если вы не указали другой в параметре ending:

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

Новая команда может быть запущена следующим образом: python manage.py closepoll <poll_id>.

Метод handle() принимает один или более poll_ids и устанавливает каждому poll.opened в False. Если пользователь запросит несуществующий опрос, то будет выброшено исключение CommandError. Атрибут poll.opened не существует в tutorial и добавлен в polls.models.Poll в этом примере.

Получение необязательных аргументов

Аналогичный 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(). Подробнее можно посмотреть в документации по argparse, где говорится про использование add_argument.

Помимо возможности принимать пользовательские опции, все стандартные команды принимают также опции по умолчанию, такие как :djadminopt:`--verbosity` и :djadminopt:`--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 («too few arguments»).

BaseCommand.output_transaction

Булево значение, которое определяет будет ли команда выводить SQL выражения. Если установлено в True, то весь вывод будет автоматически заключён между BEGIN; и COMMIT;. Значение по умолчанию - False.

BaseCommand.requires_migrations_checks

Булево значение; если True, команда выводит предупреждение, если набор миграций на диске не соответствует миграциям в базе данных. Предупреждение не препятствует выполнению команды. Значение по умолчанию - False.

BaseCommand.requires_system_checks

A boolean; if True, the entire Django project will be checked for potential problems prior to executing the command. Default value is True.

BaseCommand.style

Атрибут экземпляра команды, который помогает выделять цветом вывод в stdout или stderr. Например:

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

Смотрите Подсветка синтаксиса, чтобы узнать доступные стили и как поменять цвета (используйте названия «ролей», описанных в этом разделе, в верхнем регистре).

Если указать опцию :djadminopt:`--no-color` при запуске команды, все вызовы self.style() вернут оригинальную строку без выделения цветом.

Методы

BaseCommand содержит несколько методов, которые могут быть переопределены, однако для минимальной работы команды требуется только реализация метода handle().

Переопределение конструктора

Если вы переопределяете __init__, убедитесь, что вызываете в нём __init__ базового класса BaseCommand:

class Command(BaseCommand):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # ...
BaseCommand.create_parser(prog_name, subcommand, **kwargs)

Возвращает экземпляр CommandParser, который является Подкласс ArgumentParser с несколькими настройками для Django.

Вы можете настроить экземпляр, переопределив этот метод и вызвав super() с kwargs параметров ArgumentParser.

BaseCommand.add_arguments(parser)

Точка входа для добавления аргументов парсера для обработки аргументов командной строки. Команды должны переопределять этот метод для добавления как позиционных, так и необязательных аргументов, принимаемых командой. Вызов super() не требуется при прямом наследовании BaseCommand.

BaseCommand.get_version()

Возвращает версию Django, которая необходима для работы всех встроенных команд Django. Пользовательские команды могут переопределить этот метод и вернуть свою версию.

BaseCommand.execute(*args, **options)

Tries to execute this command, performing system checks if needed (as controlled by the requires_system_checks attribute). If the command raises a CommandError, it’s intercepted and printed to stderr.

Вызов команды в коде

Не вызывайте execute() непосредственно из кода команды. Используйте вместо этого call_command.

BaseCommand.handle(*args, **options)

Собственно, логика команды. Ваш подкласс обязательно должен реализовывать этот метод.

Может вернуть Unicode строку, которая будет выведена в stdout (обернута в BEGIN; и COMMIT;, если output_transaction равен True).

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

Uses the system check framework to inspect the entire Django project for potential problems. Serious problems are raised as a CommandError; warnings are output to stderr; minor notifications are output to stdout.

If app_configs and tags are both None, all system checks are performed. tags can be a list of check tags, like compatibility or models.

Подклассы BaseCommand

class AppCommand

Служебная команда, которая принимает в качестве аргументов имена установленных приложений и выполняет с ними однотипные действия.

Вместо реализации handle(), реализуйте метод handle_app_config(), который будет вызываться для каждого приложения.

AppCommand.handle_app_config(app_config, **options)

Выполняет действия для app_config, который является объектом AppConfig приложения, которое было указано при выполнении команды.

class LabelCommand

Команда, которая принимает один или несколько аргументов в командной строке (меток) и что-то делает с ними.

Вместо реализации handle() нужно реализовать handle_label(), который будет вызываться для каждого аргумента.

LabelCommand.label

Строка, описывающая произвольные аргументы, переданные команде. Строка используется в хелп-тексте и сообщениях об ошибках команды. По умолчанию 'label'.

LabelCommand.handle_label(label, **options)

Выполняет действие для label, которая была передана через командную строку.

Исключения команды

exception CommandError(returncode=1)

Этот класс сигнализирует о неожиданных ситуациях при выполнении команды.

If this exception is raised during the execution of a management command from a command line console, it will be caught and turned into a nicely-printed error message to the appropriate output stream (i.e., stderr); as a result, raising this exception (with a sensible description of the error) is the preferred way to indicate that something has gone wrong in the execution of a command. It accepts the optional returncode argument to customize the exit status for the management command to exit with, using sys.exit().

Если команда будет вызвана из кода через call_command, то у вас будет шанс перехватить исключения.

Changed in Django 3.1:

The returncode argument was added.

Back to Top