diff options
author | Mike Bayer <mike_mp@zzzcomputing.com> | 2023-05-10 10:08:47 -0400 |
---|---|---|
committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2023-05-10 10:08:47 -0400 |
commit | 987285fb4b13c39bcc6b8922e618d9e830577dda (patch) | |
tree | 3d4cc9f89932723252cc58ac6bc8d67a69a45d92 | |
parent | f71c73696ac7a664391513ae4c21a56d18a3c3bf (diff) | |
download | sqlalchemy-987285fb4b13c39bcc6b8922e618d9e830577dda.tar.gz |
note future row change for session.execute()
Fixes: #9761
Change-Id: I555e822b092d047badab86b3a365380121968592
-rw-r--r-- | doc/build/changelog/migration_14.rst | 49 |
1 files changed, 47 insertions, 2 deletions
diff --git a/doc/build/changelog/migration_14.rst b/doc/build/changelog/migration_14.rst index ac6cb6a53..ae93003ae 100644 --- a/doc/build/changelog/migration_14.rst +++ b/doc/build/changelog/migration_14.rst @@ -69,7 +69,6 @@ be encouraged to move to :term:`2.0 style` execution which allows Core construct to be used freely against ORM entities:: with Session(engine, future=True) as sess: - stmt = ( select(User) .where(User.name == "sandy") @@ -105,7 +104,8 @@ Things to note about the above example: * Statements that work with ORM entities and are expected to return ORM results are invoked using :meth:`.orm.Session.execute`. See - :ref:`session_querying_20` for a primer. + :ref:`session_querying_20` for a primer. See also the following note + at :ref:`change_session_execute_result`. * a :class:`_engine.Result` object is returned, rather than a plain list, which itself is a much more sophisticated version of the previous ``ResultProxy`` @@ -153,6 +153,49 @@ for some examples). :ticket:`5159` + +.. _change_session_execute_result: + +ORM ``Session.execute()`` uses "future" style ``Result`` sets in all cases +-------------------------------------------------------------------------- + +As noted in :ref:`change_4710_core`, the :class:`_engine.Result` and +:class:`_engine.Row` objects now feature "named tuple" behavior, when used with +an :class:`_engine.Engine` that includes the +:paramref:`_sa.create_engine.future` parameter set to ``True``. These +"named tuple" rows in particular include a behavioral change which is that +Python containment expressions using ``in``, such as:: + + >>> engine = create_engine("...", future=True) + >>> conn = engine.connect() + >>> row = conn.execute.first() + >>> "name" in row + True + +The above containment test will +use **value containment**, not **key containment**; the ``row`` would need to +have a **value** of "name" to return ``True``. + +Under SQLAlchemy 1.4, when :paramref:`_sa.create_engine.future` parameter set +to ``False``, legacy-style ``LegacyRow`` objects are returned which feature the +partial-named-tuple behavior of prior SQLAlchemy versions, where containment +checks continue to use key containment; ``"name" in row`` would return +True if the row had a **column** named "name", rather than a value. + +When using :meth:`_orm.Session.execute`, full named-tuple style is enabled +**unconditionally**, meaning ``"name" in row`` will use **value containment** +as the test, and **not** key containment. This is to accommodate that +:meth:`_orm.Session.execute` now returns a :class:`_engine.Result` that also +accommodates for ORM results, where even legacy ORM result rows such as those +returned by :meth:`_orm.Query.all` use value containment. + +This is a behavioral change from SQLAlchemy 1.3 to 1.4. To continue receiving +key-containment collections, use the :meth:`_engine.Result.mappings` method to +receive a :class:`_engine.MappingResult` that returns rows as dictionaries:: + + for dict_row in session.execute(text("select id from table")).mappings(): + assert "id" in dict_row + .. _change_4639: Transparent SQL Compilation Caching added to All DQL, DML Statements in Core, ORM @@ -1509,6 +1552,8 @@ There are many reasons why the above assumptions do not hold: :ref:`change_4710_orm` + :ref:`change_session_execute_result` + :ticket:`4710` .. _change_4753: |