From: Federico Caselli Date: Tue, 10 Nov 2020 22:13:55 +0000 (+0100) Subject: Add summary table for orm 2.0 migration X-Git-Tag: rel_1_4_0b2~137^2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1d7c66f53a9e6d01a7ea72562c87aaa1b09d3f7c;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Add summary table for orm 2.0 migration 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 --- diff --git a/doc/build/changelog/migration_20.rst b/doc/build/changelog/migration_20.rst index eadabd7f0e..208beaa4f5 100644 --- a/doc/build/changelog/migration_20.rst +++ b/doc/build/changelog/migration_20.rst @@ -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** diff --git a/doc/build/orm/queryguide.rst b/doc/build/orm/queryguide.rst index 5576882a6c..b97b3aec9c 100644 --- a/doc/build/orm/queryguide.rst +++ b/doc/build/orm/queryguide.rst @@ -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 ---------------------