session = Session(bind=engine)
-...or directly with a :class:`.Connection`. This is useful in some situations,
-such as within a test fixture that maintains an external transaction::
+...or directly with a :class:`.Connection`::
- from sqlalchemy.orm import sessionmaker
- from sqlalchemy import create_engine
- from unittest import TestCase
-
- # global application scope. create Session class, engine
- Session = sessionmaker()
-
- engine = create_engine('postgresql://...')
+ conn = engine.connect()
+ session = Session(bind=conn)
- 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)
-
- def test_something(self):
- # use the session in tests.
-
- self.session.add(Foo())
- self.session.commit()
-
- def tearDown(self):
- # rollback - everything that happened with the
- # Session above (including calls to commit())
- # is rolled back.
- self.trans.rollback()
- self.session.close()
-
+While the rationale for the above example may not be apparent, the typical
+usage is in a test fixture that maintains an external transaction - see
+:ref:`session_external_transaction` below for a full example.
Using the Session
==================
connection = session.connection(MyMappedClass)
+.. _session_external_transaction:
+
Joining a Session into an External Transaction
===============================================
-If a :class:`~sqlalchemy.engine.base.Connection` is being used which is
-already in a transactional state (i.e. has a
-:class:`~sqlalchemy.engine.base.Transaction`), a
-:class:`~sqlalchemy.orm.session.Session` can be made to participate within
-that transaction by just binding the :class:`~sqlalchemy.orm.session.Session`
-to that :class:`~sqlalchemy.engine.base.Connection`::
+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
+be made to participate within that transaction by just binding the
+:class:`.Session` to that :class:`.Connection`. The usual rationale for this
+is a test suite that allows ORM code to work freely with a :class:`.Session`,
+including the ability to call :meth:`.Session.commit`, where afterwards the
+entire database interaction is rolled back::
+ from sqlalchemy.orm import sessionmaker
+ from sqlalchemy import create_engine
+ from unittest import TestCase
+
+ # global application scope. create Session class, engine
Session = sessionmaker()
- # non-ORM connection + transaction
- conn = engine.connect()
- trans = conn.begin()
-
- # create a Session, bind to the connection
- session = Session(bind=conn)
-
- # ... work with session
-
- session.commit() # commit the session
- session.close() # close it out, prohibit further actions
-
- trans.commit() # commit the actual transaction
-
-Note that above, we issue a ``commit()`` both on the
-:class:`~sqlalchemy.orm.session.Session` as well as the
-:class:`~sqlalchemy.engine.base.Transaction`. This is an example of where we
-take advantage of :class:`~sqlalchemy.engine.base.Connection`'s ability to
-maintain *subtransactions*, or nested begin/commit pairs. The
-:class:`~sqlalchemy.orm.session.Session` is used exactly as though it were
-managing the transaction on its own; its
-:func:`~sqlalchemy.orm.session.Session.commit` method issues its
-:func:`~sqlalchemy.orm.session.Session.flush`, and commits the subtransaction.
-The subsequent transaction the :class:`~sqlalchemy.orm.session.Session` starts
-after commit will not begin until it's next used. Above we issue a
-:func:`~sqlalchemy.orm.session.Session.close` to prevent this from occurring.
-Finally, the actual transaction is committed using ``Transaction.commit()``.
-
-When using the ``threadlocal`` engine context, the process above is
-simplified; the :class:`~sqlalchemy.orm.session.Session` uses the same
-connection/transaction as everyone else in the current thread, whether or not
-you explicitly bind it::
-
- engine = create_engine('postgresql://mydb', strategy="threadlocal")
- engine.begin()
-
- session = Session() # session takes place in the transaction like everyone else
+ engine = create_engine('postgresql://...')
- # ... go nuts
+ class SomeTest(TestCase):
+ def setUp(self):
+ # connect to the database
+ self.connection = engine.connect()
- engine.commit() # commit the transaction
+ # begin a non-ORM transaction
+ self.trans = connection.begin()
+
+ # bind an individual Session to the connection
+ self.session = Session(bind=self.connection)
+
+ def test_something(self):
+ # use the session in tests.
+
+ self.session.add(Foo())
+ self.session.commit()
+
+ def tearDown(self):
+ # rollback - everything that happened with the
+ # Session above (including calls to commit())
+ # is rolled back.
+ self.trans.rollback()
+ self.session.close()
+
+Above, we issue :meth:`.Session.commit` as well as
+:meth:`.Transaction.rollback`. This is an example of where we take advantage
+of the :class:`.Connection` object's ability to maintain *subtransactions*, or
+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.
The :class:`.Session` object and :func:`.sessionmaker` function
================================================================
.. autoclass:: sqlalchemy.orm.scoping.ScopedSession
:members:
+.. autoclass:: sqlalchemy.util.ScopedRegistry
+ :members:
+
+.. autoclass:: sqlalchemy.util.ThreadLocalRegistry
+
.. _session_partitioning:
Partitioning Strategies
class Transaction(object):
"""Represent a Transaction in progress.
+ The object provides :meth:`.rollback` and :meth:`.commit`
+ methods in order to control transaction boundaries. It
+ also implements a context manager interface so that
+ the Python ``with`` statement can be used with the
+ :meth:`.Connection.begin` method.
+
The Transaction object is **not** threadsafe.
.. index::
"""
def __init__(self, connection, parent):
+ """The constructor for :class:`.Transaction` is private
+ and is called from within the :class:`.Connection.begin`
+ implementation.
+
+ """
self.connection = connection
self._parent = parent or self
self.is_active = True
def close(self):
- """Close this transaction.
+ """Close this :class:`.Transaction`.
If this transaction is the base transaction in a begin/commit
nesting, the transaction will rollback(). Otherwise, the
This is used to cancel a Transaction without affecting the scope of
an enclosing transaction.
+
"""
if not self._parent.is_active:
return
self.rollback()
def rollback(self):
+ """Roll back this :class:`.Transaction`.
+
+ """
if not self._parent.is_active:
return
self._do_rollback()
self._parent.rollback()
def commit(self):
+ """Commit this :class:`.Transaction`."""
+
if not self._parent.is_active:
raise exc.InvalidRequestError("This transaction is inactive")
self._do_commit()
class ScopedRegistry(object):
"""A Registry that can store one or multiple instances of a single
- class on a per-thread scoped basis, or on a customized scope.
+ class on the basis of a "scope" function.
+
+ The object implements ``__call__`` as the "getter", so by
+ calling ``myregistry()`` the contained object is returned
+ for the current scope.
- createfunc
+ :param createfunc:
a callable that returns a new object to be placed in the registry
- scopefunc
+ :param scopefunc:
a callable that will return a key to store/retrieve an object.
"""
def __init__(self, createfunc, scopefunc):
+ """Construct a new :class:`.ScopedRegistry`.
+
+ :param createfunc: A creation function that will generate
+ a new value for the current scope, if none is present.
+
+ :param scopefunc: A function that returns a hashable
+ token representing the current scope (such as, current
+ thread identifier).
+
+ """
self.createfunc = createfunc
self.scopefunc = scopefunc
self.registry = {}
return self.registry.setdefault(key, self.createfunc())
def has(self):
+ """Return True if an object is present in the current scope."""
+
return self.scopefunc() in self.registry
def set(self, obj):
+ """Set the value forthe current scope."""
+
self.registry[self.scopefunc()] = obj
def clear(self):
+ """Clear the current scope, if any."""
+
try:
del self.registry[self.scopefunc()]
except KeyError:
pass
class ThreadLocalRegistry(ScopedRegistry):
+ """A :class:`.ScopedRegistry` that uses a ``threading.local()``
+ variable for storage.
+
+ """
def __init__(self, createfunc):
self.createfunc = createfunc
self.registry = threading.local()