Полное и частичное обновление
Содержание курса
PATCH: отказ при null до изменения rooms_db
Обработчик PATCH работает в два прохода по model_fields_set. Первый проход — только проверка, без записи. Второй — только запись, без повторной проверки.
@app.patch("/api/rooms/{room_id}", response_model=RoomOut)
def partial_update_room(room_id: int, room: RoomPatch):
existing = get_room_or_404(room_id)
# Проход 1: проверить все присланные поля на null
for field in room.model_fields_set:
if getattr(room, field) is None:
raise HTTPException(status_code=422, detail="Fields cannot be null")
# Проход 2: записать только после успешной проверки
for field in room.model_fields_set:
existing[field] = getattr(room, field)
return existing
Почему два цикла, а не один? Если объединить проверку и запись в одном проходе, запрос вроде {"name": "Бета", "capacity": null} может успеть обновить name до того, как цикл доберётся до capacity и бросит исключение. Порядок обхода set не гарантирован, поэтому результат зависел бы от случайности. Разделение циклов исключает частичные изменения: либо обновляются все присланные поля, либо ни одно.
При существующей записи PATCH принимает часть полей или пустой объект, но отклоняет явный null:
Частичное обновление — 200, второе поле не тронуто:
PATCH /api/rooms/1
{"capacity": 20}
Предположим, до запроса запись была {"id": 1, "name": "Переговорная Альфа", "capacity": 12}. После запроса GET /api/rooms/1 вернёт {"id": 1, "name": "Переговорная Альфа", "capacity": 20}. name остался прежним.
Пустой объект — 200, запись не изменилась:
PATCH /api/rooms/1
{}
model_fields_set пуст, оба цикла не выполняются. GET /api/rooms/1 вернёт ту же запись.
Явный null — 422, оба поля не тронуты:
PATCH /api/rooms/1
{"name": "Бета", "capacity": null}
Первый цикл обнаружит None для одного из полей и выбросит HTTPException(422, detail="Fields cannot be null"). До второго цикла дело не дойдёт. GET /api/rooms/1 покажет прежние значения — name тоже не изменился, хотя и прошёл бы проверку сам по себе.
Сама по себе эта проверка не защищает от конкурентных запросов: если два PATCH придут одновременно, rooms_db как простой словарь не даёт никаких гарантий. Но это уже вопрос за пределами текущего урока.

