Как создавать сессию в SQLAlchemy
Что проверяют
Понимаете ли вы, что сессия это единица работы и граница транзакции, а не соединение с базой. Следом спрашивают, как вы живёте с сессией в веб-приложении: одна на запрос или одна на всё приложение.
Как отвечать
Цепочка из трёх объектов:
Engine: создаётся один раз на приложение черезcreate_engine(url)и держит пул соединений;sessionmaker: фабрика с настройками, тоже одна на приложение:SessionLocal = sessionmaker(engine);Session: создаётся на единицу работы, обычно на HTTP-запрос или задачу, и закрывается после неё.
Современный стиль SQLAlchemy 2.0 строится на контекстных менеджерах:
with SessionLocal.begin() as session:
session.add(User(name="Ann"))begin() сделает commit при успешном выходе, rollback при исключении и закроет сессию. Вариант with SessionLocal() as session: только закрывает сессию, коммит вызываете сами.
Что стоит проговорить:
- identity map: в пределах сессии одной строке соответствует один объект, повторный
session.get()в базу не ходит; - ленивое соединение: оно берётся из пула при первом запросе, а не при создании сессии;
expire_on_commit: по умолчаниюTrue, после коммита атрибуты перечитываются. ОтсюдаDetachedInstanceErrorпри обращении к объекту после закрытия сессии;- запросы: основной API это
session.execute(select(User))иsession.scalars(), аsession.query()считается наследием; - асинхронность:
create_async_engineиasync_sessionmaker, при этомAsyncSessionнельзя делить между конкурентными задачами; - веб-фреймворки: в FastAPI сессию отдают зависимостью с
yield, во Flask её к запросу привязывает Flask-SQLAlchemy.
Где спотыкаются
Заводят одну глобальную сессию на всё приложение. В многопоточном сервере это гонки и транзакция, которая никогда не заканчивается.
Не делают rollback после ошибки. Сессия остаётся в сломанной транзакции, и следующие запросы падают с PendingRollbackError.
Путают сессию с соединением и создают новый Engine на каждый запрос, теряя пул.