FastAPI и Pydantic
FastAPI - де-факто стандарт нового Python-бэкенда, и собес по нему проверяет не синтаксис декораторов, а понимаешь ли ты, откуда берутся валидация, сериализация и документация. Ответ один: из аннотаций типов и Pydantic-моделей. Не понял это - будешь дублировать проверки руками и ловить утечки.
Типовые формулировки: «как FastAPI валидирует вход?», «зачем разные модели на вход и выход?», «что не так с блокирующим вызовом в async-эндпоинте».
Одна модель - вход, валидация, OpenAPI
Из Pydantic-модели и аннотаций FastAPI выводит СРАЗУ три вещи: валидацию входа, сериализацию выхода и OpenAPI-схему. Источник правды один - тип, дублировать нечего. Pydantic v2 в lax-режиме (для JSON body) мягко приведёт числовую строку '3' к int 3, но 'хай' даст ValidationError; нужна строгость - strict-режим.
Приятный побочный эффект v2: изменяемый дефолт tags: list = [] в модели безопасен - каждому экземпляру своя копия (в обычной def-функции тот же приём - классический баг с общим списком).
// Depends(get_db) - инъекция зависимости: FastAPI выполнит get_db до эндпоинта и подставит результат; общие зависимости кэшируются в пределах запроса.
- Pydantic-модель
- схема данных: валидация, приведение типов, сериализация
- lax/strict
- режимы Pydantic: приводить типы или требовать точный
response_model отсекает утечки
response_model - не только документация ответа: он ещё и ФИЛЬТРУЕТ. Поля, которых нет в модели ответа, отбрасываются. Вернул из эндпоинта ORM-объект с hashed_password, а в UserOut этого поля нет - оно не утечёт наружу. Поэтому одна модель на вход и выход рано или поздно протекает: пароль внутрь и наружу; разделяй UserCreate и UserOut.
// Второй капкан - блокировка: обычный def-эндпоинт FastAPI уносит в threadpool, чтобы не заморозить loop, а async def идёт прямо в цикле. Значит блокирующий вызов ОПАСНЕЕ именно в async def - автопул на него не распространяется.
class UserOut(BaseModel):
id: int; email: str # hashed_password НЕ описан
@app.get('/me', response_model=UserOut)
def me(): return user # пароль в user есть, наружу не уйдёт- response_model
- схема ответа: валидирует и отсекает лишние поля
- Depends
- инъекция зависимости; кэш в пределах запроса
Как отвечать: «Зачем отдельные Pydantic-модели на вход и выход?»
Чтобы контракт был точным и ничего не утекало. На входе UserCreate принимает пароль, на выходе UserOut его просто не содержит, а раз response_model отсекает поля вне схемы, hashed_password физически не попадёт в ответ, даже если я по ошибке верну весь ORM-объект. Плюс вход и выход почти всегда разной формы: на входе нет id и created_at, на выходе нет сырого пароля. Одна общая модель рано или поздно протечёт или заставит пихать Optional везде. Разделение делает и валидацию, и документацию честными.
Названа главная причина (защита от утечки через фильтрацию response_model), подкреплена разной формой контрактов и показано, что это влияет и на безопасность, и на документацию - системный взгляд.
На чём валят
- −Одна модель на вход и выход рано или поздно протечёт: пароль внутрь и наружу. Разделяй UserCreate и UserOut.
- −Блокирующий вызов в async def душит event loop - автопул threadpool FastAPI применяет только к обычным def.
- −Считать response_model только документацией: это ещё и защита от утечки лишних полей.
- −Ждать strict-валидации по умолчанию: для JSON body Pydantic в lax-режиме приводит типы - '3' станет 3.
Проверьте себя
Пять вопросов из банка по этой подтеме. Всего их 14, остальные разбираются в тренажёре.
- Откуда FastAPI берёт валидацию тела запроса и сериализацию ответа?A)Из докстрингов эндпоинтов, парся их вручнуюB)Из аннотаций типов через Pydantic-моделиC)Из middleware, проверяющего каждый запрос по схемеD)Из отдельного конфигурационного файла валидации проекта
показать ответ и разбор
+B)Из аннотаций типов через Pydantic-модели// разбор: FastAPI читает аннотации типов параметров и Pydantic-моделей: из них строит валидацию входа, сериализацию выхода и OpenAPI-схему. Источник правды один — тип. Объявил модель — получил проверку, приведение и доку без отдельного слоя валидации.
- Зачем эндпоинту параметр
response_model?A)Ускорить ответ за счёт заранее закэшированной моделиB)Указать, какую таблицу вернуть клиенту напрямуюC)Задать модель таблицы базы данных для эндпоинтаD)Описать и отфильтровать форму ответапоказать ответ и разбор
+D)Описать и отфильтровать форму ответа// разбор: response_model валидирует и сериализует ответ по схеме и — важно — отбрасывает поля, которых в модели нет. Вернёшь объект с лишним password, а в модели его нет — клиент не увидит. Плюс корректная OpenAPI-схема ответа.
- Поле объявлено
age: int, в JSON пришло"age": "3"(строка). Что сделает Pydantic v2 (как в FastAPI)?A)Отклонит: строка это не int, даже если внутри числоB)Приведёт к int 3 — числовая строка допустимаC)Оставит значением строку '3' без приведения типаD)Молча подставит 0 как значение по умолчанию поляпоказать ответ и разбор
+B)Приведёт к int 3 — числовая строка допустима// разбор: В нестрогом (lax) режиме, который FastAPI применяет к JSON, Pydantic v2 приводит числовую строку "3" к int 3. А вот "хай" вызовет ValidationError. Нужно запретить такие приведения — включай strict на поле или модели.
- Что делает
Depends(get_db)в сигнатуре эндпоинта FastAPI?A)Откладывает запуск эндпоинта до готовности соединения с БДB)Создаёт один глобальный синглтон соединения на приложениеC)Резолвит зависимость и подставляет её результатD)Помечает этот параметр как необязательный в схемепоказать ответ и разбор
+C)Резолвит зависимость и подставляет её результат// разбор: Depends объявляет зависимость: FastAPI перед вызовом эндпоинта выполнит get_db, а результат подставит в параметр. Так инжектятся сессии БД, текущий пользователь, настройки. Зависимости кэшируются в пределах запроса и умеют цепляться друг за друга.
- Эндпоинт объявлен обычным
def(неasync def). Как FastAPI его исполнит?A)Запустит в отдельном процессе через пул процессовB)Откажется запускать: путь-операции обязаны быть asyncC)В пуле потоков, чтобы не блокировать event loopD)Так же, как async — прямо в цикле событий приложенияпоказать ответ и разбор
+C)В пуле потоков, чтобы не блокировать event loop// разбор: FastAPI видит sync-функцию и уносит её в threadpool, чтобы возможный блокирующий код не заморозил event loop. async def исполняется прямо в цикле. Отсюда правило: блокирующий I/O без await безопаснее в обычном def, чем в async с блокировкой.
дальше
Теорию прочитали. Навык ставится повторением
В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.