Вложенные данные FastAPI: как найти ошибку в нужной строке заказа
Покупатель отправляет заказ из трёх позиций. Сервер отвечает «неверное количество», а интерфейс не знает, какую строку исправлять. При ручном разборе словарей место ошибки легко потерять. Вложенные модели сохраняют структуру документа и позволяют вернуть путь до конкретного элемента.
Схема повторяет форму документа
Заказ содержит список lines. Каждая позиция имеет sku и quantity. Ограничения количества принадлежат модели позиции, а ограничение числа позиций — самому списку. Так пустой заказ и отрицательное количество получают разные пути ошибки и не смешиваются в одно сообщение.
Не превращайте весь документ в dict только ради гибкости. Для действительно произвольных атрибутов можно оставить отдельный словарь, но поля, от которых зависит бизнес-операция, полезно описать явно. Тогда API и OpenAPI согласованы, а обработчик получает уже разобранные объекты.
Проверяем вложенную ошибку через HTTP
В примере второй элемент имеет количество ноль. Индекс в loc начинается с нуля, поэтому lines, 1, quantity указывает на вторую строку. Тест намеренно не сравнивает английский текст сообщения: клиенту надёжнее использовать путь и тип ошибки.
Сохраните весь блок в файл и запустите Python 3.11+ в отдельном окружении с пакетами fastapi, httpx. Для моделей используется Pydantic v2; отдельный сервер для TestClient не нужен.
from fastapi import FastAPI
from fastapi.testclient import TestClient
from pydantic import BaseModel, Field
app = FastAPI()
class Line(BaseModel):
sku: str = Field(min_length=1)
quantity: int = Field(ge=1)
class Order(BaseModel):
lines: list[Line] = Field(min_length=1, max_length=100)
@app.post("/orders")
def order(data: Order):
return {"units": sum(line.quantity for line in data.lines)}
with TestClient(app) as client:
good = {"lines": [{"sku": "A", "quantity": 2}, {"sku": "B", "quantity": 3}]}
assert client.post("/orders", json=good).json() == {"units": 5}
bad = {"lines": [{"sku": "A", "quantity": 2}, {"sku": "B", "quantity": 0}]}
r = client.post("/orders", json=bad)
assert r.status_code == 422
assert r.json()["detail"][0]["loc"] == ["body", "lines", 1, "quantity"]
assert client.post("/orders", json={"lines": []}).status_code == 422
assert client.post("/orders", json={"lines": [{"sku": "A"}]}).status_code == 422
print("nested field location and total units checked")Как связать ответ с формой
Уберите первый сегмент body и пройдите по оставшемуся пути: имя списка, индекс, имя поля. Интерфейс может подсветить quantity у второй позиции. Если пользователь успел переставить строки после отправки, индекс относится к отправленному документу, поэтому полезно удерживать его снимок до ответа или сопоставлять строки со своими локальными идентификаторами.
Ошибок может быть несколько. Не берите всегда только detail[0] в рабочем интерфейсе: пример делает это для одной намеренно испорченной позиции. Пройдите по всем элементам detail, сгруппируйте их по полям и оставьте общее сообщение для ошибок уровня всей модели.
Где заканчивается валидация формы
Правильный sku по типу ещё не означает, что товар существует. Количество больше нуля ещё не означает, что столько товара есть на складе. Эти проверки требуют данных и выполняются после схемы. Хорошо, когда HTTP-тесты отдельно показывают невалидный документ и отказ бизнес-операции: тогда изменение каталога не ломает проверку формы.
Ограничение max_length=100 защищает число позиций, но не заменяет ограничение размера HTTP-тела. Один элемент способен содержать огромную строку, поэтому для текстовых полей задавайте соответствующие пределы и контролируйте размер запроса на сервере или прокси. Выбирайте числа из реального сценария интерфейса, чтобы допустимый заказ не оказался случайно запрещён.
Следующая практика
Начните с открытой практики ограничений полей и ошибок Pydantic. Когда вы уверенно читаете loc для одного поля, примените тот же подход к позиции вложенного списка и адресной подсветке формы заказа. Откройте бесплатный урок об ошибках валидации.
В бесплатной программе FastAPI модели запроса переходят в операции с базой и проверяемые HTTP-сценарии. Такой порядок помогает сохранить понятные ошибки формы, когда приложение становится сложнее. Откройте программу курса.
Продолжить на практике
- FastAPI для начинающих: API с базой данных и тестами ↗
Соберёте свой первый полноценный backend на FastAPI: с базой данных, регистрацией, JWT-авторизацией и автотестами. 34 коротких урока и 140 заданий с проверкой — бесплатно.
