Блог

Python/Django: расширяем менеджер команд

Основным преимуществом является то, что система позволяет расширять набор команд в вашем приложении. И эти расщирения можно использавать для облегчения работы приложения, выпонять рассылки, сложные обработки и т.д.

Все команды находятся в “django/core/management/commands/”. Каждая команда расположена в отдельном файле. Имя файла является именем команды который указывается первым параметром в manage.py. В файле должен быть объявлен класс Command который наследует один из стандртных классов типо BaseCommand или LabelCommand и. т. Созданный класс содержит логику обработки, параметров командной обрабатываются при помощи модуля optparse.

Основным аспектом создания команды является реализация метода handle, который принимает позиционные и именованные параметры.

Существуют базовые классы, специализированные и в которых необходимо реализовывать другие методы, которые для упрощения, возвращают некую строку которая будет выведена на экран в командной строке.

AppCommand – для обработки конкретных приложений.
Метод:

def handle_app(self, app, **options):
   # app - объект приложения
   #...

LabelCommand – для обработки меток.

def handle_label(self, label, **options):
   # label - имя метки. 
   # Передаются как позиционные аргументы в командной строке
   #...

NoArgsCommand – команды без аргументов, но может иметь опции

def handle_noargs(self, **options):
   #...

BaseCommand имеет несколько стандартных опций, такие как:
–settings,
–pythonpath и,
–traceback.
def handle(self, *args, **options):
#…

В производных командах можно добавлять опции, добавляя атрибут option_list.
Например как в стандартной команде test:

class Command( BaseCommand ):
    option_list = BaseCommand.option_list + (
        make_option(
            '--verbosity',
            action='store',
            dest='verbosity',
            default='1',
            type='choice',
            choices=['0', '1', '2'],
            help='Verbosity level; 0=minimal output, 1=normal output, 2=all output'
        ),
        make_option(
            '--noinput',
            action='store_false',
            dest='interactive',
            default=True,
            help='Tells Django to NOT prompt the user for input of any kind.'
        ),
    )

Функция make_option – входит в пакет optparse.
https://docs.djangoproject.com/en/1.4/howto/custom-management-commands/#BaseCommand.option_list

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

Есть ещё два полезных атрибута, это help и args, в которых можно описать команду, описывать конструкции т.д.

Теперь, как добавить новую команду для приложения?

Для того чтобы добавить новую команду в свое приложение необходимо создать в корне директории приложения директорию management, в ней в свою очередь директорию commands, а там уже модуль команды с уникальным именем. Джанга проверяет наличие дополнительных команд для всех установленных приложений, т.е. указанных в INSTALLED_APPS.

Для примера покажу реализацию команды которая печатает имена моделей в заданном приложении и если установлена опция –count то и количество объектов данной модели в базе данных:

#app.management.commands.modelsinfo.py
from django.core.management.base import AppCommand
from optparse import make_option

class Command( AppCommand ):
    option_list = AppCommand.option_list + (
        make_option(
            ‘–count',
            action='store_true',
            dest='count',
            default= False,
            help='Other info…'
        ),
    )
    args = '<poll_id poll_id …>'
    help = 'Other info…'

    def handle_app(self, app, **options):
        from django.db.models import get_models
        lines = []
        for model in get_models(app):
            lines.append("[%s]" %
                model.__name__ + (
                    options["count"] and ” – %s objects”%
                    model._default_manager.count() or “”
                )
            )

        return "\n".join(lines)

Ну и вот такой результат получился у меня:

$ python manage.py modelsinfo test –count

[Tags] – 0 objects
[Shared] – 1 objects

Расширять менеджер командной строки очень полезно, для решения различных громоздких задач.