-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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)