8000 UniqueSession · sqlalchemy/sqlalchemy Wiki · GitHub
[go: up one dir, main page]

Skip to content

UniqueSession

Mike Bayer edited this page Feb 13, 2014 · 1 revision

DistinctSession

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)
Clone this wiki locally
0