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/
__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__'.Changed in Django 3.2:In older versions, the
requires_system_checksattribute expects a boolean value instead of a list or tuple of tags.
- 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_checksattribute). If the command raises aCommandError, 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_configsandtagsare bothNone, all system checks are performed.tagscan be a list of check tags, likecompatibilityormodels.
Подклассы 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, то у вас будет шанс перехватить исключения.
The returncode argument was added.