Какие ошибки делают команды при внедрении Pydantic?
Частые ошибки: использование одной модели для input и output (утечка внутренних полей), бизнес-логика в field_validator вместо сервисного слоя, забытый model_rebuild() при circular references, игнорирование ValidationError в продакшне.
Ошибка 1: одна модель для input и output
Команды часто используют одну модель и для приёма запроса, и для ответа. В результате в API утекают внутренние поля (хэш пароля, внутренние флаги).
# Плохо: одна модель
class User(BaseModel):
id: int
email: str
password_hash: str # утечёт в ответе!
# Хорошо: разделить
class UserCreate(BaseModel):
email: str
password: str
class UserResponse(BaseModel):
id: int
email: str
model_config = {"from_attributes": True}
Ошибка 2: бизнес-логика в field_validator
Проверка уникальности email, баланса счёта или наличия записи в БД не должна быть в @field_validator. Pydantic вызывает валидаторы в конструкторе — до того, как у вас есть сессия БД или контекст запроса.
# Плохо: запрос к БД в валидаторе
class UserCreate(BaseModel):
email: str
@field_validator("email")
@classmethod
def email_unique(cls, v: str) -> str:
# НЕЛЬЗЯ: нет db session, нет async context
if db.query(User).filter_by(email=v).first():
raise ValueError("email already taken")
return v
# Хорошо: проверка в сервисном слое
async def create_user(data: UserCreate, db: AsyncSession) -> User:
existing = await db.scalar(select(User).where(User.email == data.email))
if existing:
raise HTTPException(status_code=409, detail="email already taken")
...
Ошибка 3: игнорирование ValidationError в продакшне
При парсинге внешних данных (Telegram, webhooks) ValidationError содержит точный путь к сломанному полю. Команды ловят его как Exception и теряют контекст.
from pydantic import ValidationError
import logging
logger = logging.getLogger(__name__)
try:
job = JobCreate.model_validate(raw_data)
except ValidationError as e:
# e.errors() — список с loc, msg, type
logger.warning("invalid job payload", extra={"errors": e.errors()})
raise
Ошибка 4: забытый model_rebuild() при forward references
При circular references между моделями Pydantic v2 требует явного вызова model_rebuild(). Без него — PydanticUserError: "Model is not fully defined" в runtime.
from __future__ import annotations
from pydantic import BaseModel
class Company(BaseModel):
name: str
jobs: list[Job] = []
class Job(BaseModel):
title: str
company: Company | None = None
# Обязательно после объявления обоих классов:
Company.model_rebuild()
Job.model_rebuild()
Ошибка 5: использование model_config без понимания влияния
Флаги вроде arbitrary_types_allowed=True или populate_by_name=True меняют поведение всей модели. Команды копируют их из SO без понимания последствий.
# arbitrary_types_allowed отключает проверку типов для кастомных классов
class Config(BaseModel):
model_config = {"arbitrary_types_allowed": True}
conn: SomeDatabaseConnection # Pydantic не проверит этот тип
Ошибка 6: мутабельные модели как value objects
По умолчанию Pydantic-модели мутабельны. Для ключей кэша, идентификаторов и других value objects нужно frozen=True.
class JobID(BaseModel):
model_config = {"frozen": True}
value: int
# Теперь можно использовать как ключ словаря
cache: dict[JobID, str] = {}
Подводные камни
- Миграция v1 → v2:
@validatorбез@classmethodне работает в v2 — ошибка тихая в некоторых версиях. .dict()удалён в v2 — используйте.model_dump(). Старый код падает сAttributeError.- Алиасы через
Field(alias="camelCase")ломаютmodel_dump()безby_alias=True. - Тип
list[BaseModel]в поле копирует объекты при валидации — неожиданное потребление памяти на больших списках. model_validateсstrict=Trueне приводит"123"кint— поведение отличается от дефолтного режима.- Вложенные модели сериализуются рекурсивно — глубокие структуры могут давать
RecursionError. - Использование
Optional[str]вместоstr | Noneв v2 работает, но считается устаревшим стилем. - Pydantic не логирует предупреждения при игнорировании лишних полей (default) — используйте
model_config = {"extra": "forbid"}на входных моделях.
What hurts your answer
- Перечислять ошибки без объяснения причин
- Не отличать beginner mistakes от production failure modes
- Не предлагать процесс, который предотвращает повторение ошибок
What they're listening for
- Знает типичные ошибки при работе с Pydantic
- Понимает причины ошибок
- Предлагает практики prevention и early detection