Гайды

Pydantic: валидация и сериализация моделей

BaseModel и Field, validators, model_config, вложенные модели, pydantic-settings и типичные ошибки в API.

~11 мин чтения

Pydantic: валидация и сериализация моделей

Pydantic v2 (через pydantic-core на Rust) — основа тел запросов/ответов в FastAPI. Этот гайд про модели, валидацию, сериализацию и типичные паттерны. Связка с API — Быстрый старт с FastAPI.


1. BaseModel: поля и типы

python
from pydantic import BaseModel, Field, EmailStr
from datetime import datetime

class UserCreate(BaseModel):
    email: EmailStr
    name: str = Field(min_length=1, max_length=100)
    age: int | None = Field(default=None, ge=0, le=150)

class UserRead(BaseModel):
    model_config = {"from_attributes": True}  # для ORM: read from object attrs

    id: int
    email: EmailStr
    created_at: datetime

from_attributes (раньше orm_mode) позволяет строить ответ из SQLAlchemy/Django ORM объекта.


2. Валидаторы

field_validator — проверка/нормализация одного или нескольких полей:

python
from pydantic import field_validator

class PasswordReset(BaseModel):
    password: str
    password_repeat: str

    @field_validator("password")
    @classmethod
    def strong(cls, v: str) -> str:
        if len(v) < 12:
            raise ValueError("min 12 chars")
        return v

    @field_validator("password_repeat")
    @classmethod
    def match(cls, v: str, info):
        if "password" in info.data and v != info.data["password"]:
            raise ValueError("passwords do not match")
        return v

model_validator(mode="after") — целиком по собранной модели.


3. Настройки модели (model_config)

python
class Item(BaseModel):
    model_config = {
        "str_strip_whitespace": True,
        "validate_assignment": True,  # валидация при obj.field = x
        "extra": "forbid",           # запрет неизвестных полей в JSON
    }
    name: str

4. Вложенные модели и дискриминаторы

python
class Address(BaseModel):
    city: str
    street: str

class Order(BaseModel):
    id: int
    ship_to: Address

Для union-типов с полем «тип» используйте Discriminator (события разных видов в одном эндпоинте).


5. JSON и ORM

  • model_dump() — dict для JSON (режим mode="json" сериализует datetime в ISO-строки).
  • model_dump_json() — строка JSON.
  • model_validate(data) — разбор dict/объекта в модель.

6. Настройки приложения: pydantic-settings

python
from pydantic_settings import BaseSettings, SettingsConfigDict

class Settings(BaseSettings):
    model_config = SettingsConfigDict(env_file=".env", extra="ignore")

    database_url: str
    api_secret: str

settings = Settings()

Интеграция в FastAPI: зависимость get_settings() с lru_cache или Annotated[Settings, Depends()].


7. Алиасы полей (вход и выход)

Внешний JSON в camelCase, внутри модели — snake_case: используйте Field(alias="...") и populate_by_name=True, чтобы принимать оба варианта имени при необходимости. Для ответов в v2 — serialization_alias / AliasChoices (см. документацию Field).

python
from pydantic import BaseModel, ConfigDict, Field

class EventIn(BaseModel):
    model_config = ConfigDict(populate_by_name=True)
    event_type: str = Field(alias="eventType")

Так проще не дублировать DTO для мобильного клиента и бэкенда.


8. Частые ошибки

ПроблемаРешение
ValidationError нечитаем в логахЛогировать e.errors()
Утечка внутренних полей в ответОтдельные модели Read без секретов
ORM lazy-load в asyncНе отдавать ORM напрямую; явно собрать DTO

9. Секреты и строгая конфигурация

Используйте SecretStr / SecretBytes для токенов и паролей — в model_dump() по умолчанию не попадут plain-text значения (для логов и ответов). Глобальные настройки моделей в v2 задаются через ConfigDict, а не вложенный класс Config.

python
from pydantic import BaseModel, ConfigDict, SecretStr

class WebhookIn(BaseModel):
    model_config = ConfigDict(str_max_length=4096)
    token: SecretStr

Связанные материалы


Дальше: тег Pydantic · тег Python