Написание пользовательских команд django-admin
Приложения могут регистрировать собственные действия с помощью manage.py. Например, вы можете добавить действие manage.py для приложения Django, которое вы распространяете. В этом документе мы будем создавать пользовательскую closepoll команду для приложения polls из учебника.
Для этого просто добавьте директорию management/commands в приложение. Django зарегистрирует manage.py команду для каждого Python-модуля в этой директории, имя которого не начинается с нижнего подчеркивания. Например:
polls/
__init__.py
models.py
management/
__init__.py
commands/
__init__.py
_private.py
closepoll.py
tests.py
views.py
В этом примере команда closepoll будет доступна любому проекту, который включает приложение polls в INSTALLED_APPS.
Модуль _private.py не будет доступен как команда управления.
Модуль closepoll.py имеет только одно требование – он должен определить класс Command , который расширяет BaseCommand или один из его подклассов.
Самостоятельные скрипты
Пользовательские команды управления особенно полезны для запуска автономных скриптов или скриптов, которые периодически выполняются из crontab в 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_id', nargs='+', type=int)
def handle(self, *args, **options):
for poll_id in options['poll_id']:
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. Использование этих прокси значительно упрощает тестирование вашей пользовательской команды. Обратите также внимание, что вам не нужно завершать сообщения символом новой строки, он будет добавлен автоматически, если вы не укажете параметр %%%CODE_BLOCK_20%%:
self.stdout.write("Unterminated line", ending='')
Новую пользовательскую команду можно вызвать используя python manage.py closepoll
<poll_id>.
Метод handle() принимает один или несколько poll_ids и устанавливает poll.opened в False для каждого из них. Если пользователь указал несуществующие опросы, будет поднята ошибка CommandError. Атрибут poll.opened отсутствует в учебнике и был добавлен в polls.models.Question для этого примера.
Принятие необязательных аргументов
Ту же closepoll команду можно легко изменить, чтобы удалять заданный опрос вместо его закрытия, приняв дополнительные опции командной строки. Эти пользовательские опции можно добавить в метод add_arguments() следующим образом:
class Command(BaseCommand):
def add_arguments(self, parser):
# Positional arguments
parser.add_argument('poll_id', nargs='+', type=int)
# Named (optional) arguments
parser.add_argument(
'--delete',
action='store_true',
dest='delete',
help='Delete poll instead of closing it',
)
def handle(self, *args, **options):
# ...
if options['delete']:
poll.delete()
# ...
Опция (delete в нашем примере) доступна в словаре options параметра метода handle. См. документацию Python argparse для получения дополнительной информации об использовании add_argument.
Помимо добавления пользовательских опций командной строки, все команды управления могут принимать некоторые стандартные опции, такие как --verbosity и --traceback.
Команды управления и локали
По умолчанию, метод BaseCommand.execute() отключает перевод, потому что некоторые команды, поставляемые с Django, выполняют несколько задач (например, отображение пользовательского контента и заполнение базы данных), которые требуют языка строк, независимого от проекта.
Если по какой-то причине вашей пользовательской команде управления требуется использовать фиксированную локаль, вы должны вручную активировать и деактивировать её в методе handle() с использованием функций, предоставляемых кодом поддержки I18N:
from django.core.management.base import BaseCommand, CommandError
from django.utils import translation
class Command(BaseCommand):
...
def handle(self, *args, **options):
# Activate a fixed locale, e.g. Russian
translation.activate('ru')
# Or you can activate the LANGUAGE_CODE # chosen in the settings:
from django.conf import settings
translation.activate(settings.LANGUAGE_CODE)
# Your command logic here
...
translation.deactivate()
Другая потребность может заключаться в том, чтобы ваша команда просто использовала локаль, установленную в настройках, и Django не отключал её. Вы можете добиться этого, используя опцию BaseCommand.leave_locale_alone.
При работе со сценариями, описанными выше, следует учитывать, что команды управления системой обычно должны быть очень внимательны при работе в различных локалях, поэтому вам может потребоваться:
- Убедиться, что настройка
USE_I18NвсегдаTrueпри выполнении команды (это хороший пример потенциальных проблем, возникающих в динамической среде выполнения, которых команды Django избегают, отключая переводы). - Проверить код вашей команды и код, который она вызывает, на предмет различий в поведении при изменении локалей и оценить его влияние на предсказуемость поведения вашей команды.
Тестирование
Информация о том, как протестировать пользовательские команды управления, содержится в документации по тестированию.
Переопределение команд
Django регистрирует встроенные команды, а затем ищет команды в INSTALLED_APPS в обратном порядке. Во время поиска, если имя команды дублирует уже зарегистрированную команду, новая обнаруженная команда переопределяет первую.
Другими словами, для переопределения команды новая команда должна иметь то же имя, и её приложение должно быть перед приложением переопределяемой команды в INSTALLED_APPS.
Команды управления из сторонних приложений, которые были непреднамеренно переопределены, могут быть доступны под новым именем путём создания новой команды в одном из приложений вашего проекта (упорядоченных перед приложением стороннего разработчика в INSTALLED_APPS), которая импортирует Command переопределённой команды.
Объекты команд
-
class BaseCommand[source]
Базовый класс, от которого в конечном счете все команды управления наследуются.
Используйте этот класс, если хотите получить доступ ко всем механизмам, которые анализируют аргументы командной строки и определяют, какой код нужно вызвать в ответ; если вам не нужно изменять какое-либо из этого поведения, рассмотрите использование одного из его подклассов.
Наследование от класса BaseCommand требует реализации метода handle().
Атрибуты
Все атрибуты можно установить в вашем производном классе и использовать в BaseCommand’s подклассах.
-
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.leave_locale_alone -
Булево значение, указывающее, должна ли локаль, установленная в настройках, сохраняться во время выполнения команды, вместо того, чтобы переводы отключались.
Значение по умолчанию
False.Убедитесь, что вы понимаете, что делаете, если решите изменить значение этого параметра в своей пользовательской команде, если она создаёт локально-чувствительный контент базы данных, и такой контент не должен содержать никаких переводов (как это происходит, например, с
django.contrib.authразрешениями), так как активация любой локали может вызвать непредвиденные последствия. См. раздел Команды управления и локали выше для получения дополнительной информации.
-
BaseCommand.style -
Атрибут экземпляра, помогающий создавать цветной вывод при записи в
stdoutилиstderr. Например:self.stdout.write(self.style.SUCCESS('...'))См. Цветное форматирование синтаксиса, чтобы узнать, как изменить цветовую палитру и увидеть доступные стили (используйте заглавные версии «ролей», описанных в этом разделе).
Если вы передадите параметр
--no-colorпри выполнении вашей команды, всеself.style()вызовы вернут исходную строку без форматирования.
Методы
BaseCommand имеет несколько методов, которые можно переопределить, но только метод handle() должен быть реализован.
Реализация конструктора в подклассе
Если вы реализуете __init__ в своем подклассе BaseCommand, вы должны вызвать BaseCommand’s __init__:
class Command(BaseCommand):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# ...
-
BaseCommand.add_arguments(parser)[source] -
Точка входа для добавления аргументов парсера для обработки аргументов командной строки, переданных команде. Пользовательские команды должны переопределить этот метод, чтобы добавить как позиционные, так и необязательные аргументы, принимаемые командой. Вызов
super()не требуется при непосредственном наследовании отBaseCommand.
-
BaseCommand.get_version()[source] -
Возвращает версию Django, которая должна быть правильной для всех встроенных команд Django. Команды, предоставленные пользователем, могут переопределить этот метод, чтобы вернуть свою собственную версию.
-
BaseCommand.execute(*args, **options)[source] -
Попытка выполнить эту команду, выполнив системные проверки, если это необходимо (как управляется атрибутом
requires_system_checks). Если команда вызывает исключениеCommandError, оно перехватывается и выводится на stderr.
Вызов команды управления в вашем коде
execute() не следует вызывать напрямую из вашего кода для выполнения команды. Используйте call_command() вместо этого.
-
BaseCommand.handle(*args, **options)[source] -
Фактическая логика команды. Подклассы должны реализовать этот метод.
Он может вернуть строку, которая будет выведена на
stdout(оборачиваетсяBEGIN;иCOMMIT;еслиoutput_transactionявляетсяTrue).
-
BaseCommand.check(app_configs=None, tags=None, display_num_errors=False)[source] -
Использует фреймворк системной проверки для проверки всего проекта 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[source]
Класс исключений, указывающий на проблему при выполнении команды управления.
Если это исключение возникает во время выполнения команды управления из консоли командной строки, оно будет перехвачено и превратится в красиво отформатированное сообщение об ошибке в соответствующий поток вывода (т. е. stderr); в результате, поднятие этого исключения (с разумным описанием ошибки) является предпочтительным способом указать, что что-то пошло не так при выполнении команды.
Если команда управления вызывается из кода через call_command(), вам нужно перехватить исключение, когда это необходимо.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.0/howto/custom-management-commands/