SQLAlchemy/Sessions
Для чего нужны сессии?
правитьВ наиболее глобальном смысле, сессии налаживают весь обмен данными с базой данных и предоставляют «зону проведения» (holding zone) для всех объектов, которые вы загрузили или связываете с базой(?) в течение жизненного цикла. Это обеспечивает конечную точку входа для «объектов-запросов» (query object) - каждый объект передаёт запросы в базу данных используя объект Session текущего соединения с базой данных, заполняя результирующие строки объектов, хранящихся в сессии, внутри структуры, называемой картой идентичности (Identity Map) — структура данных, которая поддерживает уникальную копию каждого объекта, где под уникальностью подразумевают «только один объект с конкретным первичным ключом».
Получение сессии
правитьSession — регулярный питоний класс, экземпляр которого вы можете создать напрямую. Однако, существует стандарт для получения и настройки сессии при помощи класса sessionmaker, обычно использующегося для создания высокоуровневых сессионных конфигураций, каждая из которых может быть использована в приложении без необходимости повторной настройки.
Использование класса sessionmaker проиллюстрировано ниже
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
# объект Engine, который объект Session будет использован для
# соединения с ресурсами
some_engine = create_engine('postgresql://scott:tiger@localhost/')
# создание конфигурации класса Session
Session = sessionmaker(bind=some_engine)
# создание объекта Session
session = Session()
# работа с объектом Session
myobject = MyObject('foo', 'bar')
session.add(myobject)
session.commit()
В примере выше sessionmaker вызовет для нас создание фабрики, которой мы припишем имя Session. Эта фабрика при вызове создаст новый объект session, используя конфигурационные аргументы, которые мы ей передали. Обычно мы настраиваем фабрику, передавая ей Engine для соединения с ресурсами.
Обычная установка ассоциирует sessionmaker с engine, так, что каждая сессия генерируется с использованием этого engine для получения присоединённых ресурсов. Ассоциация может быть настроена как в примере сверху, используя аргумент bind.
Когда вы пишете своё приложение, sessionmaker фабрика находится на глобальном уровне. Эта фабрика может быть использована другими приложениями как ресурс для новых экземпляров сессии, держа в себе настройки о том, как объект Session должен быть построен в этом месте.
Добавление дополнительной конфигурации в существующий объект sessionmaker
правитьВ общем случае, где sessionmaker вызывается в модуле, в момент импорта, однако генерация одного или более экземпляров engine и связь с sessionmaker ещё не наступила, sessionmaker предлагает метод sessionmaker.configure, который будет размещать дополнительные директивы конфигурации в существующий экземпляр sessionmaker, который будет вызван при использовании конструкции, описанной ниже:
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
# настройка класса Session c требуемыми настройками configure Session class with desired options
Session = sessionmaker()
# Затем, создаём engine
engine = create_engine('postgresql://...')
# связываем его с нашим классом Session
Session.configure(bind=engine)
# работаем с сессией
session = Session()
Создание Ad-hoc объектов Session с альтернативными аргументами
правитьИспользование сессий
правитьБыстрое знакомство с состояниями объектов
правитьПолезно знать состояния, которые могут иметь объекты в сессии:
Переходной - объект не находится в сессии и не сохранён в базе данных, т.е. он не имеет идентификатора в базе данных. Единственная связь объекта к ORM - то, что он имеет связь, установленную через mapper()
Ожидающий - когда мы добавляем объект из предыдущего состояния к сессии, используя метод add(), он переходит в состояние ожидания. Ещё не произошёл сброс текущих данных в базу данных, но он произойдет в следующий вызов сброса данных.
Хранимый - в настоящий момент объект находится в сессии и имеет запись в базе данных. Мы получаем хранимый объект несколькими путями: либо сбрасывая данные в базу данных (flush), либо обращаясь к базе данных за объектом, который уже хранится в ней (или перемещая хранимый объект из какой-либо другой сессии внутрь нашей локальной сессии)
Отсоединенный - объект, имеющий запись в базу данных, но не содержащийся в какой-либо сессии. Здесь нет ничего неправильного, и вы можете использовать этот объект как обычно, пока он в отсоединенном состоянии, кроме того что он недоступен для создания любых SQL запросов для загрузки коллекций или атрибутов, которые ещё не были загружены, либо были помечены как устаревшие "expired"
Знание этих состояний важно, так как сессия пытается быть чёткой в неоднозначных ситуациях (например, пытаясь сохранить один и тот же объект, находящийся в двух различных сессиях одновременно)
Часто задаваемые вопросы по сессиям
правитьКогда мне стоит вызвать sessionmaker?
правитьЕдиножды, пока ваше приложение находится на глобальном уровне. Следует рассматривать это как часть конфигурации вашего приложения. Если ваше приложение имеет несколько .py файлов в пакете, вы должны, например, поместить строку с sessionmaker в ваш __init__.py файл с той точки зрения, что вашим остальным модулям скажут "импортировать сессию из моего пакета". Такой подход позволяет добиться того, что все остальные модули просто используют Session(), и контроль за этой сессией будет осуществляться из одной точки.
Если ваше приложение, в момент запуска делает импорт, но не знает, какая база данных будет подключена, вы можете сделать привязку сессии на уровне "класса" с engine, используя sessionmaker.configure().
В примерах, относящихся к этой части статьи мы будем часто показывать как sessionmaker создаётся прямо над линией кода, где мы, фактически обращаемся к сессии к Сессии. Но это только ради примера! В реальности, sessionmaker находится где-то на уровне модуля. Вызов создания экземпляра сессии должны быть размещены в точке начала обмена данными с базой данных.
Когда я создам Сессию, когда следует коммитить(?), и когда следует её закрыть?
правитьСессия, как правило создаётся в начале логических операций, где ожидаем доступ к базе данных.
Сессия, всякий раз когда она используется для обмена информацией с базой данных, начинает транзакцию с базой данных, как только обмен информацией был начат. Предполагая, что флаг autocommit, как рекомендовано, имеет дефолтное значение False, эта транзакция остаётся в процессе выполнения до того момента, пока сессия не откатилась, закоммитилась или закрылась. Сессия начнёт новую транзакцию, если будет использована повторно, после конца предыдущей транзакции. Из этого следует, что Сессия способна иметь срок жизни во многих операциях, но используется только один раз. Мы называем эти понятия как область транзакции и объём сеанса.
Подразумевается, что SQLAlchemy ORM подталкивает разработчика к установке этих двух областей для применения, в том числе, не только в пределах начала и конца, но также в пространстве между этими моментами. Например, если должен быть один локальный экземпляр сессии для потока выполнения в функции или методе, то она должна быть глобальным объектом, используемым для всего приложения(?)
Ответственность за определение этой области видимости ложится на разработчика, где SQLAlchemy ORM имеет чёткую позицию о том, как должна быть использована база данных. Паттерн unit of work, в частности, является одним из накопителей изменений с течением времени и периодически сбрасывает (flush) их в базу данных, сохраняя в памяти состояние синхронно с тем, (? что сейчас известно и) присутствует в локальной транзакции. Эта модель является эффективной только тогда, когда значимые объемы транзакций находятся на своих местах.
Обычно, не трудно определить лучшие точки, в которых следует определены начало и конец зоны видимости сессии, хотя широкое разнообразие архитектур во всевозможных приложениях может вызвать осложнения.
Хотя нет "одно-решение-подходит-всем" (one-size-fits-all) о том, какая область транзакции должна быть определена, есть общие образцы (паттерны). Особенно, это касается момента, когда один пишет веб-приложение. В этом случае выбор, в значительной степени определён.
Веб-приложения является самым простым случаем, потому что такие приложения уже построены вокруг единого, в соответствии области - этот запрос, который предоставляет входящий запрос из браузера, обрабатывает этот запрос и генерируется ответ на запрос, и наконец, ответ доставляется обратно клиенту. Интеграция веб-приложений является простой задачей связки области видимости, что и запрос.