-
-
Notifications
You must be signed in to change notification settings - Fork 1.5k
UniqueSession
The use case is typically when using scoped_session()
, to temporarily use an entirely different Session
which replaces the current thread's session, then restores it when the operation is complete. A key place this technique is necessary is when constructing an after_commit()
extension which wishes to use a Session itself, and needs to call upon functionality that already assumes the presence of a global thread-local scoped session.
#!python
Session = scoped_session(sessionmaker())
class distinct_session(object):
def __enter__(self):
self.existing_session = Session()
new_session = Session.session_factory()
Session.registry.set(new_session)
def __exit__(self, type, value, traceback):
Session().close()
Session.registry.set(self.existing_session)
if __name__ == '__main__':
with distinct_session():
print Session.query("select 1")
Above, the distinct_session
object calls up a new Session
using the scoped session's session_factory
callable, sets it within the scoped session's registry then swaps it out when operations are complete.
When using the after_commit()
method of a SessionExtension
, any usage of the Session
within the method raises an error. This may appear unintuitive at first, but in fact if one attempts to use the session within after_commit()
, this implies that you must start a new transaction - since the current one is gone. SessionExtension
currently doesn't have such a hook, since it would lead to the misleading result of a new transaction starting up, then left hanging once Session.commit()
is complete - it would cause breakage of a fundamental SQLA contract that Session.commit()
cleans up all resources used. It also makes it very difficult for calling code to catch exceptions within commit()
, then issue rollback()
- since again you might be in a new transaction, and you're rolling back something completely different !
So the distinct session recipe is essential for an after_commit()
method that really needs further database access:
#!python
class MyExtension(SessionExtension):
def after_commit(self):
"""Send email messages for all Stuff, after a commit
has succeeded and we are certain the new data is available.
"""
with distinct_session():
for stuff in Session.query(Stuff).filter(...):
send_email(stuff)