Написание пользовательских команд 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
В Python 2 обязательно включите файлы __init__.py как в директории management, так и в management/commands, как показано выше, иначе ваша команда не будет распознана.
В этом примере команда 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))
До Django 1.8 команды управления базировались на модуле optparse, и позиционные аргументы передавались в *args, а необязательные аргументы — в **options. Сейчас команды управления используют argparse для обработки аргументов, и все аргументы передаются в **options по умолчанию, если вы не назначаете имена вашим позиционным аргументам, чтобы использовать args (режим совместимости). Рекомендуется использовать исключительно **options для новых команд.
Примечание
При использовании команд управления и необходимости вывода в консоль, следует писать в 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 не существует в учебнике и был добавлен в 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',
default=False,
help='Delete poll instead of closing it',
)
def handle(self, *args, **options):
# ...
if options['delete']:
poll.delete()
# ...
Ранее поддерживалась только стандартная библиотека optparse, и вам нужно было бы расширить переменную команды option_list с помощью optparse.make_option().
Параметр (delete в нашем примере) доступен в словаре параметров метода handle. Обратитесь к документации Python по argparse для получения более подробной информации об использовании add_argument.
Помимо возможности добавления пользовательских параметров командной строки, все команды управления могут принимать некоторые параметры по умолчанию, такие как --verbosity и --traceback.
Команды управления и языковые локали
По умолчанию метод BaseCommand.execute() деактивирует переводы, так как некоторые команды, поставляемые с Django, выполняют несколько задач (например, отображение пользовательского контента и заполнение базы данных), которые требуют языка строк, не зависящего от проекта.
В предыдущих версиях Django принудительно использовалась локаль «en-us» вместо деактивации переводов.
Если по какой-то причине ваша пользовательская команда управления должна использовать фиксированную локаль, вам следует вручную активировать и деактивировать её в методе handle() с помощью функций, предоставляемых кодом поддержки I18N:
from django.core.management.base import BaseCommand, CommandError
from django.utils import translation
class Command(BaseCommand):
...
can_import_settings = True
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-команды избегают, временно деактивируя переводы). - Проверить код вашей команды и код, который она вызывает, на наличие различий в поведении при изменении локалей и оценить его влияние на предсказуемость поведения вашей команды.
Тестирование
Сведения о том, как протестировать пользовательские команды управления, можно найти в документации по тестированию.
Объекты команд
-
class BaseCommand[source]
Базовый класс, от которого в конечном итоге происходят все команды управления.
Используйте этот класс, если вы хотите получить доступ ко всем механизмам, которые анализируют аргументы командной строки и определяют, какой код следует вызвать в ответ; если вам не нужно изменять это поведение, рассмотрите использование одного из его подклассов.
Расширение класса BaseCommand требует реализации метода handle().
Атрибуты
Все атрибуты могут быть установлены в вашем производном классе и могут использоваться в BaseCommand’s подклассах.
-
BaseCommand.args -
Строка, перечисляющая аргументы, принимаемые командой, пригодная для использования в сообщениях справки; например, команда, которая принимает список имён приложений, может установить это значение на ‘<app_label app_label ...>’.
Устарело начиная с версии 1.8: Это следует выполнять теперь в методе
add_arguments(), вызывая методparser.add_argument(). Смотрите примерclosepollвыше.
-
BaseCommand.can_import_settings -
Булево значение, указывающее, должна ли команда иметь возможность импортировать настройки Django; если
True,execute()проверит, возможно ли это, прежде чем продолжать. Значение по умолчанию —True.
-
BaseCommand.help -
Краткое описание команды, которое будет выведено в сообщении справки, когда пользователь выполнит команду
python manage.py help <command>.
-
BaseCommand.missing_args_message -
Если ваша команда определяет обязательные позиционные аргументы, вы можете настроить сообщение об ошибке, возвращаемое в случае отсутствия аргументов. По умолчанию выводится значение, возвращаемое
argparse(«слишком мало аргументов»).
-
BaseCommand.option_list -
Это список
optparseопций, которые будут переданы вOptionParserкоманды для обработки аргументов.Устарело начиная с версии 1.8: Теперь следует переопределить метод
add_arguments(), чтобы добавить пользовательские аргументы, принимаемые вашей командой. См. пример выше.
-
BaseCommand.output_transaction -
Булево значение, указывающее, выводит ли команда SQL-запросы; если
True, вывод будет автоматически обернутBEGIN;иCOMMIT;. Значение по умолчаниюFalse.
-
BaseCommand.requires_system_checks -
Булево значение; если
True, весь проект Django будет проверен на наличие потенциальных проблем перед выполнением команды. Значение по умолчаниюTrue.
-
BaseCommand.leave_locale_alone -
Булево значение, указывающее, следует ли сохранить локаль, заданную в настройках, во время выполнения команды вместо принудительного её установления на ‘en-us’.
Значение по умолчанию
False.Убедитесь, что вы понимаете, что делаете, если решите изменить значение этого параметра в своей пользовательской команде, если она создаёт языкозависимое содержимое базы данных, и такое содержимое не должно содержать никаких переводов (как это происходит, например, с разрешениями django.contrib.auth), так как изменение локали от фактического значения по умолчанию ‘en-us’ может вызвать непредвиденные последствия. Смотрите раздел Команды управления и локали выше для получения дополнительных сведений.
Этот параметр не может быть
Falseкогда параметрcan_import_settingsустановлен наFalse, так как попытка установить локаль требует доступа к настройкам. Это условие сгенерируетCommandError.
-
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(Command, self).__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, оно перехватывается и выводится на стандартный поток ошибок.
Вызов команды управления в вашем коде
execute() не должен вызываться непосредственно из вашего кода для выполнения команды. Используйте call_command() вместо этого.
-
BaseCommand.handle(*args, **options)[source] -
Фактическая логика команды. Подклассы должны реализовать этот метод.
Он может возвращать строку Unicode, которая будет выведена на
stdout(обрамлённуюBEGIN;иCOMMIT;еслиoutput_transactionравноTrue).
-
BaseCommand.check(app_configs=None, tags=None, display_num_errors=False)[source] -
Использует систему проверок, чтобы проверить весь проект Django на наличие потенциальных проблем. Серьезные проблемы поднимаются как
CommandError; предупреждения выводятся на стандартный поток ошибок; небольшие уведомления выводятся на стандартный поток вывода.Если
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.handle_label(label, **options) -
Выполняет действия команды для
label, которое будет строкой, заданной в командной строке.
-
class NoArgsCommand
Устарело начиная с версии 1.8: Используйте BaseCommand вместо этого, который по умолчанию не принимает аргументов.
Команда, которая не принимает аргументов в командной строке.
Вместо реализации handle(), подклассы должны реализовывать handle_noargs(); handle() переопределяется для того, чтобы убедиться, что команде не передаются аргументы.
-
NoArgsCommand.handle_noargs(**options) -
Выполнить действия этой команды
Исключения команд
-
exception CommandError[source]
Класс исключений, указывающий на проблему при выполнении команды управления.
Если это исключение возникает во время выполнения команды управления из консоли командной строки, оно будет перехвачено и преобразовано в красиво отформатированное сообщение об ошибке в соответствующий поток вывода (т.е., stderr); в результате, поднятие этого исключения (с осмысленным описанием ошибки) является предпочтительным способом указания того, что в выполнении команды что-то пошло не так.
Если команда управления вызывается из кода через call_command(), вам нужно перехватить исключение при необходимости.
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.9/howto/custom-management-commands/