Собственный модуль settings

Преамбула


Этот модуль родился в результате переосмысления (или недопонимания) мной вот этого пространного документа: Splitting up the settings file, размещённого на официальном сайте Django.

Постановка задачи


При старте веб-приложения на Django (как посредством запуска отладочного сервера, так и в качестве WSGI-приложения) фреймворк первым делом выполняет модуль, задающий начальные настройки проекта. Источник кода задаётся переменной окружения DJANGO_SETTINGS_MODULE. При создании Django-проекта стандартным способом, например:

$ django-admin startproject myproject

создаётся и модуль настроек. Это файл «myproject/myproject/settings.py». Изменяя и дополняя его, программист настраивает проект, добавляет в него собственные и сторонние компоненты и т. д.

В простых проектах, разрабатываемых одним бэкенд-программистом, бывает вполне разумно ограничиться таким модулем настроек. Однако по мере роста проекта возникают следующие проблемы:

  1. Настройки проекта для развёртывания в боевой или тестовой среде очень отличаются от настроек, с которыми проект запускают разработчики. Например, в бою приложение требует «большой» SQL-сервер (PostgreSQL или MySQL) и дополнительную базу данных «ключ-значение» для хранения кэшируемых данных (memcached или Redis), в то время как разработчик на своём компютере привык обходиться SQLite. Зато настройки разработчика включают дополнительные модули для отладки проекта (например, debug_toolbar), которые не должны попаcть в production.
  2. Переменные, устанавливающие режим отладки в Django (DEBUG, TEMPLATE_DEBUG и др.), а также аналогичные переменные для сторонних компонентов, должны быть включены при разработке и выключены в продакшне. За этим довольно муторно следить при коммитах.
  3. В модуле settings хранятся чувствительные данные (SECRET_KEY, секреты/пароли для аутентификации приложения на различных сервисах и т. д.), которые становится небезопасно хранить в одном репозитории с кодом. Это особенно важно для open source-проектов, а также для крупных проектов, в которых доступ к кодовой базе имеет много разработчиков.


Проблемы 1 и 2 частично решает хранение в репозитории нескольких файлов настроек на разные случаи жизни и выбор нужного через установку переменной DJANGO_SETTINGS_MODULE. Недостаток такого решения в том, что данные в этих файлах почти полностью дублируются. По мере развития проекта разработчик обязан вносить одинаковые изменения в несколько различных файлов конфигурации, что утомительно, приводит к ошибкам и т. д., − короче говоря, противоречит принципу DRY.

Решение


Мой модуль settings обладает максимальной обратной совместимостью с дефолтовым «myproject/myproject/settings.py»: все ссылки на myproject.settings, если они действительно необходимы, остаются в силе. В то же время моё решение позволяет администратору проекта обезопасить приватные данные, а разработчикам − организовать себе наиболее комфортную среду на собственный вкус, независимо от коллег. Дополнительным плюсом является механизм наследования настроек: в локальных настройках можно получить доступ к общим настройкам.

Минус: для хранения локальных настроек нужно придумать какой-то отдельный метод, так как репозиторий использовать не получится. Решение этого вопроса обычно лежит в организационной плоскости: передавать секрет от более опытных коллег менее опытным, публиковать сэмпловый «local.py» в приватном разделе wiki и т. п.

Зато мой метод крайне прост и быстр, не вмешивается в процесс парсинга настроек фреймворком и не создаёт лишних сущностей вроде специальных *.ini/*.conf-файлов с парсерами, классов настроек или модифицирующих настройки функций.

Hands-on


Вот последовательность действий по «апгрейду» классического модуля настроек (подразумевается, что код хранится в git-репозитории):

  1. Создайте в каталоге главного приложения подкаталог «settings». Путь к нему будет выглядеть так: «myproject/myproject/settings/».
  2. Переместите ваш старый «settings.py» в созданный в п. 1 каталог «myproject/myproject/settings/» и переименуйте его в «common.py». Будем ссылаться на этот файл в дальнейшем как на «общие настройки».

    Если вы используете в проекте относительные пути от файла настроек, увеличьте глубину вложенности на один каталог. Например, код типа:

    BASE_DIR = dirname(dirname(abspath(__file__)))
    
    
    должен превратиться в
    BASE_DIR = dirname(dirname(dirname(abspath(__file__))))
    
    
  3. Создайте файл «myproject/myproject/settings/local.py». В него сразу добавьте следующий код:
    from myproject.settings.common import *
    
    
    Вы можете добавить локальные настройки, начиная со следующей строки.

    Например, если вы − разработчик, и хотите использовать замечательный инструмент Django Debug Toolbar, вы можете добавить следующую строку:

    INSTALLED_APPS += ('debug_toolbar', )
    
    
  4. Создайте файл «myproject/myproject/settings/__init__.py» и внесите в него следующий код:
    try:
        from myproject.settings.local import *
    except ImportError:
        from myproject.settings.common import *
    
    
    Этот вариант расчитан на тот случай, когда настройки, содержащиеся в common.py, вполне достаточны для того, чтобы проект запустился. Это вряд ли будет соответствовать истине в большом проекте. Как минимум, из общих настроек следует изъять SECRET_KEY по соображениям безопасности.

    Если без файла локальной конфигурации запуск проекта не имеет смысла, можно не пытаться обойтись глобальными настройками, а выбросить исключение:

    try:
        from myproject.settings.local import *
    except ImportError:
        raise Exception('Please create local.py file')
    
    
  5. Добавьте файл «myproject/myproject/settings/local.py» в исключения git. Это последний, но от этого не менее важный шаг.


Готово! Мы разбили файл настроек на общую, прототипную часть (common.py), и локальную часть, наследущую настройки от общей (local.py). Теперь всё дело за правильной декомпозицией настроек.

© Habrahabr.ru