Написание пользовательских команд 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))
Примечание
Когда вы используете команды управления и хотите вывести данные в консоль, вы должны писать в 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()
# ...
Параметр (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 -
Новая функция в Django 1.10.
Булево значение; если
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(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, оно перехватывается и печатается в stderr.
Вызов команды управления в вашем коде
execute() не следует вызывать напрямую из вашего кода для выполнения команды. Используйте call_command() вместо этого.
-
BaseCommand.handle(*args, **options)[source] -
Фактическая логика команды. Подклассы должны реализовать этот метод.
Он может вернуть строку Unicode, которая будет напечатана в
stdout(внутриBEGIN;иCOMMIT;, еслиoutput_transactionTrue).
-
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/1.11/howto/custom-management-commands/