From: Mike Bayer Date: Sun, 11 Apr 2021 15:10:20 +0000 (-0400) Subject: Clarify select_from(A).join(B.a) X-Git-Tag: rel_1_4_8~18 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6f16636142488226d4208cd4fccabadec080fa32;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Clarify select_from(A).join(B.a) Fixes: #2722 Change-Id: I025715023fa74b1f9ade4ecc8ed8712f42de727a --- diff --git a/doc/build/orm/queryguide.rst b/doc/build/orm/queryguide.rst index 58c2c4bc34..71893b81e8 100644 --- a/doc/build/orm/queryguide.rst +++ b/doc/build/orm/queryguide.rst @@ -666,6 +666,8 @@ to it using :class:`_orm.aliased` refer to distinct sets of columns:: User(id=2, name='sandy', fullname='Sandy Cheeks') Address(id=3, email_address='squirrel@squirrelpower.org') +.. _orm_queryguide_select_from: + Controlling what to Join From ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -694,12 +696,50 @@ can be used subsequent, the :meth:`_sql.Select.select_from` method may also be used:: - >>> stmt = select(Address).select_from(User).join(User.addresses).where(User.name == 'sandy') + >>> stmt = select(Address).select_from(User).join(Address).where(User.name == 'sandy') >>> print(stmt) SELECT address.id, address.user_id, address.email_address FROM user_account JOIN address ON user_account.id = address.user_id WHERE user_account.name = :name_1 +.. tip:: + + The :meth:`_sql.Select.select_from` method does not actually have the + final say on the order of tables in the FROM clause. If the statement + also refers to a :class:`_sql.Join` construct that refers to existing + tables in a different order, the :class:`_sql.Join` construct takes + precedence. When we use methods like :meth:`_sql.Select.join` + and :meth:`_sql.Select.join_from`, these methods are ultimately creating + such a :class:`_sql.Join` object. Therefore we can see the contents + of :meth:`_sql.Select.select_from` being overridden in a case like this:: + + >>> stmt = select(Address).select_from(User).join(Address.user).where(User.name == 'sandy') + >>> print(stmt) + SELECT address.id, address.user_id, address.email_address + FROM address JOIN user_account ON user_account.id = address.user_id + WHERE user_account.name = :name_1 + + Where above, we see that the FROM clause is ``address JOIN user_account``, + even though we stated ``select_from(User)`` first. Because of the + ``.join(Address.user)`` method call, the statement is ultimately equivalent + to the following:: + + >>> user_table = User.__table__ + >>> address_table = Address.__table__ + >>> from sqlalchemy.sql import join + >>> + >>> j = address_table.join(user_table, user_table.c.id == address_table.c.user_id) + >>> stmt = ( + ... select(address_table).select_from(user_table).select_from(j). + ... where(user_table.c.name == 'sandy') + ... ) + >>> print(stmt) + SELECT address.id, address.user_id, address.email_address + FROM address JOIN user_account ON user_account.id = address.user_id + WHERE user_account.name = :name_1 + + The :class:`_sql.Join` construct above is added as another entry in the + :meth:`_sql.Select.select_from` list which supersedes the previous entry. Special Relationship Operators ------------------------------ diff --git a/doc/build/tutorial/data.rst b/doc/build/tutorial/data.rst index f5009df581..f7c0c8e207 100644 --- a/doc/build/tutorial/data.rst +++ b/doc/build/tutorial/data.rst @@ -692,6 +692,13 @@ produce the SQL ``count()`` function:: {opensql}SELECT count(:count_2) AS count_1 FROM user_account +.. seealso:: + + :ref:`orm_queryguide_select_from` - in the :ref:`queryguide_toplevel` - + contains additional examples and notes + regarding the interaction of :meth:`_sql.Select.select_from` and + :meth:`_sql.Select.join`. + .. _tutorial_select_join_onclause: Setting the ON Clause