From: Mike Bayer Date: Wed, 31 Mar 2021 13:39:34 +0000 (-0400) Subject: document rowcount for ORM update/delete X-Git-Tag: rel_1_4_5~17 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=42185a1f3866bfc8ebffa2a6c724face8a2db5dc;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git document rowcount for ORM update/delete Change-Id: I16f50cb50fc3cccc1bd7cae3a64a085b1ea68612 --- diff --git a/doc/build/orm/session_basics.rst b/doc/build/orm/session_basics.rst index 0ea797fa95..a5620ef6ec 100644 --- a/doc/build/orm/session_basics.rst +++ b/doc/build/orm/session_basics.rst @@ -559,11 +559,17 @@ Core :class:`_sql.Update` construct:: stmt = update(User).where(User.name == "squidward").values(name="spongebob").\ execution_options(synchronize_session="fetch") - session.execute(stmt) + result = session.execute(stmt) Above, the :meth:`_dml.Update.execution_options` method may be used to establish execution-time options such as "synchronize_session". +The result object returned is an instance of :class:`_result.CursorResult`; to +retrieve the number of rows matched by any UPDATE or DELETE statement, use +:attr:`_result.CursorResult.rowcount`:: + + num_rows_matched = result.rowcount + DELETEs work in the same way as UPDATE except there is no "values / set" clause established. When synchronize_session is used, matching objects within the :class:`_orm.Session` will be marked as deleted and expunged. diff --git a/doc/build/tutorial/data.rst b/doc/build/tutorial/data.rst index a658a7db79..f5009df581 100644 --- a/doc/build/tutorial/data.rst +++ b/doc/build/tutorial/data.rst @@ -1933,6 +1933,8 @@ syntaxes, such as ``DELETE FROM..USING`` on MySQL:: {opensql}DELETE FROM user_account USING user_account, address WHERE user_account.id = address.user_id AND address.email_address = %s +.. _tutorial_update_delete_rowcount: + Getting Affected Row Count from UPDATE, DELETE ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/lib/sqlalchemy/engine/cursor.py b/lib/sqlalchemy/engine/cursor.py index 5a3b929018..24a7cdb803 100644 --- a/lib/sqlalchemy/engine/cursor.py +++ b/lib/sqlalchemy/engine/cursor.py @@ -1620,7 +1620,12 @@ class BaseCursorResult(object): * Statements that use RETURNING may not return a correct rowcount. - """ + .. seealso:: + + :ref:`tutorial_update_delete_rowcount` - in the :ref:`unified_tutorial` + + """ # noqa E501 + try: return self.context.rowcount except BaseException as e: diff --git a/test/orm/test_update_delete.py b/test/orm/test_update_delete.py index c9248b1189..7586a1e2e5 100644 --- a/test/orm/test_update_delete.py +++ b/test/orm/test_update_delete.py @@ -618,25 +618,27 @@ class UpdateDeleteTest(fixtures.MappedTest): sess.execute(select(User).order_by(User.id)).scalars().all() ) - sess.execute( + result = sess.execute( update(User) .where(User.age > 29) .values({"age": User.age - 10}) .execution_options(synchronize_session="evaluate"), ) + eq_(result.rowcount, 2) eq_([john.age, jack.age, jill.age, jane.age], [25, 37, 29, 27]) eq_( sess.execute(select(User.age).order_by(User.id)).all(), list(zip([25, 37, 29, 27])), ) - sess.execute( + result = sess.execute( update(User) .where(User.age > 29) .values({User.age: User.age - 10}) - .execution_options(synchronize_session="evaluate") + .execution_options(synchronize_session="fetch") ) + eq_(result.rowcount, 1) eq_([john.age, jack.age, jill.age, jane.age], [25, 27, 29, 27]) eq_( sess.query(User.age).order_by(User.id).all(), @@ -874,10 +876,11 @@ class UpdateDeleteTest(fixtures.MappedTest): .where(User.age > 29) .values({"age": User.age - 10}) ) - sess.execute( + result = sess.execute( stmt, execution_options={"synchronize_session": "fetch"} ) + eq_(result.rowcount, 2) # these are simple values, these are now evaluated even with # the "fetch" strategy, new in 1.4, so there is no expiry eq_([john.age, jack.age, jill.age, jane.age], [25, 37, 29, 27])