]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
The "auto-aliasing" behavior of the :class:`.Query.select_from`
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 31 May 2013 00:09:44 +0000 (20:09 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 31 May 2013 00:09:44 +0000 (20:09 -0400)
method has been turned off.  The specific behavior is now
availble via a new method :class:`.Query.select_entity_from`.
[ticket:2736]

doc/build/changelog/changelog_09.rst
doc/build/changelog/migration_09.rst
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/strategies.py
test/orm/test_froms.py
test/orm/test_joins.py

index ccb2960ab1d27712bef86cafd8269a0c34727ca7..baddf7e2a99fff97f62a77c3221937f4859a5750 100644 (file)
        :tags: feature, oracle, py3k
 
        The Oracle unit tests with cx_oracle now pass
-       fully under Python 3.
\ No newline at end of file
+       fully under Python 3.
+
+    .. change::
+        :tags: bug, orm
+        :tickets: 2736
+
+        The "auto-aliasing" behavior of the :class:`.Query.select_from`
+        method has been turned off.  The specific behavior is now
+        availble via a new method :class:`.Query.select_entity_from`.
+        The auto-aliasing behavior here was never well documented and
+        is generally not what's desired, as :class:`.Query.select_from`
+        has become more oriented towards controlling how a JOIN is
+        rendered.  :class:`.Query.select_entity_from` will also be made
+        available in 0.8 so that applications which rely on the auto-aliasing
+        can shift their applications to use this method.
+
+        .. seealso::
+
+            :ref:`migration_2736`
\ No newline at end of file
index 82a0e788bda8a876e3a023078aa458289ae1ba00..969bfb62457bdadae245e3dd5d43bf7d6dfda407 100644 (file)
@@ -20,8 +20,8 @@ their applications from the 0.8 series of SQLAlchemy to 0.9.
 
 Version 0.9 is a faster-than-usual push from version 0.8,
 featuring a more versatile codebase with regards to modern
-Python versions.   The upgrade path at the moment requires no changes
-to user code, however this is subject to change.
+Python versions.   See :ref:`behavioral_changes_09` for
+potentially backwards-incompatible changes.
 
 Platform Support
 ================
@@ -39,3 +39,89 @@ the 3.1 and 3.2 interpreters.
 
 At the moment, the C extensions are still not fully ported to
 Python 3.
+
+
+.. _behavioral_changes_09:
+
+Behavioral Changes
+==================
+
+.. _migration_2736:
+
+:meth:`.Query.select_from` no longer applies the clause to corresponding entities
+---------------------------------------------------------------------------------
+
+The :meth:`.Query.select_from` method has been popularized in recent versions
+as a means of controlling the first thing that a :class:`.Query` object
+"selects from", typically for the purposes of controlling how a JOIN will
+render.
+
+Consider the following example against the usual ``User`` mapping::
+
+       select_stmt = select([User]).where(User.id == 7).alias()
+
+       q = session.query(User).\
+               join(select_stmt, User.id == select_stmt.c.id).\
+               filter(User.name == 'ed')
+
+The above statement predictably renders SQL like the following::
+
+       SELECT "user".id AS user_id, "user".name AS user_name
+       FROM "user" JOIN (SELECT "user".id AS id, "user".name AS name
+       FROM "user"
+       WHERE "user".id = :id_1) AS anon_1 ON "user".id = anon_1.id
+       WHERE "user".name = :name_1
+
+If we wanted to reverse the order of the left and right elements of the
+JOIN, the documentation would lead us to believe we could use
+:meth:`.Query.select_from` to do so::
+
+       q = session.query(User).\
+               select_from(select_stmt).\
+               join(User, User.id == select_stmt.c.id).\
+               filter(User.name == 'ed')
+
+However, in version 0.8 and earlier, the above use of :meth:`.Query.select_from`
+would apply the ``select_stmt`` to **replace** the ``User`` entity, as it
+selects from the ``user`` table which is compatible with ``User``::
+
+       -- SQLAlchemy 0.8 and earlier...
+       SELECT anon_1.id AS anon_1_id, anon_1.name AS anon_1_name
+       FROM (SELECT "user".id AS id, "user".name AS name
+       FROM "user"
+       WHERE "user".id = :id_1) AS anon_1 JOIN "user" ON anon_1.id = anon_1.id
+       WHERE anon_1.name = :name_1
+
+The above statement is a mess, the ON clause refers ``anon_1.id = anon_1.id``,
+our WHERE clause has been replaced with ``anon_1`` as well.
+
+This behavior is quite intentional, but has a different use case from that
+which has become popular for :meth:`.Query.select_from`.  The above behavior
+is now available by a new method known as :meth:`.Query.select_entity_from`.
+This is a lesser used behavior that in modern SQLAlchemy is roughly equivalent
+to selecting from a customized :func:`.aliased` construct::
+
+       select_stmt = select([User]).where(User.id == 7)
+       user_from_stmt = aliased(User, select_stmt.alias())
+
+       q = session.query(user_from_stmt).filter(user_from_stmt.name == 'ed')
+
+So with SQLAlchemy 0.9, our query that selects from ``select_stmt`` produces
+the SQL we expect::
+
+    -- SQLAlchemy 0.9
+    SELECT "user".id AS user_id, "user".name AS user_name
+    FROM (SELECT "user".id AS id, "user".name AS name
+    FROM "user"
+    WHERE "user".id = :id_1) AS anon_1 JOIN "user" ON "user".id = id
+    WHERE "user".name = :name_1
+
+The :meth:`.Query.select_entity_from` method will be available in SQLAlchemy
+**0.8.2**, so applications which rely on the old behavior can transition
+to this method first, ensure all tests continue to function, then upgrade
+to 0.9 without issue.
+
+:ticket:`2736`
+
+
+
index cb788e0a43f4877f05fd417a9e24e7a2e7de763b..79fd61c63940b3e2cdf247a66886d1a9bfc45c16 100644 (file)
@@ -162,15 +162,19 @@ class Query(object):
             for m in m2.iterate_to_root():
                 self._polymorphic_adapters[m.local_table] = adapter
 
-    def _set_select_from(self, *obj):
+    def _set_select_from(self, obj, set_base_alias):
         fa = []
         select_from_alias = None
+
         for from_obj in obj:
             info = inspect(from_obj)
 
             if hasattr(info, 'mapper') and \
                 (info.is_mapper or info.is_aliased_class):
-                self._select_from_entity = from_obj
+                if set_base_alias:
+                    raise sa_exc.ArgumentError(
+                            "A selectable (FromClause) instance is "
+                            "expected when the base alias is being set.")
                 fa.append(info.selectable)
             elif not info.is_selectable:
                 raise sa_exc.ArgumentError(
@@ -179,12 +183,14 @@ class Query(object):
             else:
                 if isinstance(from_obj, expression.SelectBase):
                     from_obj = from_obj.alias()
-                select_from_alias = from_obj
+                if set_base_alias:
+                    select_from_alias = from_obj
                 fa.append(from_obj)
 
         self._from_obj = tuple(fa)
 
-        if len(self._from_obj) == 1 and \
+        if set_base_alias and \
+            len(self._from_obj) == 1 and \
             isinstance(select_from_alias, expression.Alias):
             equivs = self.__all_equivs()
             self._from_obj_alias = sql_util.ColumnAdapter(
@@ -953,7 +959,7 @@ class Query(object):
                 '_prefixes',
         ):
             self.__dict__.pop(attr, None)
-        self._set_select_from(fromclause)
+        self._set_select_from([fromclause], True)
 
         # this enables clause adaptation for non-ORM
         # expressions.
@@ -1970,21 +1976,134 @@ class Query(object):
     def select_from(self, *from_obj):
         """Set the FROM clause of this :class:`.Query` explicitly.
 
-        Sending a mapped class or entity here effectively replaces the
+        :meth:`.Query.select_from` is often used in conjunction with
+        :meth:`.Query.join` in order to control which entity is selected
+        from on the "left" side of the join.
+
+        The entity or selectable object here effectively replaces the
         "left edge" of any calls to :meth:`~.Query.join`, when no
         joinpoint is otherwise established - usually, the default "join
         point" is the leftmost entity in the :class:`~.Query` object's
         list of entities to be selected.
 
-        Mapped entities or plain :class:`~.Table` or other selectables
-        can be sent here which will form the default FROM clause.
+        A typical example::
+
+            q = session.query(Address).select_from(User).\\
+                join(User.addresses).\\
+                filter(User.name == 'ed')
+
+        Which produces SQL equivalent to::
+
+            SELECT address.* FROM user
+            JOIN address ON user.id=address.user_id
+            WHERE user.name = :name_1
+
+        :param \*from_obj: collection of one or more entities to apply
+         to the FROM clause.  Entities can be mapped classes,
+         :class:`.AliasedClass` objects, :class:`.Mapper` objects
+         as well as core :class:`.FromClause` elements like subqueries.
+
+        .. versionchanged:: 0.9
+            This method no longer applies the given FROM object
+            to be the selectable from which matching entities
+            select from; the :meth:`.select_entity_from` method
+            now accomplishes this.  See that method for a description
+            of this behavior.
+
+        .. seealso::
+
+            :meth:`~.Query.join`
+
+            :meth:`.Query.select_entity_from`
+
+        """
+
+        self._set_select_from(from_obj, False)
+
+    @_generative(_no_clauseelement_condition)
+    def select_entity_from(self, from_obj):
+        """Set the FROM clause of this :class:`.Query` to a
+        core selectable, applying it as a replacement FROM clause
+        for corresponding mapped entities.
+
+        This method is similar to the :meth:`.Query.select_from`
+        method, in that it sets the FROM clause of the query.  However,
+        where :meth:`.Query.select_from` only affects what is placed
+        in the FROM, this method also applies the given selectable
+        to replace the FROM which the selected entities would normally
+        select from.
+
+        The given ``from_obj`` must be an instance of a :class:`.FromClause`,
+        e.g. a :func:`.select` or :class:`.Alias` construct.
+
+        An example would be a :class:`.Query` that selects ``User`` entities,
+        but uses :meth:`.Query.select_entity_from` to have the entities
+        selected from a :func:`.select` construct instead of the
+        base ``user`` table::
+
+            select_stmt = select([User]).where(User.id == 7)
+
+            q = session.query(User).\\
+                    select_entity_from(select_stmt).\\
+                    filter(User.name == 'ed')
+
+        The query generated will select ``User`` entities directly
+        from the given :func:`.select` construct, and will be::
+
+            SELECT anon_1.id AS anon_1_id, anon_1.name AS anon_1_name
+            FROM (SELECT "user".id AS id, "user".name AS name
+            FROM "user"
+            WHERE "user".id = :id_1) AS anon_1
+            WHERE anon_1.name = :name_1
+
+        Notice above that even the WHERE criterion was "adapted" such that
+        the ``anon_1`` subquery effectively replaces all references to the
+        ``user`` table, except for the one that it refers to internally.
+
+        Compare this to :meth:`.Query.select_from`, which as of
+        version 0.9, does not affect existing entities.  The
+        statement below::
+
+            q = session.query(User).\\
+                    select_from(select_stmt).\\
+                    filter(User.name == 'ed')
+
+        Produces SQL where both the ``user`` table as well as the
+        ``select_stmt`` construct are present as separate elements
+        in the FROM clause.  No "adaptation" of the ``user`` table
+        is applied::
+
+            SELECT "user".id AS user_id, "user".name AS user_name
+            FROM "user", (SELECT "user".id AS id, "user".name AS name
+            FROM "user"
+            WHERE "user".id = :id_1) AS anon_1
+            WHERE "user".name = :name_1
+
+        :meth:`.Query.select_entity_from` maintains an older
+        behavior of :meth:`.Query.select_from`.  In modern usage,
+        similar results can also be achieved using :func:`.aliased`::
+
+            select_stmt = select([User]).where(User.id == 7)
+            user_from_select = aliased(User, select_stmt.alias())
+
+            q = session.query(user_from_select)
+
+        :param from_obj: a :class:`.FromClause` object that will replace
+         the FROM clause of this :class:`.Query`.
+
+        .. seealso::
+
+            :meth:`.Query.select_from`
 
-        See the example in :meth:`~.Query.join` for a typical
-        usage of :meth:`~.Query.select_from`.
+        .. versionadded:: 0.8
+            :meth:`.Query.select_entity_from` was added to specify
+            the specific behavior of entity replacement, however
+            the :meth:`.Query.select_from` maintains this behavior
+            as well until 0.9.
 
         """
 
-        self._set_select_from(*from_obj)
+        self._set_select_from([from_obj], True)
 
     def __getitem__(self, item):
         if isinstance(item, slice):
index 4651c71b793336b51d31b81870b95111bbf6824e..48ee6a5f8f8d047de802daf6757c3358351ed535 100644 (file)
@@ -781,7 +781,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
         # set a real "from" if not present, as this is more
         # accurate than just going off of the column expression
         if not q._from_obj and entity_mapper.isa(leftmost_mapper):
-            q._set_select_from(entity_mapper)
+            q._set_select_from([entity_mapper], False)
 
         # select from the identity columns of the outer
         q._set_entities(q._adapt_col_list(leftmost_attr))
index 5040c46852c5c4334a1e419f68621e0fca05de22..2bc93836820606c890a214bc1aa892f3d83d0ea6 100644 (file)
@@ -161,7 +161,7 @@ class RawSelectTest(QueryTest, AssertsCompiledSQL):
 
         sess = create_session()
 
-        self.assert_compile(sess.query(users).select_from(
+        self.assert_compile(sess.query(users).select_entity_from(
                     users.select()).with_labels().statement,
             "SELECT users.id AS users_id, users.name AS users_name FROM users, "
             "(SELECT users.id AS id, users.name AS name FROM users) AS anon_1",
@@ -180,7 +180,7 @@ class RawSelectTest(QueryTest, AssertsCompiledSQL):
             filter(addresses.c.user_id == users.c.id).correlate(users).\
                         statement.alias()
 
-        self.assert_compile(sess.query(users, s.c.email).select_from(
+        self.assert_compile(sess.query(users, s.c.email).select_entity_from(
                     users.join(s, s.c.id == users.c.id)
                 ).with_labels().statement,
                 "SELECT users.id AS users_id, users.name AS users_name, "
@@ -425,12 +425,12 @@ class ColumnAccessTest(QueryTest, AssertsCompiledSQL):
             "WHERE anon_1.anon_2_users_name = :name_1"
         )
 
-    def test_select_from(self):
+    def test_select_entity_from(self):
         User = self.classes.User
         sess = create_session()
 
         q = sess.query(User)
-        q = sess.query(User).select_from(q.statement)
+        q = sess.query(User).select_entity_from(q.statement)
         self.assert_compile(
             q.filter(User.name=='ed'),
             "SELECT anon_1.id AS anon_1_id, anon_1.name AS anon_1_name "
@@ -438,6 +438,19 @@ class ColumnAccessTest(QueryTest, AssertsCompiledSQL):
             "users) AS anon_1 WHERE anon_1.name = :name_1"
         )
 
+    def test_select_from_no_aliasing(self):
+        User = self.classes.User
+        sess = create_session()
+
+        q = sess.query(User)
+        q = sess.query(User).select_from(q.statement)
+        self.assert_compile(
+            q.filter(User.name=='ed'),
+            "SELECT users.id AS users_id, users.name AS users_name "
+            "FROM users, (SELECT users.id AS id, users.name AS name FROM "
+            "users) AS anon_1 WHERE users.name = :name_1"
+        )
+
     def test_anonymous_expression(self):
         from sqlalchemy.sql import column
 
@@ -681,15 +694,15 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
             assert self.static.user_address_result == l
         self.assert_sql_count(testing.db, go, 1)
 
-        # better way.  use select_from()
+        # better way.  use select_entity_from()
         def go():
-            l = sess.query(User).select_from(query).\
+            l = sess.query(User).select_entity_from(query).\
                         options(contains_eager('addresses')).all()
             assert self.static.user_address_result == l
         self.assert_sql_count(testing.db, go, 1)
 
         # same thing, but alias addresses, so that the adapter
-        # generated by select_from() is wrapped within
+        # generated by select_entity_from() is wrapped within
         # the adapter created by contains_eager()
         adalias = addresses.alias()
         query = users.select(users.c.id==7).\
@@ -699,7 +712,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
                     select(use_labels=True,
                             order_by=['ulist.id', adalias.c.id])
         def go():
-            l = sess.query(User).select_from(query).\
+            l = sess.query(User).select_entity_from(query).\
                     options(contains_eager('addresses', alias=adalias)).all()
             assert self.static.user_address_result == l
         self.assert_sql_count(testing.db, go, 1)
@@ -733,7 +746,7 @@ class InstancesTest(QueryTest, AssertsCompiledSQL):
 
         adalias = addresses.alias()
         q = sess.query(User).\
-                select_from(users.outerjoin(adalias)).\
+                select_entity_from(users.outerjoin(adalias)).\
                 options(contains_eager(User.addresses, alias=adalias)).\
                 order_by(User.id, adalias.c.id)
         def go():
@@ -1044,7 +1057,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
 
         sel = users.select(User.id.in_([7, 8])).alias()
         q = sess.query(User)
-        q2 = q.select_from(sel).values(User.name)
+        q2 = q.select_entity_from(sel).values(User.name)
         eq_(list(q2), [('jack',), ('ed',)])
 
         q = sess.query(User)
@@ -1080,17 +1093,17 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
         q2 = q.values(func.count(User.name))
         assert next(q2) == (4,)
 
-        q2 = q.select_from(sel).filter(User.id==8).values(User.name, sel.c.name, User.name)
+        q2 = q.select_entity_from(sel).filter(User.id==8).values(User.name, sel.c.name, User.name)
         eq_(list(q2), [('ed', 'ed', 'ed')])
 
         # using User.xxx is alised against "sel", so this query returns nothing
-        q2 = q.select_from(sel).\
+        q2 = q.select_entity_from(sel).\
                 filter(User.id==8).\
                 filter(User.id>sel.c.id).values(User.name, sel.c.name, User.name)
         eq_(list(q2), [])
 
         # whereas this uses users.c.xxx, is not aliased and creates a new join
-        q2 = q.select_from(sel).\
+        q2 = q.select_entity_from(sel).\
                 filter(users.c.id==8).\
                 filter(users.c.id>sel.c.id).values(users.c.name, sel.c.name, User.name)
         eq_(list(q2), [('ed', 'jack', 'jack')])
@@ -1119,7 +1132,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
         sel = users.select(User.id.in_([7, 8])).alias()
         q = sess.query(User)
         u2 = aliased(User)
-        q2 = q.select_from(sel).\
+        q2 = q.select_entity_from(sel).\
                     filter(u2.id>1).\
                     order_by(User.id, sel.c.id, u2.id).\
                     values(User.name, sel.c.name, u2.name)
@@ -1206,7 +1219,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
 
         sel = users.select(User.id.in_([7, 8])).alias()
         q = sess.query(User.name)
-        q2 = q.select_from(sel).all()
+        q2 = q.select_entity_from(sel).all()
         eq_(list(q2), [('jack',), ('ed',)])
 
         eq_(sess.query(User.name, Address.email_address).filter(User.id==Address.user_id).all(), [
@@ -1277,7 +1290,7 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
             ]
         )
 
-        # test eager aliasing, with/without select_from aliasing
+        # test eager aliasing, with/without select_entity_from aliasing
         for q in [
             sess.query(User, adalias.email_address).\
                     outerjoin(adalias, User.addresses).\
@@ -1466,14 +1479,14 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
 
         q = sess.query(User)
         adalias = addresses.alias('adalias')
-        q = q.add_entity(Address, alias=adalias).select_from(users.outerjoin(adalias))
+        q = q.add_entity(Address, alias=adalias).select_entity_from(users.outerjoin(adalias))
         l = q.order_by(User.id, adalias.c.id).all()
         assert l == expected
 
         sess.expunge_all()
 
         q = sess.query(User).add_entity(Address, alias=adalias)
-        l = q.select_from(users.outerjoin(adalias)).filter(adalias.c.email_address=='ed@bettyboop.com').all()
+        l = q.select_entity_from(users.outerjoin(adalias)).filter(adalias.c.email_address=='ed@bettyboop.com').all()
         assert l == [(user8, address3)]
 
     def test_with_entities(self):
@@ -1591,9 +1604,9 @@ class MixedEntitiesTest(QueryTest, AssertsCompiledSQL):
 
         sess.expunge_all()
 
-        # test with select_from()
+        # test with select_entity_from()
         q = create_session().query(User).add_column(func.count(addresses.c.id))\
-            .add_column(("Name:" + users.c.name)).select_from(users.outerjoin(addresses))\
+            .add_column(("Name:" + users.c.name)).select_entity_from(users.outerjoin(addresses))\
             .group_by(users).order_by(users.c.id)
 
         assert q.all() == expected
@@ -1718,19 +1731,19 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         sel = users.select(users.c.id.in_([7, 8])).alias()
         sess = create_session()
 
-        eq_(sess.query(User).select_from(sel).all(), [User(id=7), User(id=8)])
+        eq_(sess.query(User).select_entity_from(sel).all(), [User(id=7), User(id=8)])
 
-        eq_(sess.query(User).select_from(sel).filter(User.id==8).all(), [User(id=8)])
+        eq_(sess.query(User).select_entity_from(sel).filter(User.id==8).all(), [User(id=8)])
 
-        eq_(sess.query(User).select_from(sel).order_by(desc(User.name)).all(), [
+        eq_(sess.query(User).select_entity_from(sel).order_by(desc(User.name)).all(), [
             User(name='jack',id=7), User(name='ed',id=8)
         ])
 
-        eq_(sess.query(User).select_from(sel).order_by(asc(User.name)).all(), [
+        eq_(sess.query(User).select_entity_from(sel).order_by(asc(User.name)).all(), [
             User(name='ed',id=8), User(name='jack',id=7)
         ])
 
-        eq_(sess.query(User).select_from(sel).options(joinedload('addresses')).first(),
+        eq_(sess.query(User).select_entity_from(sel).options(joinedload('addresses')).first(),
             User(name='jack', addresses=[Address(id=1)])
         )
 
@@ -1745,7 +1758,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         sel = users.select(users.c.id.in_([7, 8]))
         sess = create_session()
 
-        eq_(sess.query(User).select_from(sel).all(),
+        eq_(sess.query(User).select_entity_from(sel).all(),
             [
                 User(name='jack',id=7), User(name='ed',id=8)
             ]
@@ -1772,14 +1785,14 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         )
 
         self.assert_compile(
-            sess.query(ualias).select_from(sel).filter(ualias.id>sel.c.id),
+            sess.query(ualias).select_entity_from(sel).filter(ualias.id>sel.c.id),
             "SELECT users_1.id AS users_1_id, users_1.name AS users_1_name FROM "
             "users AS users_1, (SELECT users.id AS id, users.name AS name FROM "
             "users WHERE users.id IN (:id_1, :id_2)) AS anon_1 WHERE users_1.id > anon_1.id",
         )
 
         self.assert_compile(
-            sess.query(ualias).select_from(sel).join(ualias, ualias.id>sel.c.id),
+            sess.query(ualias).select_entity_from(sel).join(ualias, ualias.id>sel.c.id),
             "SELECT users_1.id AS users_1_id, users_1.name AS users_1_name "
             "FROM (SELECT users.id AS id, users.name AS name "
             "FROM users WHERE users.id IN (:id_1, :id_2)) AS anon_1 "
@@ -1787,7 +1800,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         )
 
         self.assert_compile(
-            sess.query(ualias).select_from(sel).join(ualias, ualias.id>User.id),
+            sess.query(ualias).select_entity_from(sel).join(ualias, ualias.id>User.id),
             "SELECT users_1.id AS users_1_id, users_1.name AS users_1_name "
             "FROM (SELECT users.id AS id, users.name AS name FROM "
             "users WHERE users.id IN (:id_1, :id_2)) AS anon_1 "
@@ -1805,7 +1818,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
 
         # this one uses an explicit join(left, right, onclause) so works
         self.assert_compile(
-            sess.query(ualias).select_from(join(sel, ualias, ualias.id>sel.c.id)),
+            sess.query(ualias).select_entity_from(join(sel, ualias, ualias.id>sel.c.id)),
             "SELECT users_1.id AS users_1_id, users_1.name AS users_1_name FROM "
             "(SELECT users.id AS id, users.name AS name FROM users WHERE users.id "
             "IN (:id_1, :id_2)) AS anon_1 JOIN users AS users_1 ON users_1.id > anon_1.id",
@@ -1848,7 +1861,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         # here for comparison
         self.assert_compile(
             sess.query(User.name).\
-                    select_from(users.select().where(users.c.id > 5)),
+                    select_entity_from(users.select().where(users.c.id > 5)),
             "SELECT anon_1.name AS anon_1_name FROM (SELECT users.id AS id, "
             "users.name AS name FROM users WHERE users.id > :id_1) AS anon_1"
         )
@@ -1861,7 +1874,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         sel = users.select(users.c.id.in_([7, 8]))
         sess = create_session()
 
-        eq_(sess.query(User).select_from(sel).all(),
+        eq_(sess.query(User).select_entity_from(sel).all(),
             [
                 User(name='jack',id=7), User(name='ed',id=8)
             ]
@@ -1881,7 +1894,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         sel = users.select(users.c.id.in_([7, 8]))
         sess = create_session()
 
-        eq_(sess.query(User).select_from(sel).join('addresses').
+        eq_(sess.query(User).select_entity_from(sel).join('addresses').
                     add_entity(Address).order_by(User.id).order_by(Address.id).all(),
             [
                 (User(name='jack',id=7), Address(user_id=7,email_address='jack@bean.com',id=1)),
@@ -1892,7 +1905,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         )
 
         adalias = aliased(Address)
-        eq_(sess.query(User).select_from(sel).join(adalias, 'addresses').
+        eq_(sess.query(User).select_entity_from(sel).join(adalias, 'addresses').
                     add_entity(adalias).order_by(User.id).order_by(adalias.id).all(),
             [
                 (User(name='jack',id=7), Address(user_id=7,email_address='jack@bean.com',id=1)),
@@ -1931,7 +1944,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         sess = create_session()
         sel = users.select(users.c.id.in_([7, 8]))
 
-        eq_(sess.query(User).select_from(sel).\
+        eq_(sess.query(User).select_entity_from(sel).\
                 join('orders', 'items', 'keywords').\
                 filter(Keyword.name.in_(['red', 'big', 'round'])).\
                 all(),
@@ -1939,7 +1952,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
             User(name='jack',id=7)
         ])
 
-        eq_(sess.query(User).select_from(sel).\
+        eq_(sess.query(User).select_entity_from(sel).\
                     join('orders', 'items', 'keywords', aliased=True).\
                     filter(Keyword.name.in_(['red', 'big', 'round'])).\
                     all(),
@@ -1949,7 +1962,7 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
 
         def go():
             eq_(
-                sess.query(User).select_from(sel).
+                sess.query(User).select_entity_from(sel).
                         options(joinedload_all('orders.items.keywords')).
                         join('orders', 'items', 'keywords', aliased=True).
                         filter(Keyword.name.in_(['red', 'big', 'round'])).\
@@ -1994,14 +2007,14 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
 
         sess.expunge_all()
         sel2 = orders.select(orders.c.id.in_([1,2,3]))
-        eq_(sess.query(Order).select_from(sel2).\
+        eq_(sess.query(Order).select_entity_from(sel2).\
                     join('items', 'keywords').\
                     filter(Keyword.name == 'red').\
                     order_by(Order.id).all(), [
             Order(description='order 1',id=1),
             Order(description='order 2',id=2),
         ])
-        eq_(sess.query(Order).select_from(sel2).\
+        eq_(sess.query(Order).select_entity_from(sel2).\
                     join('items', 'keywords', aliased=True).\
                     filter(Keyword.name == 'red').\
                     order_by(Order.id).all(), [
@@ -2025,7 +2038,9 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         sess = create_session()
 
         def go():
-            eq_(sess.query(User).options(joinedload('addresses')).select_from(sel).order_by(User.id).all(),
+            eq_(sess.query(User).options(
+                    joinedload('addresses')
+                    ).select_entity_from(sel).order_by(User.id).all(),
                 [
                     User(id=7, addresses=[Address(id=1)]),
                     User(id=8, addresses=[Address(id=2), Address(id=3), Address(id=4)])
@@ -2035,14 +2050,19 @@ class SelectFromTest(QueryTest, AssertsCompiledSQL):
         sess.expunge_all()
 
         def go():
-            eq_(sess.query(User).options(joinedload('addresses')).select_from(sel).filter(User.id==8).order_by(User.id).all(),
+            eq_(sess.query(User).options(
+                    joinedload('addresses')
+                ).select_entity_from(sel).filter(User.id==8).order_by(User.id).all(),
                 [User(id=8, addresses=[Address(id=2), Address(id=3), Address(id=4)])]
             )
         self.assert_sql_count(testing.db, go, 1)
         sess.expunge_all()
 
         def go():
-            eq_(sess.query(User).options(joinedload('addresses')).select_from(sel).order_by(User.id)[1], User(id=8, addresses=[Address(id=2), Address(id=3), Address(id=4)]))
+            eq_(sess.query(User).options(
+                    joinedload('addresses')
+            ).select_entity_from(sel).order_by(User.id)[1],
+                User(id=8, addresses=[Address(id=2), Address(id=3), Address(id=4)]))
         self.assert_sql_count(testing.db, go, 1)
 
 class CustomJoinTest(QueryTest):
@@ -2180,7 +2200,7 @@ class ExternalColumnsTest(QueryTest):
 
         ua = aliased(User)
         eq_(sess.query(Address, ua.concat, ua.count).
-                    select_from(join(Address, ua, 'user')).
+                    select_entity_from(join(Address, ua, 'user')).
                     options(joinedload(Address.user)).order_by(Address.id).all(),
             [
                 (Address(id=1, user=User(id=7, concat=14, count=1)), 14, 1),
@@ -2195,7 +2215,7 @@ class ExternalColumnsTest(QueryTest):
             [(1, 7, 14, 1), (2, 8, 16, 3), (3, 8, 16, 3), (4, 8, 16, 3), (5, 9, 18, 1)]
         )
 
-        eq_(list(sess.query(Address, ua).select_from(join(Address,ua, 'user')).values(Address.id, ua.id, ua.concat, ua.count)),
+        eq_(list(sess.query(Address, ua).select_entity_from(join(Address,ua, 'user')).values(Address.id, ua.id, ua.concat, ua.count)),
             [(1, 7, 14, 1), (2, 8, 16, 3), (3, 8, 16, 3), (4, 8, 16, 3), (5, 9, 18, 1)]
         )
 
index 320104acb4d6eb1061a8fea073d2f1fdbd741dbe..2dac591501c4d021b608fa5c2ebd5683b6d406b7 100644 (file)
@@ -1066,7 +1066,7 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
 
         # same with an explicit select_from()
         eq_(
-            sess.query(User).select_from(select([users]).
+            sess.query(User).select_entity_from(select([users]).
                                 order_by(User.id).offset(2).alias()).
                                 join(Order, User.id==Order.user_id).
                                 all(),