From: Mike Bayer Date: Fri, 13 May 2022 14:25:37 +0000 (-0400) Subject: more expire_on_commit reminders X-Git-Tag: rel_1_4_37~25 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=3538289c3289f1de6174a2f7a3b842361bc11434;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git more expire_on_commit reminders the session commit/close docs still feel awkward in how one learns about this operation. hopefully another pass over 2.0 can make things more linear. removed a 1.4 note about autobegin that was completely inaccurate; commit() does autobegin so it has an effect, just not usually on the database. Change-Id: Iaa4b96bd3df6cf82e851b2943322ddad7abbbac0 (cherry picked from commit cd628fad7c92f5f54bf1bf6985fd983269b0ec19) --- diff --git a/doc/build/orm/session_basics.rst b/doc/build/orm/session_basics.rst index dd2a868daa..bed901712d 100644 --- a/doc/build/orm/session_basics.rst +++ b/doc/build/orm/session_basics.rst @@ -82,6 +82,18 @@ persisted to the database. If we were only issuing SELECT calls and did not need to write any changes, then the call to :meth:`_orm.Session.commit` would be unnecessary. +.. note:: + + Note that after :meth:`_orm.Session.commit` is called, either explicitly or + when using a context manager, all objects associated with the + :class:`.Session` are :term:`expired`, meaning their contents are erased to + be re-loaded within the next transaction. If these objects are instead + :term:`detached`, they will be non-functional until re-associated with a + new :class:`.Session`, unless the :paramref:`.Session.expire_on_commit` + parameter is used to disable this behavior. See the + section :ref:`session_committing` for more detail. + + .. _session_begin_commit_rollback_block: Framing out a begin / commit / rollback block @@ -771,13 +783,23 @@ Committing ---------- :meth:`~.Session.commit` is used to commit the current -transaction, if any. When there is no transaction in place, the method -passes silently. - -When :meth:`_orm.Session.commit` operates upon the current open transaction, -it first always issues :meth:`~.Session.flush` -beforehand to flush any remaining state to the database; this is independent -of the "autoflush" setting. +transaction. At its core this indicates that it emits ``COMMIT`` on +all current database connections that have a transaction in progress; +from a :term:`DBAPI` perspective this means the ``connection.commit()`` +DBAPI method is invoked on each DBAPI connection. + +When there is no transaction in place for the :class:`.Session`, indicating +that no operations were invoked on this :class:`.Session` since the previous +call to :meth:`.Session.commit`, the method will begin and commit an +internal-only "logical" transaction, that does not normally affect the database +unless pending flush changes were detected, but will still invoke event +handlers and object expiration rules. + +The :meth:`_orm.Session.commit` operation unconditionally issues +:meth:`~.Session.flush` before emitting COMMIT on relevant database +connections. If no pending changes are detected, then no SQL is emitted to the +database. This behavior is not configurable and is not affected by the +:paramref:`.Session.autoflush` parameter. Subsequent to that, :meth:`_orm.Session.commit` will then COMMIT the actual database transaction or transactions, if any, that are in place. @@ -789,15 +811,6 @@ result of a SELECT, they receive the most recent state. This behavior may be controlled by the :paramref:`_orm.Session.expire_on_commit` flag, which may be set to ``False`` when this behavior is undesirable. -.. versionchanged:: 1.4 - - The :class:`_orm.Session` object now features deferred "begin" behavior, as - described in :ref:`autobegin `. If no transaction is - begun, methods like :meth:`_orm.Session.commit` and - :meth:`_orm.Session.rollback` have no effect. This behavior would not - have been observed prior to 1.4 as under non-autocommit mode, a - transaction would always be implicitly present. - .. seealso:: :ref:`session_autobegin` diff --git a/doc/build/tutorial/orm_data_manipulation.rst b/doc/build/tutorial/orm_data_manipulation.rst index 740880567f..1ee5e95fa9 100644 --- a/doc/build/tutorial/orm_data_manipulation.rst +++ b/doc/build/tutorial/orm_data_manipulation.rst @@ -214,6 +214,28 @@ behaviors and features: >>> session.commit() COMMIT +The above operation will commit the transaction that was in progress. The +objects which we've dealt with are still :term:`attached` to the :class:`.Session`, +which is a state they stay in until the :class:`.Session` is closed +(which is introduced at :ref:`tutorial_orm_closing`). + + +.. tip:: + + An important thing to note is that attributes on the objects that we just + worked with have been :term:`expired`, meaning, when we next access any + attributes on them, the :class:`.Session` will start a new transaction and + re-load their state. This option is sometimes problematic for both + performance reasons, or if one wishes to use the objects after closing the + :class:`.Session` (which is known as the :term:`detached` state), as they + will not have any state and will have no :class:`.Session` with which to load + that state, leading to "detached instance" errors. The behavior is + controllable using a parameter called :paramref:`.Session.expire_on_commit`. + More on this is at :ref:`tutorial_orm_closing`. + + + + .. _tutorial_orm_updating: Updating ORM Objects @@ -510,6 +532,7 @@ and of course the database data is present again as well: [...] ('patrick',){stop} True +.. _tutorial_orm_closing: Closing a Session ------------------ diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 315a1254fd..c6a91693e3 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -1401,8 +1401,22 @@ class Session(_SessionClassMethods): def commit(self): """Flush pending changes and commit the current transaction. - If no transaction is in progress, the method will first - "autobegin" a new transaction and commit. + When the COMMIT operation is complete, all objects are fully + :term:`expired`, erasing their internal contents, which will be + automatically re-loaded when the objects are next accessed. In the + interim, these objects are in an expired state and will not function if + they are :term:`detached` from the :class:`.Session`. Additionally, + this re-load operation is not supported when using asyncio-oriented + APIs. The :paramref:`.Session.expire_on_commit` parameter may be used + to disable this behavior. + + When there is no transaction in place for the :class:`.Session`, + indicating that no operations were invoked on this :class:`.Session` + since the previous call to :meth:`.Session.commit`, the method will + begin and commit an internal-only "logical" transaction, that does not + normally affect the database unless pending flush changes were + detected, but will still invoke event handlers and object expiration + rules. If :term:`1.x-style` use is in effect and there are currently SAVEPOINTs in progress via :meth:`_orm.Session.begin_nested`, @@ -1427,6 +1441,8 @@ class Session(_SessionClassMethods): :ref:`unitofwork_transaction` + :ref:`asyncio_orm_avoid_lazyloads` + """ if self._transaction is None: if not self._autobegin():