Задача курса

FastAPI: создание бронирования и ответ 409 при конфликте

Курс «FastAPI для начинающих: API с базой данных и тестами» · урок «Создание бронирования или ответ 409»

Условие

Заявка на свободную переговорную

Roomly принимает заявку на бронирование комнаты на указанный интервал. Комната должна существовать, а время не должно пересекаться с её сохранёнными бронированиями. Соседние встречи без перекрытия допустимы.

Если проверки пройдены, сохраните бронь и верните подтверждение со временем в UTC. Иначе отклоните заявку, не меняя прежние записи. Повтор той же заявки должен конфликтовать с уже созданной бронью. Здесь рассматривается последовательная обработка запросов.

Что уже дано

Это самостоятельная SQLAlchemy-лаба с Room(id/name/capacity) и Booking(id/room_id/starts_at/ends_at), а не миграция прежней базы. В видимом файле полностью даны модели, SQLite с внешними ключами, Session, BookingIn/BookingOut, to_db_utc/find_conflict и CRUD комнат. GET бронирований тоже готов. TODO только в create_booking и public_booking; остальные части переписывать не нужно. Весь файл исполняется один раз; оставьте по одному GET и POST на пути бронирований.

Перед файлом публичная подготовка создаёт собственный временный SQLite-файл и предоставляет DATABASE_URL. Она выполняет настоящий HTTP через TestClient и читает тот же файл отдельным SQLite-соединением. Ожидаемых ответов в ней нет. Наблюдатель сообщает реальные SQL-записи, чтобы проверить отказ до INSERT, а не после его отката. После проверки закрываются свои ресурсы и удаляется только собственный каталог. Не пишите инфраструктуру или тесты.

Что нужно сделать

Завершите POST /api/rooms/{room_id}/bookings. Тело — предоставленная BookingIn. Для корректного тела сначала проверьте Room: отсутствующая комната даёт404/{"detail":"Room not found"}. Затем вызовите готовый find_conflict: совпадение даёт409/{"detail":"Booking conflict"}. Оба отказа происходят до SQL-изменений и сохраняют прежние строки. Только свободный интервал создаёт отдельную Booking с этим room_id: границы записываются через to_db_utc, изменение фиксируется, ответ —201. Идентификатор назначается базой; дополнительный id/room_id из JSON или query не заменяет path и не перезаписывает записи.

Реализуйте public_booking для Booking, прочитанной из столбцов naive UTC. Верните BookingOut либо словарь с ровно id/room_id/starts_at/ends_at и aware UTC-значениями времени. Это присвоение пояса заведомо UTC-данным, не угадывание пояса пользовательской строки. Готовый GET применяет ту же функцию: существующая комната получает200 со всеми своими бронями по Booking.id, пустая —[], отсутствующая —404. Строки времени JSON могут использовать Z или +00:00; сам момент и смещение UTC должны быть верны. Явный refresh не требуется при эквивалентно корректном результате.

BookingIn сохраняет422 для пропущенных, неверных, naive и несогласованных границ до входа в обработчик, даже если комнаты нет. Дробный и нечисловой path также дают422. Повтор уже принятого интервала даёт409; касание границ допустимо, вложение конфликтует. Рассматриваются только последовательные обращения, гарантий одновременной обработки здесь нет.

Сохраните готовые GET/POST/PUT/PATCH/DELETE комнат с их валидацией и фиксацией состояния; занятая комната защищена409/{"detail":"Room has bookings"}, свободная удаляется204 без тела. Это готовый контекст, не дополнительные TODO. Не вводите новые поля или бизнес-правила.

Ввод и вывод

stdin не используется. Печатать через print в stdout ничего не нужно. После выполнения файла проверяющая система обращается к верхнеуровневому app: публичный переходник вызывает TestClient внутри процесса приложения и передаёт результат скрытой проверке. Запускать Uvicorn, сеть, клиентский скрипт или pytest не требуется.

После создания комнаты используйте фактически выданный id. Бронь 10:00–11:00 с +03:00 сохраняется и возвращается как07:00–08:00 UTC. Её последовательный повтор получает409 без новой строки;08:00–09:00 UTC допустим. Следующий GET показывает обе сохранённые брони.

Попробуйте решить

РешениеPython
Без регистрации · результат не сохраняется