CRUD как HTTP-контракт

Полное и частичное обновление

Содержание курса

В прошлом уроке мы добавили get_room_or_404 — вспомогательную функцию, которая либо возвращает найденную переговорную, либо сразу отдаёт 404. Теперь эта функция будет работать в обоих обработчиках обновления: PUT для полной замены и PATCH для изменения только присланных полей.

PUT /api/rooms/{room_id}: замена name и capacity

PUT в этом API означает полную замену записи: клиент обязан прислать все поля, а сервер перезаписывает их целиком, не сохраняя ничего из старых значений.

Для тела запроса используем уже знакомую RoomIn — она требует name и capacity с теми же ограничениями, что и при создании. Маршрут возвращает обновлённую переговорную через response_model=RoomOut.

@app.put("/api/rooms/{room_id}", response_model=RoomOut)
def update_room(room_id: int, room: RoomIn):
    existing = get_room_or_404(room_id)
    rooms_db[room_id] = {"id": room_id, "name": room.name, "capacity": room.capacity}
    return rooms_db[room_id]

Порядок действий здесь принципиален. Сначала get_room_or_404 проверяет существование записи — если её нет, функция сразу выбрасывает HTTPException(404) и дальше ничего не происходит. Только после успешной проверки обработчик записывает новый словарь в rooms_db. id берётся из пути, а не из тела запроса — так он не может быть перезаписан клиентом случайно.

Запись с id=1 уже существует, а с id=99 — нет. Результат PUT зависит от тела и выбранного id:

Полное тело — запись меняется, ответ 200:

PUT /api/rooms/1
{"name": "Переговорная Альфа", "capacity": 12}

Сервер вернёт {"id": 1, "name": "Переговорная Альфа", "capacity": 12}. Последующий GET /api/rooms/1 покажет те же данные.

Пропущено обязательное поле — 422, запись не тронута:

PUT /api/rooms/1
{"name": "Переговорная Альфа"}

RoomIn требует capacity, и FastAPI вернёт 422 ещё до вызова функции. rooms_db не изменится.

Несуществующий id — 404:

PUT /api/rooms/99
{"name": "Переговорная Альфа", "capacity": 12}

get_room_or_404(99) выбрасывает 404. Это поведение нашего конкретного API: мы решили не создавать новую запись по произвольному id через PUT. Другие API могут делать иначе — это не стандарт HTTP, а контракт конкретного сервиса.