Pydantic превращает сырой словарь из JSON в объект с проверенными полями или в структурированный список ошибок. Соберём модель заказа на Pydantic 2, проверим пропущенное имя, отрицательную цену и неверное количество. Затем превратим технический ValidationError в сообщения, которые можно показать пользователю API.
Обычный dict принимает строку вместо числа и замечает проблему только глубже в программе. С Pydantic граница данных появляется сразу после json.loads. В этой статье вы соберёте валидатор заказа с полями name, price и quantity. Финальный результат можно проверить без догадок: правильный JSON даёт total=599.80, а три неверных поля возвращаются отдельными ошибками.
Типы и ограничения живут в классе Order, а не размазаны по нескольким if.
model_validate принимает словарь и либо создаёт объект, либо поднимает ValidationError.
errors() даёт loc, type и msg, из которых собирается понятный ответ API.
Как Pydantic проверяет JSON на границе программы
Входной JSON сначала превращается в словарь Python. Затем Order.model_validate сопоставляет ключи с аннотациями, применяет Field и дополнительный field_validator. Бизнес-код получает только Order, поэтому не повторяет проверки цены и количества. Для Pydantic держите этапы раздельно: ввод, проверка, основное действие и вывод. Такой маршрут позволяет остановиться в конкретной точке, увидеть фактические данные и исправить одну причину вместо случайной перестановки строк.
| Этап | Действие | Проверяемый результат |
|---|---|---|
| Разбор JSON | json.loads превращает строку в dict | Ключи и значения доступны Python |
| Модель | Order.model_validate проверяет схему | Получен Order или ValidationError |
| Ошибки | errors() раскрывает путь и тип | Каждая проблема имеет поле |
| Расчёт | total умножает price на quantity | Сумма считается из валидных чисел |
Ошибка останавливается у входа, а не всплывает как TypeError во время расчёта. У Pydantic каждый этап оставляет наблюдаемый след, поэтому следующий шаг начинается только после проверки предыдущего.

От входных данных до видимого результата. Бизнес-код получает только проверенный объект
Пишем модель заказа и выводим ошибки
Создайте проект командами uv init pydantic-lab и uv add pydantic. Сохраните код в main.py и запустите uv run python main.py. Пример использует API Pydantic 2: BaseModel, Field, field_validator и model_validate. Старый parse_obj из материалов по Pydantic 1 здесь не нужен.
import json
from decimal import Decimal
from pydantic import BaseModel, Field, ValidationError, field_validator
class Order(BaseModel):
name: str = Field(min_length=2, max_length=40)
price: Decimal = Field(gt=0, decimal_places=2)
quantity: int = Field(ge=1, le=20)
@field_validator("name")
@classmethod
def name_must_not_be_blank(cls, value: str) -> str:
cleaned = value.strip()
if not cleaned:
raise ValueError("name cannot be blank")
return cleaned
@property
def total(self) -> Decimal:
return self.price * self.quantity
raw = '{"name":"Keyboard","price":"299.90","quantity":2}'
try:
order = Order.model_validate(json.loads(raw))
print(order.model_dump())
print(f"total={order.total:.2f}")
except ValidationError as error:
for item in error.errors():
field = ".".join(map(str, item["loc"]))
print(f"{field}: {item['msg']}")- Строка цены безопасно преобразуется в Decimal.
- Пробелы по краям имени удаляются валидатором.
- Количество 0 даёт ошибку greater_than_equal.
- Валидный заказ печатает total=599.80.
Decimal выбран вместо float, чтобы учебный пример не накапливал двоичную погрешность в денежной сумме. Пример Pydantic специально ограничен одним пользовательским сценарием, чтобы назначение каждой строки оставалось видимым.

Что отличает устойчивый проект от случайного запуска. Контракт держит ограничения рядом с типами
Из чего состоит ValidationError в Pydantic 2
BaseModel читает аннотации один раз при создании класса и строит внутреннюю схему. Field добавляет ограничения поверх типа. При model_validate Pydantic собирает все найденные проблемы, а не останавливается на первой. Поле loc показывает путь, поэтому вложенная ошибка может выглядеть как items.0.price. Поле type удобно для машинной обработки, msg подходит для журнала, input помогает увидеть исходное значение. В ответе публичного API не стоит бездумно возвращать весь input: там может оказаться пароль или токен.
| Часть | За что отвечает | Что проверить |
|---|---|---|
| BaseModel | Описывает форму объекта | Все обязательные поля перечислены |
| Field | Задаёт диапазон и длину | Границы совпадают с задачей |
| field_validator | Проверяет одно поле дополнительно | Возвращает очищенное значение |
| ValidationError | Хранит список проблем | errors() не теряет loc |
| model_dump | Сериализует готовую модель | Decimal обработан осознанно |
Pydantic может преобразовать строку "299.90" в Decimal, но это не значит, что нужно принимать любые преобразования. Для строгой границы используйте StrictInt, StrictStr или model_config. У значения по умолчанию есть отдельная ловушка: оно не всегда валидируется так же, как вход, пока не включена соответствующая настройка. Модели также умеют вложенные списки и JSON Schema, однако новичку полезнее сначала научиться читать errors(). Если ошибка не объяснима по loc и type, усложнять модель рано.
Ломаем входные данные и проверяем правила
Запустите валидный заказ. Затем по очереди удалите name, передайте price=-1, quantity="many" и имя из пробелов. После каждого запуска выпишите loc и type. Добавьте необязательное поле coupon: str | None = None и запретите код длиннее 12 символов. Последний шаг: создайте Cart с полем items: list[Order] и проверьте ошибку во втором элементе. Не исправляйте данные внутри except, сначала добейтесь точного сообщения.
- Запустите валидный JSON и проверьте total.
- Удалите name и найдите тип missing.
- Передайте отрицательную price и найдите поле в loc.
- Проверьте quantity=21 и верхнюю границу.
- Добавьте coupon с ограничением длины.
- Создайте список из двух Order и сломайте второй.
- Валидный словарь создаёт Order.
- Три неверных поля видны отдельно.
- Денежный расчёт использует Decimal.
- Новая проверка добавляется в одном месте.
Хорошая модель не угадывает намерение пользователя. Она принимает описанный формат и точно сообщает, где контракт нарушен. После этой проверки измените одно правило Pydantic и заранее запишите ожидаемый результат. Так копирование превращается в понимание.

Четыре проверки перед следующим шагом. Ошибку можно воспроизвести отдельным входом
Как применять Pydantic в настоящем API
В FastAPI модели Pydantic подключаются к телу запроса автоматически, но смысл остаётся тем же: недоверенные данные проходят через контракт до бизнес-логики. Разделяйте модели создания, ответа и обновления, если у них разные обязательные поля. Не храните секреты в repr и не отдавайте внутренний input без фильтра. Для больших проектов вынесите модели в отдельный модуль, но оставьте рядом примеры входа и тесты на границы.
Как изучить тему в Кодике
Начните со словарей, функций и исключений Python. Затем сравните ручные if с декларативной моделью. В Кодике сначала восстановите чистую логику без фреймворка, затем верните библиотеку и интерфейс Pydantic.
| Шаг | Что разобрать | Мини-проверка |
|---|---|---|
| 1 | Словари и JSON | Получить price по ключу |
| 2 | Аннотации типов | Объяснить int и Decimal |
| 3 | Исключения | Перехватить только ValidationError |
| 4 | Классы и свойства | Вычислить total |
| 5 | Контракты API | Собрать вложенный Cart |
Сначала напишите одну ручную проверку, потом замените её Field и сравните сообщения. Финальная проверка для Pydantic: закрыть статью, объяснить путь данных и добавить одну функцию без копирования готового блока.
Decimal делает ожидаемой сумму 299.90 × 2 и форматирование до копеек.
Валидация должна завершиться до вызова бизнес-логики.
Соберите безопасный ответ из loc, type и собственного текста.
Для нового проекта используйте model_validate, model_dump и field_validator из Pydantic 2.
Что получится в итоге

Модель принимает корректный заказ, считает сумму и превращает несколько нарушений контракта в отдельные сообщения с путём к полю.
Что возвращает Order.model_validate?
Order.model_validate возвращает экземпляр Order или поднимает ValidationError со списком проблем.
Зачем нужен loc в ошибке Pydantic?
loc показывает путь к полю, включая индекс элемента во вложенном списке.
Когда нужен field_validator?
field_validator нужен для правила одного поля, которого нельзя выразить только типом и Field.
Как понять, что учебный проект Pydantic действительно работает?
Проверьте основной сценарий Pydantic, неверный ввод, повторный запуск и один граничный случай. Затем объясните путь данных своими словами.
Можно ли начать с готового кода Pydantic из статьи?
Да. Сначала добейтесь результата Pydantic, затем измените одно правило и восстановите ключевой файл без копирования.
Базовые типы и исключения разберите в курсе Python. Затем подключите модель к первому API на FastAPI.
Сравните ошибки модели с разбором TypeError между строкой и числом и ValueError при преобразовании int.