Оптимизируйте настройки Django с помощью подсказок типов: руководство по Pydantic
Опубликовано: 2022-07-22Раньше проекты Django меня расстраивали, потому что мне не хватало надежного и масштабируемого способа добавления новых сред. Объединив подсказки типов pydantic и Python, я создал мощную основу, в которой нуждался.
Как описано в PEP 484, подсказки типов поддерживают статический анализ, но эти же аннотации также доступны во время выполнения. Сторонние пакеты, такие как pydantic, предлагают проверку типов во время выполнения, которая использует эти дополнительные метаданные. Pydantic использует подсказки типов Python, чтобы помочь управлять метаданными настроек и выполнять проверку данных во время выполнения.
В этом руководстве по pydantic будут показаны далеко идущие положительные эффекты использования управления настройками pydantic с Django.
Наша конфигурация соответствует рекомендациям, описанным на веб-сайте приложения Twelve-Factor:
- Определите непостоянные и секретные конфигурации как переменные среды.
- В средах разработки определите переменные среды в файле
.env
и добавьте.env
в.gitignore
. - Используйте механизмы облачного провайдера для определения (секретных) переменных среды для QA, промежуточной и рабочей сред.
- Используйте один файл
settings.py
, который настраивается из переменных среды. - Используйте pydantic для чтения, проверки, проверки и приведения переменных среды к переменным Python, которые определяют конфигурации Django.
Кроме того, некоторые разработчики создают несколько файлов настроек, например settings_dev.py
и settings_prod.py
. К сожалению, этот подход плохо масштабируется. Это приводит к дублированию кода, путанице, трудно находимым ошибкам и увеличению усилий по обслуживанию.
Используя вышеупомянутые передовые методы, добавление любого количества сред является простым, четко определенным и защищенным от ошибок. Хотя мы могли бы изучить более сложную конфигурацию среды, для ясности мы сосредоточимся на двух: разработке и производстве.
Как это выглядит на практике?
Управление настройками Pydantic и переменные среды
Теперь мы сосредоточимся на примере как в разработке, так и в производстве. Мы покажем, как каждая среда настраивает свои параметры по-разному и как pydantic их поддерживает.
Для нашего примера приложения требуется база данных, поддерживаемая Django, поэтому нам нужно сохранить строку подключения к базе данных. Мы перемещаем информацию о конфигурации подключения к базе данных в переменную среды DATABASE_URL
с помощью пакета Python dj-database-url. Обратите внимание, что эта переменная имеет тип str
и имеет следующий формат:
postgres://{user}:{password}@{hostname}:{port}/{database-name} mysql://{user}:{password}@{hostname}:{port}/{database-name} oracle://{user}:{password}@{hostname}:{port}/{database-name} sqlite:///PATH
В нашей среде разработки мы можем использовать экземпляр PostgreSQL, содержащийся в Docker, для простоты использования, а в нашей производственной среде мы укажем на подготовленную службу базы данных.
Еще одна переменная, которую мы хотим определить, — это логическое значение DEBUG
. Флаг DEBUG в Django никогда не должен быть включен в производственном развертывании. Он предназначен для получения дополнительной обратной связи во время разработки. Например, в режиме отладки Django будет отображать подробные страницы ошибок при возникновении исключения.
Различные значения для разработки и производства можно определить следующим образом:
Имя переменной | Разработка | Производство |
---|---|---|
DATABASE_URL | postgres://postgres:mypw@localhost:5432/mydb | postgres://foo1:foo2@foo3:5432/foo4 |
DEBUG | True | False |
Мы используем модуль управления настройками pydantic для управления этими различными наборами значений переменных среды в зависимости от среды.
Подготовительные шаги
Чтобы применить это на практике, мы начинаем настраивать нашу среду разработки, создавая наш единственный файл .env
со следующим содержимым:
DATABASE_URL=postgres://postgres:mypw@localhost:5432/mydb DEBUG=True
Затем мы добавляем файл .env
в файл .gitignore
проекта. Файл .gitignore
позволяет избежать сохранения потенциально конфиденциальной информации в системе управления версиями.
В то время как этот подход хорошо работает в нашей среде разработки, в спецификации нашей производственной среды используется другой механизм. Наши передовые методы требуют, чтобы переменные производственной среды использовали секреты среды. Например, в Heroku эти секреты называются Config Vars и настраиваются через панель инструментов Heroku. Они становятся доступными для развернутого приложения в виде переменных среды:
После этого нам нужно настроить конфигурацию приложения, чтобы автоматически считывать эти значения из любой среды.
Настройка Django в settings.py
Давайте начнем с нового проекта Django, чтобы обеспечить необходимую структуру для нашего примера. Мы создаем новый проект Django с помощью следующей команды терминала:
$ django-admin startproject mysite
Теперь у нас есть базовая структура проекта mysite
. Файловая структура проекта выглядит следующим образом:
mysite/ manage.py mysite/ __init__.py settings.py urls.py asgi.py wsgi.py
Файл settings.py
содержит шаблонный код, который позволяет нам управлять конфигурацией приложения. Он имеет много предопределенных настроек по умолчанию, которые мы должны настроить в соответствующей среде.
Чтобы управлять этими настройками приложения с помощью переменных среды и pydantic, добавьте этот код в начало файла settings.py
:
import os from pathlib import Path from pydantic import ( BaseSettings, PostgresDsn, EmailStr, HttpUrl, ) import dj_database_url # Build paths inside the project like this: BASE_DIR / 'subdir'. BASE_DIR = Path(__file__).resolve().parent.parent class SettingsFromEnvironment(BaseSettings): """Defines environment variables with their types and optional defaults""" DATABASE_URL: PostgresDsn DEBUG: bool = False class Config: """Defines configuration for pydantic environment loading""" env_file = str(BASE_DIR / ".env") case_sensitive = True config = SettingsFromEnvironment() os.environ["DATABASE_URL"] = config.DATABASE_URL DATABASES = { "default": dj_database_url.config(conn_max_age=600, ssl_require=True) } DEBUG = config.DEBUG
Этот код делает следующее:
- Определяет класс
SettingsFromEnvironment
, наследуемый от класса BaseSettingsBaseSettings
. - Определяет
DATABASE_URL
иDEBUG
, устанавливая их тип и необязательное значение по умолчанию, используя подсказки типа Python. - Определяет класс
Config
, указывающий pydantic искать переменные в файле.env
, если они отсутствуют в переменных среды системы. - Добавляет класс
Config
в объектconfig
; нужные переменные становятся доступными какconfig.DATABASE_URL
иconfig.DEBUG
. - Определяет обычные переменные Django
DATABASES
иDEBUG
из этих элементовconfig
.
Один и тот же код работает во всех средах, и pydantic заботится о следующем:
- Он ищет переменные среды
DATABASE_URL
иDEBUG
.- Если они определены как переменные среды, как в рабочей среде, они будут использоваться.
- В противном случае он извлекает эти значения из файла
.env
. - Если он не найдет значение, он сделает следующее:
- Для
DATABASE_URL
выдает ошибку. - Для
DEBUG
присваивается значение по умолчаниюFalse
.
- Для
- Если он находит переменную среды, он проверяет типы полей и выдает ошибку, если какой-либо из них неверен:
- Для
DATABASE_URL
он проверяет, что его тип поля является URL-адресом в стилеPostgresDsn
. - Для
DEBUG
он проверяет, что его тип поля является допустимым, нестрогим пидантичным логическим значением.
- Для
Обратите внимание на явную настройку переменной среды операционной системы из значения конфигурации для DATABASE_URL
. Установка os.environ["DATABASE_URL"] = config.DATABASE_URL
может показаться излишним, поскольку DATABASE_URL
уже определен как переменная внешней среды. Однако это позволяет pydantic анализировать, проверять и проверять эту переменную. Если переменная среды DATABASE_URL
отсутствует или имеет неправильный формат, pydantic выдаст четкое сообщение об ошибке. Эти проверки ошибок бесценны, когда приложение переходит от разработки к последующим средам.
Если переменная не определена, либо будет присвоено значение по умолчанию, либо появится сообщение об ошибке. Во всех сгенерированных подсказках также указывается желаемый тип переменной. Дополнительным преимуществом этих проверок является то, что новым членам команды и инженерам DevOps легче определить, какие переменные необходимо определить. Это позволяет избежать сложно обнаруживаемых проблем, возникающих, когда приложение запускается без определенных переменных.
Вот и все. Приложение теперь имеет удобную в сопровождении реализацию управления настройками, использующую единую версию settings.py
. Прелесть этого подхода в том, что он позволяет нам указать правильные переменные среды в файле .env
или любым другим желаемым способом, доступным через нашу среду хостинга.
Масштабируемый путь
Я использую управление настройками Django с помощью pydantic во время выполнения во всех своих проектах Django. Я обнаружил, что это приводит к меньшим, более удобным для сопровождения кодовым базам. Он также обеспечивает хорошо структурированный, самодокументируемый и масштабируемый подход к добавлению новых сред.
Следующая статья в этой серии представляет собой пошаговое руководство по созданию приложения Django с нуля с управлением настройками pydantic и его развертыванию в Heroku.
Блог Toptal Engineering выражает благодарность Стивену Дэвидсону за рассмотрение примеров кода, представленных в этой статье.
В более ранней версии этой статьи особое внимание уделялось конкретному выпуску Python. Поскольку Python поддерживает подсказки типов уже несколько лет, мы обобщили вводный текст, удалив эту ссылку на версию.