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

Реализация собственных команд django-admin

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

Для этого добавим в приложение каталог management/commands. Для каждого модуля в этом каталоге, который не начинается с подчёркивания, Django создаст соответствующую команду. Например:

polls/
    __init__.py
    models.py
    management/
        __init__.py
        commands/
            __init__.py
            _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

Список или кортеж тегов, например, [Tags.staticfiles, Tags.models]. Системные проверки зарегистрированные в выбранных тегах будут проверены на наличие ошибок перед выполнением команды. Значение '__all__' можно использовать для указания того, что должны быть выполнены все системные проверки. Значение по умолчанию - '__all__'.

BaseCommand.style

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

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

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

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

BaseCommand.suppressed_base_arguments

Параметры команды по умолчанию для подавления в выводе справки. Это должен быть набор имен параметров (например, '--verbosity'). Значения по умолчанию для подавляемых параметров по-прежнему передаются.

Методы

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)

Попытается выполнить команду, выполнив при необходимости проверку системы (регулируется атрибутом requires_system_checks). Если команда выбрасывает исключение CommandError, будет разумно перехватить его и распечатать в 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, include_deployment_checks=False, fail_level=checks.ERROR, databases=None)

Выполняет проверку проекта на предмет ошибок. Ошибки вызывают исключение CommandError, предупреждения просто выводятся в stderr, все остальные уведомления – в stdout.

Если app_configs и tags равны None, выполняется полная проверка. tags может содержкать список тегов, указывающих что проверять, например, compatibility или models.

Вы можете передать include_deployment_checks=True, чтобы также выполнить проверки развертывания, и список псевдонимов баз данных в databases, чтобы запустить проверки, связанные с базой данных.

Подклассы 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)

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

Если это исключение произошло в ходе выполнения команды из консоли, то оно будет преобразовано в понятное сообщение об ошибке и выведено в стандартный поток ошибок (например, stderr). Генерация этого исключения (с подробным описанием ошибки) - предпочтительный способ сообщения об исключительной ситуации в ходе выполнения команды.

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

Back to Top