Настройка Django Settings: лучшие практики

Spread the love

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

Table of contents:

Управление настройками Django: проблемы

Разные окружения. Обычно в разработке используется несколько сред: локальная, dev, ci, qa, staging (промежуточная) , производственная и т. д. Каждая среда может иметь свои собственные конкретные настройки (например: DEBUG = True, более подробное ведение журнала, дополнительные приложения, некоторые фальшивые (mocked) данные и т. д.). Поэтому нам нужен подход, который позволяет сохранять все эти настройки Django для каждой среды отдельно.

Чувствительные данные. У нас есть SECRET_KEY в каждом проекте Django. Кроме того, могут быть пароли и токены БД для сторонних API, таких как Amazon или Twitter. Все данные желательно сохранять в системах VCS.

Обмен настройками между членами команды. Так же обычно нужен общий подход для устранения человеческих ошибок при работе с настройками. Например, разработчик может добавить стороннее приложение или некоторую интеграцию с API в одну конфигурацию и не добавляет определенные настройки в другие. В больших (или даже средних) проектах это может вызвать реальные проблемы.

Настройки Django — это обычный код на Python. Это проклятие и благословение одновременно. Это дает большую гибкость, но также может быть проблемой — вместо пар ключ-значение у settings.py может быть очень сложная логика.

Настройка конфигурации: разные подходы

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

settings_local.py

Это самый старый метод. Я использовал его, когда впервые настраивал проект Django на производственном сервере. Я видел, как многие люди использовали это в свое время, и я все еще вижу это сейчас.

Основная идея этого метода заключается в расширении всех специфических для среды настроек в файле settings_local.py, который игнорируется системой VCS. Вот пример:

settings.py file:

ALLOWED_HOSTS = ['example.com']
DEBUG = False
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'production_db',
        'USER': 'user',
        'PASSWORD': 'password',
        'HOST': 'db.example.com',
        'PORT': '5432',
        'OPTIONS': {
            'sslmode': 'require'
        }
    }
}

...

from .settings_local import *

settings_local.py file:

ALLOWED_HOSTS = ['localhost']
DEBUG = True
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'local_db',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

Плюсы:

  • Секреты коды не попадают в VCS.

Недостатки:

  • settings_local.py отсутствует в VCS, поэтому вы можете потерять некоторые настройки среды Django.
  • Файл настроек Django представляет собой код Python, поэтому settings_local.py может иметь некоторую неочевидную логику.
  • У вас должен быть файл settings_local.example (в VCS), чтобы поделиться настройками по умолчанию со всеми разработчиками.

Отдельный файл настроек для каждой среды

Это расширение предыдущего подхода. Оно позволяет сохранять все конфигурации в VCS и делиться настройками по умолчанию между разработчиками.

В этом случае вы создаете каталог (пакет) settings со следующей структурой:

settings/
   ├── __init__.py
   ├── base.py
   ├── ci.py
   ├── local.py
   ├── staging.py
   ├── production.py
   └── qa.py

settings/local.py:

from .base import *


ALLOWED_HOSTS = ['localhost']
DEBUG = True
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'local_db',
        'HOST': '127.0.0.1',
        'PORT': '5432',
    }
}

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

python manage.py runserver --settings=settings.local

Плюсы:

  • Все среды находятся в VCS.
  • Легко обмениваться настройками между разработчиками.

Недостатки:

  • Нужно найти способ обработки секретных паролей и токенов.
  • «Наследование» настроек может быть сложно отслеживать и контролировать.

Переменные окружения

Чтобы решить проблему с конфиденциальными данными, вы можете использовать переменные среды.

import os


SECRET_KEY = os.environ['SECRET_KEY']
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ['DATABASE_NAME'],
        'HOST': os.environ['DATABASE_HOST'],
        'PORT': int(os.environ['DATABASE_PORT']),
    }
}

Это самый простой пример использования Python os.environ, и у него есть несколько проблем:

  1. Необходимо обработать исключения KeyError.
  2. Необходимо конвертировать типы вручную (см. Использование DATABASE_PORT).

Чтобы исправить KeyError, вы можете написать свою собственную оболочку. Например:

import os

from django.core.exceptions import ImproperlyConfigured


def get_env_value(env_variable):
    try:
      	return os.environ[env_variable]
    except KeyError:
        error_msg = 'Set the {} environment variable'.format(var_name)
        raise ImproperlyConfigured(error_msg)


SECRET_KEY = get_env_value('SECRET_KEY')
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': get_env_value('DATABASE_NAME'),
        'HOST': get_env_value('DATABASE_HOST'),
        'PORT': int(get_env_value('DATABASE_PORT')),
    }
}

Кроме того, вы можете установить значения по умолчанию для этой оболочки и добавить преобразование типов. Но на самом деле нет необходимости писать эту обертку, потому что вы можете использовать стороннюю библиотеку (мы поговорим об этом позже).

Плюсы:

  • Конфигурация отделена от кода.
  • Паритет среды — у вас одинаковый код для всех сред.
  • Нет наследования в настройках, и более чистый и согласованный код.
  • Существует теоретическое обоснование использования переменных среды — 12 факторов.

Недостатки:

  • Вы должны управлять совместным использованием конфигурации по умолчанию между разработчиками.

12 факторов

12 Факторов — это набор рекомендаций о том, как создавать распределенные веб-приложения, которые будут легко развертывать и масштабировать в облаке. Он был создан Heroku, известным провайдером облачного хостинга.

Как следует из названия, коллекция состоит из двенадцати частей:

  1. Базы кода (Codebase)
  2. Зависимости (Dependencies)
  3. Конфигурация (Config)
  4. Вспомогательные сервисы (Backing services)
  5. Сборка, релиз, запуск (Build, release, run)
  6. Процессы (Processes)
  7. Привязка к портам (Port binding)
  8. Параллелизм (Concurrency)
  9. Disposability
  10. Dev/prod паритет (Dev/prod parity)
  11. Логгирование (Logs)
  12. Процессы администратора (Admin processes)

Каждый пункт описывает рекомендуемый способ реализации определенного аспекта проекта. Некоторые из этих пунктов освещаются такими инструментами, как Django, Python, pip. Некоторые покрыты шаблонами проектирования или настройкой инфраструктуры. В контексте этой статьи нас интересует одна часть: Конфигурация.

Его главное правило — хранить конфигурацию в среде. Следование этой рекомендации даст нам строгое отделение конфига от кода.

Вы можете прочитать больше на 12factor.net.

django-environ

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

Теперь пришло время поговорить о наборе инструментов.

Написание кода с использованием os.environ иногда может быть сложным и потребовать дополнительных усилий для обработки ошибок. Вместо этого лучше использовать django-environment.

Технически это слияние следующих проектов:

Это приложение предоставляет хорошо работающий API для чтения значений из переменных среды или текстовых файлов, небольшого преобразования типов и т. д. Давайте рассмотрим несколько примеров.

файл settings.py перед:

import os


SITE_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.realpath(__file__))))

DEBUG = True
TEMPLATE_DEBUG = DEBUG

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'production_db',
        'USER': 'user',
        'PASSWORD': 'password',
        'HOST': 'db.example.com',
        'PORT': '5432',
        'OPTIONS': {
            'sslmode': 'require'
        }
    }
}

MEDIA_ROOT = os.path.join(SITE_ROOT, 'assets')
MEDIA_URL = 'media/'
STATIC_ROOT = os.path.join(SITE_ROOT, 'static')
STATIC_URL = 'static/'

SECRET_KEY = 'Some-Autogenerated-Secret-Key'

CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': '127.0.0.1:6379/1',
    }
}

файл settings.py после:

import environ


root = environ.Path(__file__) - 3  # get root of the project
env = environ.Env()
environ.Env.read_env()  # reading .env file

SITE_ROOT = root()

DEBUG = env.bool('DEBUG', default=False)
TEMPLATE_DEBUG = DEBUG

DATABASES = {'default': env.db('DATABASE_URL')}

public_root = root.path('public/')
MEDIA_ROOT = public_root('media')
MEDIA_URL = env.str('MEDIA_URL', default='media/')
STATIC_ROOT = public_root('static')
STATIC_URL = env.str('STATIC_URL', default='static/')

SECRET_KEY = env.str('SECRET_KEY')

CACHES = {'default': env.cache('REDIS_CACHE_URL')}

Файл .env:

DEBUG = True
DATABASE_URL = postgres://user:password@db.example.com:5432/production_db?sslmode=require
REDIS_CACHE_URL = redis://user:password@cache.example.com:6379/1
SECRET_KEY = Some-Autogenerated-Secret-Key

Структура настроек

Вместо разделения настроек по средам вы можете разделить их по источникам: Django, сторонним приложениям (Celery, DRF и т. д.) И пользовательским настройкам.

Структура файла:

project/
├── apps/
├── settings/
│   ├── __init__.py
│   ├── djano.py
│   ├── project.py
│   └── third_party.py
└── manage.py

файл __init__.py:

from .django import *       # All Django related settings
from .third_party import *  # Celery, Django REST Framework & other 3rd parties
from .project import *      # You custom settings

Каждый модуль может быть выполнен в виде пакета, и вы можете разбить его более детально:

project/
├── apps/
├── settings/
│   ├── project
│   │   ├── __init__.py
│   │   ├── custom_module_foo.py
│   │   ├── custom_module_bar.py
│   │   └── custom_module_xyz.py
│   ├── third_party
│   │   ├── __init__.py
│   │   ├── celery.py
│   │   ├── email.py
│   │   └── rest_framework.py
│   ├── __init__.py
│   └── djano.py
└── manage.py

Соглашения об именах

Именование переменных является одной из самых сложных частей разработки. Так называется наименование настроек. Мы не можем ссылаться на Django или сторонние приложения, но мы можем следовать этим простым правилам для наших пользовательских (проектных) настроек:

  • Дайте значимые имена вашим настройкам.
  • Всегда используйте префикс с именем проекта для пользовательских настроек (проекта).
  • Пишите описания ваших настроек в комментариях.

Плохой пример:

API_SYNC_CRONTAB = env.str('API_SYNC_CRONTAB')

Хороший пример:

# Run job for getting new tweets.
# Accept string in crontab format. By default: every 30 minutes.
MYAWESOMEPROJECT_TWEETS_API_SYNC_CRONTAB = env.str(
    'MYAWESOMEPROJECT_TWEETS_API_SYNC_CRONTAB', default='30 * * * *'
)

Измените MYAWESOMEPROJECT на ваше реальное название проекта.

Настройки Django: лучшие практики

  • Сохраняйте настройки в переменных среды.
  • Запишите значения по умолчанию для производственной конфигурации (исключая секретные ключи и токены).
  • Не задавайте жесткие настройки и не помещайте их в VCS.
  • Разделите настройки на группы: Django, third-party, проект.
  • Следуйте правилам именования для пользовательских (проектных) настроек.

Заключение

Файл настроек — это небольшая, но очень важная часть любого проекта Django. Если вы сделаете это неправильно, у вас будет много проблем на всех этапах разработки. Но если вы все сделаете правильно, это станет хорошей основой для вашего проекта, которая позволит ему расти и масштабироваться в будущем.

Используя подход с использованием переменных сред, вы можете легко перейти от монолитной архитектуры к микросервису, обернуть свой проект в контейнеры Docker и развернуть его на любой платформе хостинга VPS или Cloud, такой как: Amazon, Google Cloud или ваш собственный кластер Kubernetes.

Оригинальная статья: Alexander Ryabtsev Configuring Django Settings: Best Practices

Была ли вам полезна эта статья?
[21 / 4.8]

Spread the love
Подписаться
Уведомление о
guest
2 Комментарий
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Vadim
Vadim
5 лет назад

Использую смесь 1+2 способов. Т.е. основные настройки разбиты по файлам, плюс существует __local_***.py .
Просто не представляю, как можно по другому хранить разные ключи, настройки, да хоть логирование переопределить — не залезая при этом в основной код.

Планирую еще начать использовать что нибудь из https://djangopackages.org/grids/g/configuration/ , что бы часть настроек можно было без разработчика менять.

йцукен
йцукен
5 лет назад

откройте для себя https://github.com/rochacbruno/dynaconf
часть параметров можно положить в .env файл, часть в переменные окружения, а эта библиотека сольёт все в одно.