Написание пользовательских команд django-admin

Приложения могут регистрировать свои собственные действия с помощью manage.py. Например, вы можете захотеть добавить действие manage.py для приложения Django, которое вы распространяете. В этом документе мы будем создавать пользовательскую команду closepoll для приложения polls из tutorial.

Для этого просто добавьте в приложение каталог management/commands. Django зарегистрирует команду manage.py для каждого модуля Python в этом каталоге, имя которого не начинается с символа подчеркивания. Например:

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

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

Модуль _private.py будет недоступен в качестве команды управления.

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

Автономные сценарии

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

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

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

Метод handle() принимает один или несколько poll_ids и устанавливает poll.opened в False для каждого из них. Если пользователь ссылается на несуществующий опрос, то возникает ошибка CommandError. Атрибут poll.opened не существует в tutorial и был добавлен в 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 в нашем примере) доступна в параметре options dict метода handle. Подробнее об использовании параметра <argparse см. документацию по Python :py``add_argument``.

Помимо возможности добавления пользовательских параметров командной строки, все management commands могут принимать некоторые параметры по умолчанию, такие как --verbosity и --traceback.

Команды управления и локали

По умолчанию команды управления выполняются с текущей активной локалью.

Если по какой-то причине ваша пользовательская команда управления должна выполняться без активной локали (например, чтобы предотвратить вставку переведенного содержимого в базу данных), отключите переводы с помощью декоратора @no_translations на вашем методе handle():

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_translations является новым. В старых версиях переводы деактивируются перед выполнением команды, если атрибут команды leave_locale_alone (теперь удален) не установлен на True.

Тестирование

Информацию о том, как тестировать пользовательские команды управления, можно найти в разделе testing docs.

Переопределение команд

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

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

Команды управления из сторонних приложений, которые были непреднамеренно переопределены, могут быть доступны под новым именем путем создания новой команды в одном из приложений вашего проекта (расположенном перед сторонним приложением в INSTALLED_APPS), которая импортирует Command переопределенной команды.

Командные объекты

class BaseCommand[исходный код]

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

Используйте этот класс, если вам нужен доступ ко всем механизмам, которые анализируют аргументы командной строки и определяют, какой код вызвать в ответ; если вам не нужно изменять ничего из этого поведения, подумайте об использовании одного из классов subclasses.

Подкласс класса BaseCommand требует, чтобы вы реализовали метод handle().

Атрибуты

Все атрибуты могут быть установлены в вашем производном классе и могут быть использованы в BaseCommandsubclasses“.

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

Булево значение; если True, весь проект Django будет проверен на наличие потенциальных проблем перед выполнением команды. Значение по умолчанию True.

BaseCommand.style

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

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

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

Если вы передадите опцию --no-color при выполнении команды, все вызовы self.style() будут возвращать исходную строку неокрашенной.

Методы

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

Реализация конструктора в подклассе

Если вы реализуете __init__ в своем подклассе BaseCommand, вы должны вызвать 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() с kwargs из ArgumentParser параметров.

Changed in Django 2.2:

kwargs был добавлен.

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)[исходный код]

Фактическая логика команды. Подклассы должны реализовать этот метод.

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

BaseCommand.check(app_configs=None, tags=None, display_num_errors=False)[исходный код]

Использует фреймворк system check для проверки всего проекта Django на наличие потенциальных проблем. Серьезные проблемы выдаются в виде CommandError; предупреждения выводятся на stderr; незначительные уведомления выводятся на stdout.

Если app_configs и tags оба являются None, выполняются все системные проверки. tags может быть списком тегов проверки, например compatibility или 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[исходный код]

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

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

Если команда управления вызывается из кода через call_command(), вы сами должны поймать исключение, когда это необходимо.

Вернуться на верх