А вы задумывались как работает волшебный manage.py? Как он там внутри устроен? А, между прочим, очень интересно.
Вначале чуть-чуть истории. До августа прошлого года вся логика manage.py была в одном большом файле django/core/management.py. Там обработчики всех команд были написаны скопом, от чего файл стал просто монcтруозным и трудно поддавался созерцанию. Да и не расширяем извне.
Видимо, разработчикам джанги это надоело и они в целях облегчения своей жизни и жизни всех сочувствующих изменили систему. Её основным преимуществом является то, что новая система позволяет расширять набор команд в сторонних приложениях.
Вся машинерия переехала в django/core/management/commands/. Каждая команда расположена в отдельном файле. Имя файла является именем команды и именно его нужно указывать первым параметром в manage.py.В этом файле должен быть объявлен класс Command в общем случае наследник от BaseCommand. В недрах которых скрывается логика обработки параметров командной строки ОС посредством стандартного модуля optparse.
Основным аспектом создания команды является реализация метода handle, который принимает позиционные параметры и именованные(опции). Он выглядит так:
def handle(self, *args, **options):
#...
Существуют и другие базовые классы, но чуть более специализированные и в которых необходимо реализовывать другие методы, которые для упрощения, возвращают некую строку которая будет выведена в стандартный поток.
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. В производных командах можно добавлять опции, добавляя атрибут 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.
Также команда может иметь атрибут requires_model_validation, если он установлен в True, то перед запуском команды будет проведена валидация моделей.
Ещё два полезных атрибута, это help и args, в которых можно описать вспомогательную информацию и подсказку по использованию текущей команды.
Для того чтобы добавить новую команду в своем приложении необходимо создать в корне директории приложения директорию management, в ней в свою очередь директорию commands, а там уже модуль команды с уникальным именем. Джанга проверяет наличие дополнительных команд для всех установленных приложений, т.е. указанных в INSTALLED_APPS.
Для примера покажу реализацию команды которая печатает имена моделей в заданном приложении и если установлена опция --count то и количество объектов данной модели в базе данных:
#myapp.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='Add object count information' ),
)
help = 'Prints model names for given application and optional object count.'
args = '[appname ...]'
requires_model_validation = True
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 )
Ну и вот такой результат получился у меня:
> ./manage.py modelsinfo auth --count
[Message] - 0 objects
[Group] - 0 objects
[User] - 1 objects
[Permission] - 165 objects
Этот пример кончено мало полезен, но зато показывает базовый принцип использования команд в джанго. А вообще довольно часто есть необходимость в таких служебных действиях, которые очень удобно делать и использовать как часть manage.py.
Так что пользуйтесь этим инструментом!:)
Комментарии 12
Интересно, спасибо.
Только текст читается с трудом, ибо:
Спасибо за замечания. Учтем.
Оставлен 06 Февраль 2008 в 00:42 ¶Что я делаю не так?:)
$ python manage.py modelsinfo auth --count Unknown command: 'modelsinfo' Type 'manage.py help' for usage.
поставил deseb, смотрю как там все сделано - вроде все тоже самое, но там работает :)
приложение прописано в INSTALLED_APPS.
Интересная ситуация...
Оставлен 10 Февраль 2008 в 22:53 ¶__init__.pyнигде не забыл?:) У меня так иногда случается...Все на месте 8)
уже час сижу торможу :)
Мдя... Рассказываю животрепещущая историю: я обычно в
Оставлен 10 Февраль 2008 в 23:23 ¶INSTALLED_APPSи вообще везде где можно, пишу полный import path, т.е. всегда в начало добавляю имя проекта, напримерmyproject.myapp.modelsи т.п. Ну так вот, когда идет загрузка доступных команд, пакетmyprojectне доступен и надо писатьmyapp.models. Это я условно. Т.е. родительский для проекта каталог не находится вPYTHON_PATH. Может у тебя этот случай?точно, именно этот случай, спасибо, заработало :)
Ура! Отлично:)
Оставлен 10 Февраль 2008 в 23:31 ¶Спасибо оказалось очень полезно (а то я уже начал делать велосипед для однго проекта в котором для некоторых приложений нужно запускать задачи в cron`e)
Немного оффтопа... У вас очень симпатичная подсветка питоновского кода. Под чем вы кодите? Не поделитесь цветовой схемой?
Ничего особенного, для подсветки в блоге использую pygments c таким стилем.
Кстати, в текстовых редакторах предпочитаю классическую схему - светлый фон, темный текст.
Оставлен 29 Февраль 2008 в 00:13 ¶так как в результате решать вопрос с тем что родительский проект не в python_path? если написать в settings.py
INSTALLEDAPPS = ( .... 'appname', )
то работает manage.py но не работает админка "ImportError: No module named app_name"
Оставлен 04 Август 2008 в 14:01 ¶Пингбеки 4
[...]Оформить его в виде команды Джанго. Тогда в скрипте сразу будет нужная среда.[...]
[...]ако для вящего удобства тех, кто захочет Cicero использовать, я оформил скрипт обновления в виде джанговской команды, вызов которой в кроне выглядит примерно так:[...]
[...]ÐŸÐ¾Ñ Ñ‚ “Командовать парадом будет Django!” | Интернет нового века | webnewage.org[...]
[...]Александр Кошелев полно и подробно пишет про описание пользовательских команд для manage.py. Одна из приятных фичек в Джанге последнего времени.[...]
Оставьте комментарий