14 мая 2008 г.

django-manage.py или не единым django-admin.py сыты будем

В разработке своих проектов на Django я придерживаюсь архитектуры, близкой к той, которую описал Мальколм Трединник в своей статье "Django Tip: Developing Without Projects". Странно звучит, да, разработка проектов используя методику разработки без проектов.

Но это только на первый взгляд. В действительности я не использую терминологию Django, в которой проект - это продукт действия django-admin.py startproject, порождающий дополнительную ветвь в иерархии и приводящий к повсеместному использованию projectname. в питон коде. Для меня проект - это скорее объединение приложений, как reusable (как-то django-tagging, django-mptt и многие многие другие) с одной стороны, так и тех, которые разрабатываются исключительно для текущего веб-сайта, под одной общей крышей (settings.py, urls.py).

В идеале архитектура любого моего проекта выглядит как:

project/
    application/
    another_application/
    locale/
    templates/
    settings.py
    urls.py
Т.е. по-большому счету ничего лишнего. Управление и деплоймент проекта ведется при помощи универсальной утилиты django-admin.py, которой передаются переменные DJANGO_SETTINGS_MODULE (settings) и PYTHONPATH (абсолютный путь к project/) (опять же в идеале создаются алиасы для каждого из проекта вида django-project и это в дальнейшем облегчает их использование в коммандной строке).

И казалось бы жизнь прекрасна и чудесна. Но так было до сегодняшнего дня, а сегодня я решил установить django-compress - приложение для сжатие CSS и JavaScript файлов. И одним из условий его использования было выполнение комманды

$ ./manage.py synccompress
Я, конечно, ничуть не смутился и попытался было:
$ django-project synccompress
но эта попытка закончилась
Unknown command: 'synccompress'
Type 'django-admin.py help' for usage.

Причина оказалось простой и, наверное, вполне логичной. django-admin.py вызывает django.core.management.execute_from_command_line, manage.py же, лежащий в директории проекта, после его создания (startproject), вызывает django.core.management.execute_manager. Вся же разница состоит в том, какая утилита управления (ManagementUtility) инициализируется и выполняется в этих функциях. И в итоге, ProjectManagementUtility считывает дополнительные команды, которые находятся в директории commands всех приложений, перечисленных в INSTALLED_APPS проекта. Именно это и дает возможность выполнения кастомных команд.

Собственно все это и навело на мысль создания "магичной" (новый тренд в джанго-среде) утилиты django-manage.py. Программный код этого велосипеда:

#!/usr/bin/env python

import os, sys

if __name__ == '__main__':
    try:
        dirname = os.environ['PWD']
    except KeyError:
        import subprocess
        dirname = subprocess.Popen('cd', shell=True, stdout=subprocess.PIPE).communicate()[0].strip()
    sys.path.append(dirname)

    try:
        settings = 'settings'
        try:
            __import__(settings)
        except ImportError, e:
            settings = os.path.basename(dirname) + '.settings'
            __import__(settings)
    except ImportError, e:
        sys.stderr.write("Error: Can't find the file 'settings.py' in the current work directory %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % dirname)
        sys.exit(1)

    os.environ['DJANGO_SETTINGS_MODULE'] = settings
    from django.core import management
    utility = management.ProjectManagementUtility(None, os.path.basename(dirname))
    utility.execute()
Установка выполняется копированием сохраненного кода в PATH и выдачей ему прав на исполнение (последнее только для Unix'ов).

Примеры использования тоже вполне очевидны:

$ cd /path/to/project
$ django-manage.py help
django-manage.py  [options] [args]
Django command line tool, version 0.97-pre-SVN-unknown
Type 'django-manage.py help ' for help on a specific subcommand.
Available subcommands:
  adminindex
  createcachetable
  dbshell
  diffsettings
  dumpdata
  flush
  inspectdb
  loaddata
  reset
  runfcgi
  runserver
  shell
  sql
  sqlall
  sqlclear
  sqlcustom
  sqlflush
  sqlindexes
  sqlinitialdata
  sqlreset
  sqlsequencereset
  startapp
  synccompress
  syncdb
  test
  testserver
  validate

Ну и подитоживая могу лишь порадоваться, что процесс расширения Django под свои нужды проходит так просто и безболезненно.

Важно! Сей метод подходит лишь для архитектуры, описанной мной в начале поста, если вы используете архитектуру Django-проектов используйте для этих же целей manage.py, расположенный в корне директории проекта.

UPD 1 Добавлена проверка текущей рабочей директории для Windows, так как в нем нет ключа PWD в словаре os.environ.

UPD 2 Добавлена поддержка settings, project.settings (стандартной архитектуры проектов Django).

0 комментариев: