Оптимизируйте настройки Django с помощью подсказок типов: руководство по Pydantic

Опубликовано: 2022-07-22

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

Как описано в PEP 484, подсказки типов поддерживают статический анализ, но эти же аннотации также доступны во время выполнения. Сторонние пакеты, такие как pydantic, предлагают проверку типов во время выполнения, которая использует эти дополнительные метаданные. Pydantic использует подсказки типов Python, чтобы помочь управлять метаданными настроек и выполнять проверку данных во время выполнения.

В этом руководстве по pydantic будут показаны далеко идущие положительные эффекты использования управления настройками pydantic с Django.

Наша конфигурация соответствует рекомендациям, описанным на веб-сайте приложения Twelve-Factor:

  1. Определите непостоянные и секретные конфигурации как переменные среды.
  2. В средах разработки определите переменные среды в файле .env и добавьте .env в .gitignore .
  3. Используйте механизмы облачного провайдера для определения (секретных) переменных среды для QA, промежуточной и рабочей сред.
  4. Используйте один файл settings.py , который настраивается из переменных среды.
  5. Используйте 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. Они становятся доступными для развернутого приложения в виде переменных среды:

Скриншот веб-интерфейса Config Vars. На левой боковой панели есть описание: «Переменные конфигурации изменяют поведение вашего приложения. Помимо создания собственных, некоторые надстройки поставляются со своими собственными». В основном разделе есть две строки, каждая с двумя текстовыми полями, значком карандаша и значком X. Текстовые поля содержат те же данные, что и столбцы «Имя переменной» и «Производство» в предыдущей таблице. В правом верхнем углу есть кнопка с надписью «Скрыть переменные конфигурации».

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

Настройка 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 , наследуемый от класса BaseSettings BaseSettings .
  • Определяет 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 поддерживает подсказки типов уже несколько лет, мы обобщили вводный текст, удалив эту ссылку на версию.