Чтение и создание через SQLAlchemy 2
Уроки курсаЧтение и создание через SQLAlchemy 2
В прошлом уроке мы настроили Engine, SessionLocal и зависимость get_db, которая выдаёт Session на каждый HTTP-запрос и закрывает её в finally. Теперь Session есть — осталось использовать её для реальных запросов к SQLite.
Чтение Room: scalars(select(...)) для списка и get для id
Два самых частых сценария чтения — получить все строки таблицы и получить одну строку по первичному ключу. В SQLAlchemy 2 каждый из них решается одной строкой.
Список комнат
select(Room) строит SQL-выражение, но не выполняет его. Выполнение происходит, когда это выражение передаётся в метод сессии:
rooms = db.scalars(select(Room).order_by(Room.id)).all()
Здесь db.scalars(...) выполняет запрос и возвращает итератор, где каждый элемент — уже готовый ORM-объект Room. .all() собирает их в список. Если таблица пустая, результат — пустой список [], а не исключение.
Почему scalars, а не execute? Оба метода выполняют один и тот же SQL, но возвращают разные обёртки. db.execute(select(Room)).all() возвращает список объектов Row — строк результата, в каждой из которых ORM-объект лежит на нулевой позиции. db.scalars(...) автоматически извлекает этот первый элемент из каждой строки. Это не означает, что у таблицы один столбец — просто scalars избавляет от лишнего уровня распаковки.
Порядок строк SQL без ORDER BY не определён: база вправе вернуть их в любом порядке, и этот порядок может меняться между запросами. Если нужен стабильный порядок — добавляйте .order_by(Room.id) явно.
Одна комната по id
room = db.get(Room, room_id)
if room is None:
raise HTTPException(status_code=404, detail="Room not found")
db.get(Room, room_id) ищет строку по первичному ключу. Если строка нашлась — возвращает объект Room, если нет — None. Клиент получает 404 в случае отсутствующего id.
Внутри одного HTTP-запроса сессия ведёт так называемую identity map: если объект Room с данным id уже был загружен в текущей Session и не помечен устаревшим, повторный db.get(Room, room_id) вернёт тот же объект без нового SELECT. Это кэш в рамках одной сессии — он не переживает закрытие Session и не является общим между разными HTTP-запросами.
Проверку типа и формата room_id берёт на себя FastAPI: если маршрут объявлен с room_id: int, нечисловой путь вернёт 422 ещё до вызова db.get.
Оба фрагмента выше — части обработчиков, а не самостоятельные файлы. Полная сборка с импортами появится в следующей секции.
