]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
more expire_on_commit reminders
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 13 May 2022 14:25:37 +0000 (10:25 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 13 May 2022 14:25:37 +0000 (10:25 -0400)
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

doc/build/orm/session_basics.rst
doc/build/tutorial/orm_data_manipulation.rst
lib/sqlalchemy/orm/session.py

index 992e64e31c7796fe6318fcd57ee1d0834ddf02cb..22c391d540872a79ab0c6c17aae37f15723ab3c7 100644 (file)
@@ -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
@@ -774,13 +786,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.
@@ -792,15 +814,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 <session_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`
index 740880567f48f567fcc62d0e49c198e2e8889196..1ee5e95fa95953ecdc8c2d04eadb6a06622c6364 100644 (file)
@@ -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
 ------------------
index 736b140cd33d1ba5b1838c7192fc0e88867171b3..6570e3bace1e1a7b1d8170c0dfdcd0e5a0710986 100644 (file)
@@ -1615,8 +1615,22 @@ class Session(_SessionClassMethods, EventTarget):
     def commit(self) -> None:
         """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.
 
         The outermost database transaction is committed unconditionally,
         automatically releasing any SAVEPOINTs in effect.
@@ -1627,6 +1641,8 @@ class Session(_SessionClassMethods, EventTarget):
 
             :ref:`unitofwork_transaction`
 
+            :ref:`asyncio_orm_avoid_lazyloads`
+
         """
         trans = self._transaction
         if trans is None: