Урок курса
Ограничения полей и ошибки валидации
FastAPI для начинающих: API с базой данных и тестамиВ прошлом уроке модель RoomIn объявляла поля через простые аннотации: name: str и capacity: int. FastAPI проверял тип, но ничего не знал о допустимых значениях — вместимость 0 или имя из одного символа проходили без ошибки. Сейчас добавим правила прямо в объявление поля.
Field(...) как контракт: ge, le, min_length, max_length
Чтобы задать числовые или строковые ограничения, используют Field из pydantic. Выражение с Field записывают справа от =: оно описывает ограничения поля и, при необходимости, его значение по умолчанию. В показанном ниже Field(...) многоточие оставляет поле обязательным без значения по умолчанию.
Первый аргумент Field — это либо многоточие ..., либо конкретное значение по умолчанию. Многоточие означает «поле обязательно»: если клиент не передаст его в теле запроса, FastAPI вернёт 422 не выполняя обработчик.
Для числовых полей:
ge— значение должно быть больше или равно указанному;le— меньше или равно;gtиlt— строго больше и строго меньше.
Для строковых:
min_length— минимальная длина в символах Unicode;max_length— максимальная.
Модель для переговорной выглядит так:
from pydantic import BaseModel, Field
class RoomIn(BaseModel):
name: str = Field(..., min_length=2, max_length=50)
capacity: int = Field(..., ge=1, le=50)
Теперь name обязана содержать от 2 до 50 символов, а capacity — целое число от 1 до 50 включительно. Правила проверяются до того, как управление дойдёт до функции-обработчика: если хотя бы одно нарушено, FastAPI отвечает 422.
Field(0, ge=1) против Field(..., ge=1): что происходит при пропуске поля
Представьте, что кто-то написал модель чуть иначе:
class RoomIn(BaseModel):
name: str = Field(..., min_length=2, max_length=50)
capacity: int = Field(0, ge=1) # default = 0
Теперь у capacity есть значение по умолчанию — 0. Если клиент отправит тело без этого поля:
{"name": "Большой зал"}
Pydantic v2 в стандартной конфигурации подставит 0 и не будет проверять, удовлетворяет ли сам default ограничению ge=1. Обработчик получит capacity=0 и вернёт 200.
При этом если клиент явно передаст "capacity": 0, ограничение сработает и ответ будет 422. Итог: одно и то же значение 0 ведёт себя по-разному в зависимости от того, пришло ли оно из запроса или было подставлено как default.
Чтобы этого не было, используйте Field(..., ge=1). Тогда пропущенное поле даёт 422, а default, нарушающий правило, в принципе не возникает. Значение по умолчанию стоит задавать только тогда, когда оно само по себе допустимо по контракту.
Как прочитать ответ 422 и найти неверное поле
Возьмём полный маршрут с моделью, которую объявили выше:
from fastapi import FastAPI
from pydantic import BaseModel, Field
app = FastAPI()
class RoomIn(BaseModel):
name: str = Field(..., min_length=2, max_length=50)
capacity: int = Field(..., ge=1, le=50)
@app.post("/api/rooms")
def create_room(room: RoomIn):
return {"received_name": room.name, "capacity": room.capacity}
Отправим запрос с намеренно неверными значениями:
{"name": "А", "capacity": 0}
FastAPI вернёт статус 422. Ниже сокращённое тело ответа: у каждой ошибки оставлены loc, msg и type.
{
"detail": [
{
"loc": ["body", "name"],
"msg": "String should have at least 2 characters",
"type": "string_too_short"
},
{
"loc": ["body", "capacity"],
"msg": "Input should be greater than or equal to 1",
"type": "greater_than_equal"
}
]
}
detail — массив: каждый объект описывает одно нарушение. Для диагностики рассмотрим три показанных поля; в полном ответе могут быть и другие, например input и ctx.
loc — путь к проблемному месту. Первый элемент "body" говорит, что ошибка в JSON-теле запроса. Второй — имя конкретного поля модели. По этому пути сразу понятно, что исправлять.
msg — человекочитаемое описание: что именно нарушено. Видно, что name слишком короткое, а capacity меньше 1.
type — машинный код ошибки: удобен, если нужно обрабатывать ответ программно.
В данном случае оба поля нарушены одновременно, поэтому detail содержит два объекта. Чтобы исправить запрос, достаточно передать name длиной от 2 до 50 символов и capacity от 1 до 50 — например, {"name": "Малый зал", "capacity": 10}.
![Поле capacity: int ограничено Field(..., ge=1, le=50). Переданное capacity 0 даёт 422 до вызова обработчика. В одном элементе detail loc равен [body, capacity], а msg сообщает Input should be greater than or equal to 1.](/uploads/lesson-media/field_error-1788107190599-0f86e12a-670e-4204-9275-e61ad785ba98.png)
Попробуйте решить
Модель объявлена так:
class RoomIn(BaseModel):
name: str = Field(..., min_length=2, max_length=50)
capacity: int = Field(..., ge=1, le=50)
Клиент отправляет {"name": "Переговорная номер один в офисе Москва длинное название которое точно превышает лимит", "capacity": 10}. Каков статус ответа и какое поле укажет loc?
Продолжить с проверкой и прогрессом
Откройте интерактивный раннер с заданиями урока.
