Настройка параметров Django

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

Скачивайте книги ТОЛЬКО на русском языке у нас в телеграм канале: PythonBooksRU

В статье вы также найдете рекомендации относительно инструментов, лучших практик и архитектурных решений, проверенных временем и успешными проектами. А если после прочтения статьи у вас останутся вопросы, вы всегда можете оставить комментарий ниже.

Основные проблемы управления настройками Django

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

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

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

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

Подходы к настройке параметров Django

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

settings_local.py

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

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

Файл settings.py:

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:

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), чтобы поделиться конфигурациями Django по умолчанию для разработчиков.

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

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

В этом случае существует несколько файлов, из которых проекты на Django получают настройки, и вы создаете пакет 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',
    }
}

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

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

Плюсы:

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

Минусы:

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

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

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

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')),
    }
}

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

Плюсы:

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

Минусы:

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

12 факторов

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

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

  1. Одно приложение — один репозиторий
  2. Явные зависимости
  3. Конфигурация
  4. Сторонние службы
  5. Сборка, релиз и выполнение
  6. Приложение – набор процессов
  7. Настройка портов
  8. Параллелизм
  9. Быстрый запуск и корректное завершение
  10. Соотношение среды разработки и развертывания
  11. Логирование
  12. Задачи администрирования

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

Ее основное правило – хранить конфигурацию в окружении. Следуя этой рекомендации, мы получим строгое отделение конфигурации от кода.

Подробнее вы можете прочитать на сайте 12factor.net.

Примечание редакции: также предлагаем почитать “Плюсы и минусы использования Python для веб-разработки”.

django-environ

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

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

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

Технически это слияние:

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

Файл Django 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',
    }
}

Файл Django 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, сторонние, проект.
  • Соблюдайте соглашения об именовании для пользовательских (проектных) настроек.

Заключение

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

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

Перевод статьи Alex Ryabtsev «Configuring Django Settings: Best Practices».

Оставьте комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *