Собственный модуль settings
Преамбула
Этот модуль родился в результате переосмысления (или недопонимания) мной вот этого пространного документа: Splitting up the settings file, размещённого на официальном сайте Django.
Постановка задачи
При старте веб-приложения на Django (как посредством запуска отладочного сервера, так и в качестве WSGI-приложения) фреймворк первым делом выполняет модуль, задающий начальные настройки проекта. Источник кода задаётся переменной окружения DJANGO_SETTINGS_MODULE. При создании Django-проекта стандартным способом, например:
$ django-admin startproject myproject
создаётся и модуль настроек. Это файл «myproject/myproject/settings.py». Изменяя и дополняя его, программист настраивает проект, добавляет в него собственные и сторонние компоненты и т. д.
В простых проектах, разрабатываемых одним бэкенд-программистом, бывает вполне разумно ограничиться таким модулем настроек. Однако по мере роста проекта возникают следующие проблемы:
- Настройки проекта для развёртывания в боевой или тестовой среде очень отличаются от настроек, с которыми проект запускают разработчики. Например, в бою приложение требует «большой» SQL-сервер (PostgreSQL или MySQL) и дополнительную базу данных «ключ-значение» для хранения кэшируемых данных (memcached или Redis), в то время как разработчик на своём компютере привык обходиться SQLite. Зато настройки разработчика включают дополнительные модули для отладки проекта (например, debug_toolbar), которые не должны попаcть в production.
- Переменные, устанавливающие режим отладки в Django (DEBUG, TEMPLATE_DEBUG и др.), а также аналогичные переменные для сторонних компонентов, должны быть включены при разработке и выключены в продакшне. За этим довольно муторно следить при коммитах.
- В модуле 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-репозитории):
- Создайте в каталоге главного приложения подкаталог «settings». Путь к нему будет выглядеть так: «myproject/myproject/settings/».
- Переместите ваш старый «settings.py» в созданный в п. 1 каталог «myproject/myproject/settings/» и переименуйте его в «common.py». Будем ссылаться на этот файл в дальнейшем как на «общие настройки».
Если вы используете в проекте относительные пути от файла настроек, увеличьте глубину вложенности на один каталог. Например, код типа:
BASE_DIR = dirname(dirname(abspath(__file__)))
BASE_DIR = dirname(dirname(dirname(abspath(__file__))))
- Создайте файл «myproject/myproject/settings/local.py». В него сразу добавьте следующий код:
from myproject.settings.common import *
Например, если вы − разработчик, и хотите использовать замечательный инструмент Django Debug Toolbar, вы можете добавить следующую строку:
INSTALLED_APPS += ('debug_toolbar', )
- Создайте файл «myproject/myproject/settings/__init__.py» и внесите в него следующий код:
try: from myproject.settings.local import * except ImportError: from myproject.settings.common import *
Если без файла локальной конфигурации запуск проекта не имеет смысла, можно не пытаться обойтись глобальными настройками, а выбросить исключение:
try: from myproject.settings.local import * except ImportError: raise Exception('Please create local.py file')
- Добавьте файл «myproject/myproject/settings/local.py» в исключения git. Это последний, но от этого не менее важный шаг.
Готово! Мы разбили файл настроек на общую, прототипную часть (common.py), и локальную часть, наследущую настройки от общей (local.py). Теперь всё дело за правильной декомпозицией настроек.