From ad4a0458a75d49658f43ba2acbb1826b59a8025e Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Fri, 8 Apr 2022 10:36:50 -0400 Subject: [PATCH] clarify autoflush setting does not apply to commit Change-Id: Icad0f3bd071422b8d1af204c9a1193a9ce6124ba References: #7916 (cherry picked from commit ed2b29dc344c9cb65745c767b755f82d913695b8) --- doc/build/orm/session_basics.rst | 58 ++++++++++++++++++++------------ lib/sqlalchemy/orm/session.py | 4 +++ 2 files changed, 40 insertions(+), 22 deletions(-) diff --git a/doc/build/orm/session_basics.rst b/doc/build/orm/session_basics.rst index d60db4d73c..8d3ebb735f 100644 --- a/doc/build/orm/session_basics.rst +++ b/doc/build/orm/session_basics.rst @@ -424,39 +424,53 @@ a :term:`2.0-style` :meth:`_orm.Session.execute` call, as well as within the committed. It also occurs before a SAVEPOINT is issued when :meth:`~.Session.begin_nested` is used. -Regardless of the autoflush setting, a flush can always be forced by issuing -:meth:`~.Session.flush`:: +A :class:`.Session` flush can be forced at any time by calling the +:meth:`~.Session.flush` method:: session.flush() -The "flush-on-Query" aspect of the behavior can be disabled by constructing -:class:`.sessionmaker` with the flag ``autoflush=False``:: +The flush which occurs automatically within the scope of +:meth:`_orm.Session.execute`, :class:`_query.Query`, as well as other +:class:`.Session` methods such as :meth:`.Session.merge` (but **not** including +the :meth:`.Session.commit` method) is known as **autoflush**. This "autoflush" +behavior can be disabled by constructing a :class:`.Session` or +:class:`.sessionmaker` passing the :paramref:`.Session.autoflush` parameter as +``False``:: Session = sessionmaker(autoflush=False) -Additionally, autoflush can be temporarily disabled by setting the -``autoflush`` flag at any time:: - - mysession = Session() - mysession.autoflush = False - -More conveniently, it can be turned off within a context managed block using :attr:`.Session.no_autoflush`:: +Additionally, autoflush can be temporarily disabled within the flow +of using a :class:`.Session` using the +:attr:`.Session.no_autoflush` context manager:: with mysession.no_autoflush: mysession.add(some_object) mysession.flush() -The flush process *always* occurs within a transaction, even if the -:class:`~sqlalchemy.orm.session.Session` has been configured with -``autocommit=True``, a setting that disables the session's persistent -transactional state. If no transaction is present, -:meth:`~.Session.flush` creates its own transaction and -commits it. Any failures during flush will always result in a rollback of -whatever transaction is present. If the Session is not in ``autocommit=True`` -mode, an explicit call to :meth:`~.Session.rollback` is -required after a flush fails, even though the underlying transaction will have -been rolled back already - this is so that the overall nesting pattern of -so-called "subtransactions" is consistently maintained. +The flush process **always occurs** when the :meth:`.Session.commit` method is +called, regardless of any "autoflush" settings, when the :class:`.Session` has +remaining pending changes to process. + +The flush process *always* occurs within a transaction, (subject to the +:ref:`isolation level ` of the database +transaction), provided that the DBAPI is not in +:ref:`driver level autocommit ` mode. This includes even if +the :class:`~sqlalchemy.orm.session.Session` has been configured with the +deprecated :paramref:`_orm.Session.autocommit` setting, which disables the +session's persistent transactional state. If no transaction is present, +:meth:`~.Session.flush` creates its own transaction and commits it. This means +that assuming the database connection is providing for :term:`atomicity` within +its transactional settings, if any individual DML statement inside the flush +fails, the entire operation will be rolled back. + +Outside of using :paramref:`_orm.Session.autocommit`, when a failure occurs +within a flush, in order to continue using that same :class:`_orm.Session`, an +explicit call to :meth:`~.Session.rollback` is required after a flush fails, +even though the underlying transaction will have been rolled back already (even +if the database driver is technically in driver-level autocommit mode). This is +so that the overall nesting pattern of so-called "subtransactions" is +consistently maintained. The FAQ section :ref:`faq_session_rollback` contains a +more detailed description of this behavior. .. _session_expiring: diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index d5a80953d6..315a1254fd 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -1003,6 +1003,10 @@ class Session(_SessionClassMethods): :meth:`~.Session.flush` are rarely needed; you usually only need to call :meth:`~.Session.commit` (which flushes) to finalize changes. + .. seealso:: + + :ref:`session_flushing` - additional background on autoflush + :param bind: An optional :class:`_engine.Engine` or :class:`_engine.Connection` to which this ``Session`` should be bound. When specified, all SQL -- 2.47.2