Spec-Zone.ru › Django 1.8

Написание пользовательских команд 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 или панели управления запланированными задачами Windows.

Для реализации команды отредактируйте polls/management/commands/closepoll.py следующим образом:

from django.core.management.base import BaseCommand, CommandError
from polls.models import 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('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.Poll для этого примера.

Принятие необязательных аргументов

Ту же самую 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 в нашем примере) доступен в параметре options словаря метода 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 будет проверен на возможные проблемы перед выполнением команды. Если requires_system_checks отсутствует, используется значение requires_model_validation. Если и этот флаг отсутствует, используется значение по умолчанию (True). Определение обоих requires_system_checks и requires_model_validation приведёт к ошибке.

BaseCommand.requires_model_validation

Устарело начиная с версии 1.7: Заменено на requires_system_checks

Булево значение; если True, будет выполнена валидация установленных моделей перед выполнением команды. Значение по умолчанию True. Чтобы проверить модели отдельного приложения, а не всех приложений, вызовите validate() из handle().

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.NOTICE('...'))

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

Если вы передаёте параметр --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_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.validate(app=None, display_num_errors=False) [source]

Устарело начиная с версии 1.7: Заменено на команду check

Если app равно None, то проверяются все установленные приложения на наличие ошибок.

Подклассы BaseCommand

class AppCommand

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

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

AppCommand.handle_app_config(app_config, **options)

Выполнить действия команды для app_config, который будет экземпляром AppConfig, соответствующим значению имени приложения, указанному в командной строке.

END_OF_DOCUMENT_MARKER

Ранее подклассы AppCommand должны были реализовывать handle_app(app, **options), где app был модулем моделей. Новый API позволяет обрабатывать приложения без модуля моделей. Самый быстрый способ миграции следующий:

def handle_app_config(app_config, **options):
    if app_config.models_module is None:
        return                                  # Or raise an exception.
    app = app_config.models_module
    # Copy the implementation of handle_app(app_config, **options) here.

Однако, вы можете упростить реализацию, используя напрямую атрибуты app_config.

class LabelCommand

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

Вместо реализации handle(), подклассы должны реализовать handle_label(), которая будет вызвана один раз для каждой метки.

LabelCommand.handle_label(label, **options)

Выполнить действия команды для label, которая будет строкой, заданной в командной строке.

class NoArgsCommand

Устарело начиная с версии 1.8: Используйте BaseCommand вместо этого, которая по умолчанию не принимает аргументов.

Команда, которая не принимает аргументов в командной строке.

Вместо реализации handle(), подклассы должны реализовать handle_noargs(); handle() сама переопределяется для того, чтобы убедиться, что команде не передаются аргументы.

NoArgsCommand.handle_noargs(**options)

Выполнить действия этой команды

Исключения команд

class CommandError [source]

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

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

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

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.8/howto/custom-management-commands/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API