Pydantic v2: модели и валидация данных
Pydantic-модель — это класс-наследник BaseModel с типизированными полями; он принимает «сырые» данные, валидирует их, конвертирует типы и даёт строго типизированный объект.
Модель — это одновременно описание формы данных, валидатор и сериализатор. В версии 2 ядро Pydantic переписали на Rust, поэтому валидация стала в разы быстрее.
Pydantic — это вторая опора FastAPI. Именно он стоит за тем, что тело запроса проверяется, а ответ корректно сериализуется. Вы описываете модель как обычный класс с аннотациями полей, а Pydantic берёт на себя всё остальное: проверит, что строка не пришла вместо числа, подставит значения по умолчанию, отбросит лишнее или пожалуется на недостающее.
from pydantic import BaseModel
class User(BaseModel):
id: int
name: str
age: int = 0
is_active: bool = True
# создание из словаря (Pydantic v2)
user = User.model_validate({"id": "7", "name": "Аня", "age": "30"})
print(user.id, type(user.id)) # 7 <class 'int'> — строка сконвертирована
print(user.model_dump()) # сериализация в обычный dict
Ключевые методы Pydantic v2, которые заменили старые: model_validate(data) вместо parse_obj создаёт и проверяет модель из словаря, model_validate_json(bytes) — прямо из JSON-байтов (быстрее, чем сначала парсить, потом валидировать), model_dump() возвращает словарь, model_dump_json() — JSON-строку. FastAPI вызывает эти методы за вас.
Как работает под капотом
Pydantic для каждого поля знает целевой тип и пытается привести значение к нему по понятным правилам, а при провале накапливает ошибки и сообщает их разом. Смоделируем суть валидатора модели на stdlib:
def validate(schema, data):
result, errors = {}, []
for field, (typ, required, default) in schema.items():
if field not in data:
if required:
errors.append(f"{field}: обязательное поле отсутствует")
else:
result[field] = default # подстановка дефолта
continue
try:
result[field] = typ(data[field]) # конвертация в целевой тип
except (TypeError, ValueError):
errors.append(f"{field}: ожидался {typ.__name__}")
return result, errors
schema = {
"id": (int, True, None),
"name": (str, True, None),
"age": (int, False, 0),
}
print(validate(schema, {"id": "7", "name": "Аня", "age": "30"}))
print(validate(schema, {"name": "Боб"})) # нет id
print(validate(schema, {"id": "x", "name": "В"})) # id не число
Попробуй сам ▶ Видно три исхода: успешная конвертация, отсутствие обязательного поля, ошибка типа — это упрощённая модель того, что делает Pydantic, только быстрее и подробнее.
Частые ошибки
Первая — путать API v1 и v2: parse_obj, .dict(), .json() устарели, в v2 это model_validate, model_dump, model_dump_json. Вторая — полагаться на неявную конвертацию там, где она нежелательна; иногда нужен строгий режим (strict=True), чтобы строка "7" не превращалась молча в число. Третья — складывать в модель «сырые» данные без аннотаций типов, теряя смысл инструмента.
Best practices
- Используйте методы v2:
model_validate,model_validate_json,model_dump,model_dump_json. - Берите
model_validate_json, если у вас уже есть JSON-байты, — это быстрее. - Описывайте поля точными типами; для строгости применяйте strict-режим там, где важна точность.
- Держите модели маленькими и осмысленными; одна модель — одна форма данных.
model_config и поведение модели
В Pydantic v2 поведение модели настраивается через атрибут model_config (на смену устаревшему вложенному классу Config из v1). Через него включают полезные режимы: from_attributes=True позволяет строить модель прямо из ORM-объекта, читая его атрибуты, что незаменимо при отдаче данных из базы; str_strip_whitespace=True автоматически обрезает пробелы у строк; frozen=True делает модель неизменяемой. Особо стоит выделить from_attributes: именно он позволяет вернуть из обработчика SQLAlchemy-объект, а FastAPI через response_model аккуратно спроецирует его в выходную модель, не требуя ручного перекладывания полей. Знание model_config отличает поверхностное использование Pydantic от осознанного: вы не боретесь с библиотекой, а настраиваете её под свой сценарий.
Итог: Pydantic-модель валидирует, конвертирует и сериализует данные на основе аннотаций. В v2 ядро на Rust и новые методы (model_*); именно этот слой делает данные в FastAPI надёжными.