]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Add summary table for orm 2.0 migration
authorFederico Caselli <cfederico87@gmail.com>
Tue, 10 Nov 2020 22:13:55 +0000 (23:13 +0100)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 22 Nov 2020 19:44:06 +0000 (14:44 -0500)
A new formatting option is added to zzzeeksphinx
to allow the table to slide for horizontal overflow
without blowing up the page.   overall though trying
to keep the table narrow as the code examples are side by
side, other configurations didn't work as well.

Change-Id: Ia2c6cf8b81dd2bf9989a0fcbec4ce6ece08c568f

doc/build/changelog/migration_20.rst
doc/build/orm/queryguide.rst

index eadabd7f0ee83aee13ab845424a8b87f3807851e..208beaa4f597452bea6c1005fed05544b158c0f9 100644 (file)
@@ -1148,6 +1148,167 @@ declarative decorator and classical mapping forms.
 2.0 Migration - ORM Usage
 =============================================
 
+The biggest visible change in SQLAlchemy 2.0 is the use of
+:meth:`_orm.Session.execute` in conjunction with :func:`_sql.select` to run ORM
+queries, instead of using :meth:`_orm.Session.query`.  As mentioned elsewhere,
+there is no plan to actually remove the :meth:`_orm.Session.query` API itself,
+as it is now implemented by using the new API internally it will remain as a
+legacy API, and both APIs can be used freely.
+
+The table below provides an introduction to the general change in
+calling form with links to documentation for each technique
+presented.  The individual migration notes are in the embedded sections
+following the table, and may include additional notes not summarized here.
+
+
+.. container:: sliding-table
+
+  .. list-table:: **Overview of Major ORM Querying Patterns**
+    :header-rows: 1
+
+    * - :term:`1.x style` form
+      - :term:`2.0 style` form
+      - See Also
+
+    * - ::
+
+          session.query(User).get(42)
+
+      - ::
+
+          session.get(User, 42)
+
+      - :ref:`migration_20_get_to_session`
+
+    * - ::
+
+          session.query(User).all()
+
+      - ::
+
+          session.execute(
+              select(User)
+          ).scalars().all()
+
+      - :ref:`migration_20_unify_select`
+
+        :meth:`_engine.Result.scalars`
+
+    * - ::
+
+          session.query(User).\
+          filter_by(name='some user').one()
+
+      - ::
+
+          session.execute(
+              select(User).
+              filter_by(name="some user")
+          ).scalar_one()
+
+      - :ref:`migration_20_unify_select`
+
+        :meth:`_engine.Result.scalar_one`
+
+    * - ::
+
+            session.query(User).options(
+                joinedload(User.addresses)
+            ).all()
+
+      - ::
+
+            session.execute(
+                select(User).
+                options(
+                  joinedload(User.addresses)
+                )
+            ).unique().all()
+
+      - :ref:`joinedload_not_uniqued`
+
+    * - ::
+
+          session.query(User).\
+              join(Address).\
+              filter(Address.email == 'e@sa.us').\
+              all()
+
+      - ::
+
+          session.execute(
+              select(User).
+              join(Address).
+              where(Address.email == 'e@sa.us')
+          ).scalars().all()
+
+      - :ref:`migration_20_unify_select`
+
+        :ref:`orm_queryguide_joins`
+
+    * - ::
+
+          session.query(User).from_statement(
+              text("select * from users")
+          ).all()
+
+      - ::
+
+          session.execute(
+              select(User).
+              from_statement(
+                  text("select * from users")
+              )
+          ).scalars().all()
+
+      - :ref:`orm_queryguide_selecting_text`
+
+    * - ::
+
+          session.query(User).\
+              join(User.addresses).\
+              options(
+                contains_eager(User.addresses)
+              ).\
+              populate_existing().all()
+
+      - ::
+
+          session.execute(
+              select(User).
+              join(User.addresses).
+              options(contains_eager(User.addresses)).
+              execution_options(populate_existing=True)
+          ).scalars().all()
+
+      -
+
+          :ref:`orm_queryguide_execution_options`
+
+          :ref:`orm_queryguide_populate_existing`
+
+    *
+      - ::
+
+          session.query(User).\
+              filter(User.name == 'foo').\
+              update(
+                  {"fullname": "Foo Bar"},
+                  synchronize_session="evaluate"
+              )
+
+
+      - ::
+
+          session.execute(
+              update(User).
+              where(User.name == 'foo').
+              values(fullname="Foo Bar").
+              execution_options(synchronize_session="evaluate")
+          )
+
+      - :ref:`orm_expression_update_delete`
+
 .. _migration_20_unify_select:
 
 ORM Query Unified with Core Select
@@ -1299,6 +1460,8 @@ the majority of this ORM logic is also cached.
 
   :ref:`change_5159`
 
+.. _migration_20_get_to_session:
+
 ORM Query - get() method moves to Session
 ------------------------------------------
 
@@ -1695,8 +1858,19 @@ ORM rows returned by ``session.execute(stmt)`` are no longer automatically
 "uniqued".    This will normally be a welcome change, except in the case
 where the "joined eager loading" loader strategy is used with collections::
 
-    stmt = select(User).options(joinedload(User.addresses))
+    # In the legacy API, many rows each have the same User primary key, but
+    # only one User per primary key is returned
+    users = session.query(User).options(joinedload(User.addresses))
+
+    # In the new API, uniquing is available but not implicitly
+    # enabled
+    result = session.execute(
+        select(User).options(joinedload(User.addresses))
+    )
 
+    # this actually will raise an error to let the user know that
+    # uniquing should be applied
+    rows = result.all()
 
 **Migrating to 2.0**
 
@@ -1711,8 +1885,10 @@ while still maintaining explicitness::
     stmt = select(User).options(joinedload(User.addresses))
 
     # statement will raise if unique() is not used, due to joinedload()
-    # of a collection.  in all other cases, unique() is not needed
-    rows = session.execute(stmt).unique().execute().all()
+    # of a collection.  in all other cases, unique() is not needed.
+    # By stating unique() explicitly, confusion over discrepancies between
+    # number of objects/ rows returned vs. "SELECT COUNT(*)" is resolved
+    rows = session.execute(stmt).unique().all()
 
 **Discussion**
 
index 5576882a6c40a9f4ea929ba8f495e23fd1984464..b97b3aec9c48fe4edde8bfc082447315b0bb0f3f 100644 (file)
@@ -718,6 +718,7 @@ sections below.
     * :ref:`loading_toplevel` - details relationship and loading options that
       affect how :func:`_orm.relationship` mapped attributes are loaded
 
+.. _orm_queryguide_execution_options:
 
 ORM Execution Options
 ---------------------