summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-01-15 15:14:24 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2014-01-15 15:14:24 -0500
commit46728df2b48d2a31b44fecf84c912a3b4d7fee8a (patch)
treef39456253930de2114af83cd796886dfa17f8309
parent789ab9174354bd0a6f2cc0de97a724cadd9a6945 (diff)
downloadsqlalchemy-46728df2b48d2a31b44fecf84c912a3b4d7fee8a.tar.gz
- 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
-rw-r--r--doc/build/orm/session.rst47
1 files changed, 44 insertions, 3 deletions
diff --git a/doc/build/orm/session.rst b/doc/build/orm/session.rst
index c4d9b0874..ea811aab4 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