Перейти к содержанию

Конфигурация FastAPI из окружения

Оглавление серии

Название «Окно» пока записано непосредственно при создании FastAPI. Адрес базы появится в следующем блоке курса, а учебный вход потребует отдельных переключателей. Эти значения отличаются от кабинетов и броней: они описывают окружение процесса. Вынесем их в Settings, чтобы перенос проекта между собственными учебными средами не требовал редактировать обработчики и предметные модели.

Переменная окружения — строковое значение, которое процесс получает при запуске. Типизированная настройка превращает эту строку в нужный объект и проверяет форму. Например, demo_enabled должен стать логическим значением, а не использоваться как строка, где даже текст false является непустым. Мы выбираем pydantic-settings второго семейства; в Pydantic 2 управление настройками относится к отдельному пакету. Его наличие указано в requirements.txt, но установка в ходе подготовки не выполнялась.

Префикс и область значения

SettingsConfigDict задаёт env_prefix="OKNO_". Поэтому app_name соответствует OKNO_APP_NAME, а database_url — OKNO_DATABASE_URL. Префикс уменьшает вероятность случайного совпадения с параметрами другой программы. В учебном проекте он также помогает читателю увидеть, кому принадлежит переменная. Само название поля остаётся коротким Python-именем: предметные функции не должны знать о написании системной переменной.

Значение app_name используется при создании приложения. Изменение настройки повлияет на название API и его документации, но не переименует кабинет в каталоге. Это существенная граница: конфигурация процесса и предметное состояние не являются взаимозаменяемыми. Когда пользователь отменит бронь, это будет запись в базе, а не переменная окружения. И наоборот, смена адреса базы не должна выполняться через JSON бронирования, отправленный обычным клиентом.

database_url по умолчанию указывает на локальную базу okno_lab без пароля в строке. Это пример адреса собственной учебной среды, а не утверждение, что такая база уже существует. Отдельный пользователь PostgreSQL, права доступа и способ подачи учётных данных остаются задачей читателя при самостоятельной настройке. Мы не добавляем реальные токены и пароли ни в статью, ни в архив. Файл env.example показывает имена и выключенный режим входа.

Полные файлы шага

Показанные файлы полностью совпадают с выбранным состоянием архива. Остальные согласованные файлы находятся в examples/fastapi-booking-lab/lesson-04/.

settings.py

from functools import lru_cache

from pydantic import SecretStr
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="OKNO_")
    app_name: str = "Окно"
    database_url: str = "postgresql+psycopg://localhost/okno_lab"
    demo_enabled: bool = False
    demo_first_token: SecretStr | None = None
    demo_second_token: SecretStr | None = None


@lru_cache
def get_settings() -> Settings:
    return Settings()

env.example

OKNO_APP_NAME=Окно
OKNO_DATABASE_URL=postgresql+psycopg://localhost/okno_lab
OKNO_DEMO_ENABLED=false
# Set your own two different values later; do not commit actual tokens.
OKNO_DEMO_FIRST_TOKEN=
OKNO_DEMO_SECOND_TOKEN=

Ожидаемый результат

Это ожидаемый договор для самостоятельного исследования; пример не выполнялся.

OKNO_APP_NAME задаёт название API.
При отсутствии OKNO_DEMO_ENABLED учебный вход выключен.
Файл env.example сам по себе не загружается.

Кэширование настроек

Функция get_settings помечена lru_cache. В простом случае она создаёт Settings один раз и возвращает тот же объект при последующих вызовах. Это устраняет повторное чтение окружения в каждой зависимости. Обратите внимание, что такое кэширование не делает переменные динамическими. Если процесс уже получил объект, изменение значения вне процесса не обязано изменить объект внутри него. Для новой конфигурации предусмотрен новый запуск собственной учебной программы.

Настройки создаются до обработки запросов. Ошибка их формы должна обнаружиться на границе запуска, а не после нескольких успешных ответов. Например, произвольная строка вместо корректного логического переключателя не является пользовательской ошибкой бронирования. Возврат 422 отдельному посетителю здесь был бы неверным объяснением: посетитель не задавал окружение сервера. Разделение уровней помогает не перепутать ошибки эксплуатации с ошибками публичного входного DTO.

Поля demo_first_token и demo_second_token объявлены как SecretStr или None. SecretStr уменьшает случайное раскрытие при обычном печатном представлении объекта, но не является шифрованным хранилищем и не заменяет защиту файла окружения. Для сравнения токена позже придётся извлечь значение явно. Это полезная учебная граница: тип помогает внимательному обращению, однако автор кода всё равно может раскрыть строку, если отправит её клиенту.

Учебный режим выключен

По умолчанию demo_enabled равен False, а оба токена отсутствуют. Мы не хотим, чтобы скачанный архив автоматически изображал готовый публичный механизм входа. Учебный провайдер в дальнейшей главе нужен для двух вымышленных владельцев и объяснения проверки прав. Это отдельный локальный режим, который читатель включает осознанно и только для собственной среды. Реальное приложение потребует другого провайдера удостоверенной личности, управления сессиями и правил эксплуатации.

В main.py изменена только строка создания приложения: title берётся из get_settings().app_name. Остальные маршруты сохраняют прежний договор. Поэтому ожидаемый запрос к кабинету 1 возвращает те же данные. Изменение параметра названия можно увидеть в будущей документации API, но оно не должно менять идентификаторы или форму броней. Если клиент неожиданно получает другой JSON после смены app_name, причина находится в реализации, а не в назначении настройки.

Файл env.example не читается автоматически этим кодом. Здесь не задан env_file: мы показываем перечень значений, а способ безопасной подачи окружения выбирает читатель. Это снимает частую путаницу между примером конфигурации и действительным источником настроек. Простое существование файла с произвольным именем не означает, что библиотека обязана открыть его. При переносе снимка можно проследить весь механизм: системная переменная, поле Settings, кэшированный объект, использование в приложении.

Для самостоятельного разбора сравните пустой токен и отсутствие токена. Оба состояния ещё не дают возможность входа при выключенном demo_enabled. Затем мысленно подайте неверное логическое значение и определите, на каком шаге возникнет отказ. Полученный результат — управляемая конфигурационная граница без настоящих секретов в исходниках. Следующий урок даст похожую границу для источника данных: обработчик будет зависеть от договорённой операции, а не от конкретного словаря.

Окружение не является ответом пользователю

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

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

Документация и исходники

Механизмы главы сверены с первичными источниками: документация 1, документация 2. Полный архив «Окна» содержит все шестнадцать состояний. Пакеты, приложение и миграции не запускались.