From: Mike Bayer Date: Wed, 15 Jan 2014 20:14:24 +0000 (-0500) Subject: - should close the session before rolling back the transaction X-Git-Tag: rel_0_9_2~57 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=46728df2b48d2a31b44fecf84c912a3b4d7fee8a;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - should close the session before rolling back the transaction - make section title actually say, "such as for test suites" - add topic section detailing an evented approach to allowing ROLLBACK by using savepoint --- diff --git a/doc/build/orm/session.rst b/doc/build/orm/session.rst index c4d9b08742..ea811aab4f 100644 --- a/doc/build/orm/session.rst +++ b/doc/build/orm/session.rst @@ -1648,8 +1648,8 @@ proper context for the desired engine:: .. _session_external_transaction: -Joining a Session into an External Transaction -=============================================== +Joining a Session into an External Transaction (such as for test suites) +======================================================================== If a :class:`.Connection` is being used which is already in a transactional state (i.e. has a :class:`.Transaction` established), a :class:`.Session` can @@ -1686,11 +1686,12 @@ entire database interaction is rolled back:: self.session.commit() def tearDown(self): + self.session.close() + # rollback - everything that happened with the # Session above (including calls to commit()) # is rolled back. self.trans.rollback() - self.session.close() # return connection to the Engine self.connection.close() @@ -1702,6 +1703,46 @@ nested begin/commit-or-rollback pairs where only the outermost begin/commit pair actually commits the transaction, or if the outermost block rolls back, everything is rolled back. +.. topic:: Supporting Tests with Rollbacks + + The above recipe works well for any kind of database enabled test, except + for a test that needs to actually invoke :meth:`.Session.rollback` within + the scope of the test itself. The above recipe can be expanded, such + that the :class:`.Session` always runs all operations within the scope + of a SAVEPOINT, which is established at the start of each transaction, + so that tests can also rollback the "transaction" as well while still + remaining in the scope of a larger "transaction" that's never committed, + using two extra events:: + + from sqlalchemy import event + + class SomeTest(TestCase): + def setUp(self): + # connect to the database + self.connection = engine.connect() + + # begin a non-ORM transaction + self.trans = connection.begin() + + # bind an individual Session to the connection + self.session = Session(bind=self.connection) + + # two events make sure a SAVEPOINT is always started + # for this session. After the initial "begin"... + @event.listens_for(self.session, "after_begin") + def start_savepoint(session, transaction, connection): + if not transaction.nested: + session.begin_nested() + + # ... and after the end of each "transaction", assuming + # the transaction ending was the "nested" transaction + @event.listens_for(self.session, "after_transaction_end") + def restart_savepoint(session, transaction): + if transaction.nested and not transaction._parent.nested: + session.begin_nested() + + # ... the tearDown() method stays the same + .. _unitofwork_contextual: Contextual/Thread-local Sessions