From: Mike Bayer Date: Thu, 28 Sep 2017 15:43:48 +0000 (-0400) Subject: - add more dragons to session.begin() / autocommit docs X-Git-Tag: rel_1_1_15~25 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=124c14c6e47fc316c7a404ed514e137c32de7bf7;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - add more dragons to session.begin() / autocommit docs Change-Id: I9e326f353d396321565dfbf53b7a30f18d8c86e9 (cherry picked from commit 1ec7ba52e1c7e6d232223912f154e25252fb9b92) --- diff --git a/doc/build/orm/session_transaction.rst b/doc/build/orm/session_transaction.rst index 955ec02f6e..bd06286a6e 100644 --- a/doc/build/orm/session_transaction.rst +++ b/doc/build/orm/session_transaction.rst @@ -81,7 +81,7 @@ The example below illustrates this lifecycle:: # state. Neither of these steps are usually essential. # However, if the commit() or rollback() itself experienced # an unanticipated internal failure (such as due to a mis-behaved - # user-defined event handler), .close() will ensure that + # user-defined event handler), .close() will ensure that # invalid state is removed. session.close() @@ -141,39 +141,45 @@ things like unique constraint exceptions:: Autocommit Mode --------------- -The example of :class:`.Session` transaction lifecycle illustrated at -the start of :ref:`unitofwork_transaction` applies to a :class:`.Session` configured in the -default mode of ``autocommit=False``. Constructing a :class:`.Session` -with ``autocommit=True`` produces a :class:`.Session` placed into "autocommit" mode, where each SQL statement -invoked by a :meth:`.Session.query` or :meth:`.Session.execute` occurs -using a new connection from the connection pool, discarding it after -results have been iterated. The :meth:`.Session.flush` operation -still occurs within the scope of a single transaction, though this transaction -is closed out after the :meth:`.Session.flush` operation completes. +The examples of session lifecycle at :ref:`unitofwork_transaction` refer +to a :class:`.Session` that runs in its default mode of ``autocommit=False``. +In this mode, the :class:`.Session` begins new transactions automatically +as soon as it needs to do work upon a database connection; the transaction +then stays in progress until the :meth:`.Session.commit` or :meth:`.Session.rollback` +methods are called. + +The :class:`.Session` also features an older legacy mode of use called +**autocommit mode**, where a transaction is not started implicitly, and unless +the the :meth:`.Session.begin` method is invoked, the :class:`.Session` will +perform each database operation on a new connection checked out from the +connection pool, which is then released back to the pool immediately +after the operation completes. This refers to +methods like :meth:`.Session.execute` as well as when executing a query +returned by :meth:`.Session.query`. For a flush operation, the :class:`.Session` +starts a new transaction for the duration of the flush, and commits it when +complete. .. warning:: - "autocommit" mode should **not be considered for general use**. - If used, it should always be combined with the usage of - :meth:`.Session.begin` and :meth:`.Session.commit`, to ensure - a transaction demarcation. - - Executing queries outside of a demarcated transaction is a legacy mode - of usage, and can in some cases lead to concurrent connection - checkouts. - - In the absence of a demarcated transaction, the :class:`.Session` - cannot make appropriate decisions as to when autoflush should - occur nor when auto-expiration should occur, so these features - should be disabled with ``autoflush=False, expire_on_commit=False``. - -Modern usage of "autocommit" is for framework integrations that need to control -specifically when the "begin" state occurs. A session which is configured with -``autocommit=True`` may be placed into the "begin" state using the -:meth:`.Session.begin` method. -After the cycle completes upon :meth:`.Session.commit` or :meth:`.Session.rollback`, -connection and transaction resources are :term:`released` and the :class:`.Session` -goes back into "autocommit" mode, until :meth:`.Session.begin` is called again:: + "autocommit" mode is a **legacy mode of use** and should not be + considered for new projects. If autocommit mode is used, it is strongly + advised that the application at least ensure that tranasction scope + is made present via the :meth:`.Session.begin` method, rather than + using the session in pure autocommit mode. + + If the :meth:`.Session.begin` method is not used, and operations are allowed + to proceed using ad-hoc connections with immediate autocommit, then the + application probably should set ``autoflush=False, expire_on_commit=False``, + since these features are intended to be used only within the context + of a database transaction. + +Modern usage of "autocommit mode" tends to be for framework integrations that +wish to control specifically when the "begin" state occurs. A session which is +configured with ``autocommit=True`` may be placed into the "begin" state using +the :meth:`.Session.begin` method. After the cycle completes upon +:meth:`.Session.commit` or :meth:`.Session.rollback`, connection and +transaction resources are :term:`released` and the :class:`.Session` goes back +into "autocommit" mode, until :meth:`.Session.begin` is called again:: Session = sessionmaker(bind=engine, autocommit=True) session = Session() @@ -189,7 +195,7 @@ goes back into "autocommit" mode, until :meth:`.Session.begin` is called again:: raise The :meth:`.Session.begin` method also returns a transactional token which is -compatible with the Python 2.6 ``with`` statement:: +compatible with the ``with`` statement:: Session = sessionmaker(bind=engine, autocommit=True) session = Session() diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 2c040561b0..b79c435dd9 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -778,20 +778,35 @@ class Session(_SessionClassMethods): def begin(self, subtransactions=False, nested=False): """Begin a transaction on this :class:`.Session`. - The :meth:`.Session.begin` method is only - meaningful if this session is in **autocommit mode** prior to - it being called; see :ref:`session_autocommit` for background - on this setting. + .. warning:: - The method will raise an error if this :class:`.Session` - is already inside of a transaction, unless - :paramref:`.Session.begin.subtransactions` or - :paramref:`.Session.begin.nested` are specified. + The :meth:`.Session.begin` method is part of a larger pattern + of use with the :class:`.Session` known as **autocommit mode**. + This is essentially a **legacy mode of use** and is + not necessary for new applications. The :class:`.Session` + normally handles the work of "begin" transparently, which in + turn relies upon the Python DBAPI to transparently "begin" + transactions; there is **no need to explcitly begin transactions** + when using modern :class:`.Session` programming patterns. + In its default mode of ``autocommit=False``, the + :class:`.Session` does all of its work within + the context of a transaction, so as soon as you call + :meth:`.Session.commit`, the next transaction is implicitly + started when the next database operation is invoked. See + :ref:`session_autocommit` for further background. + + The method will raise an error if this :class:`.Session` is already + inside of a transaction, unless + :paramref:`~.Session.begin.subtransactions` or + :paramref:`~.Session.begin.nested` are specified. A "subtransaction" + is essentially a code embedding pattern that does not affect the + transactional state of the database connection unless a rollback is + emitted, in which case the whole transaction is rolled back. For + documentation on subtransactions, please see + :ref:`session_subtransactions`. :param subtransactions: if True, indicates that this - :meth:`~.Session.begin` can create a subtransaction if a transaction - is already in progress. For documentation on subtransactions, please - see :ref:`session_subtransactions`. + :meth:`~.Session.begin` can create a "subtransaction". :param nested: if True, begins a SAVEPOINT transaction and is equivalent to calling :meth:`~.Session.begin_nested`. For documentation on