.. changelog::
:version: 0.8.0b2
+ .. change::
+ :tags: orm, bug
+ :tickets: 2635
+
+ The :meth:`.Query.select_from` method can now be used with a
+ :func:`.aliased` construct without it interfering with the entities
+ being selected. Basically, a statement like this::
+
+ ua = aliased(User)
+ session.query(User.name).select_from(ua).join(User, User.name > ua.name)
+
+ Will maintain the columns clause of the SELECT as coming from the
+ unaliased "user", as specified; the select_from only takes place in the
+ FROM clause::
+
+ SELECT users.name AS users_name FROM users AS users_1
+ JOIN users ON users.name < users_1.name
+
+ Note that this behavior is in contrast
+ to the original, older use case for :meth:`.Query.select_from`, which is that
+ of restating the mapped entity in terms of a different selectable::
+
+ session.query(User.name).\
+ select_from(user_table.select().where(user_table.c.id > 5))
+
+ Which produces::
+
+ 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
+
+ It was the "aliasing" behavior of the latter use case that was
+ getting in the way of the former use case. The method now
+ specifically considers a SQL expression like
+ :func:`.expression.select` or :func:`.expression.alias`
+ separately from a mapped entity like a :func:`.aliased`
+ construct.
+
.. change::
:tags: sql, bug
:tickets: 2633
self._polymorphic_adapters[m.local_table] = adapter
def _set_select_from(self, *obj):
-
fa = []
+ select_from_alias = None
for from_obj in obj:
- if isinstance(from_obj, expression.SelectBase):
- from_obj = from_obj.alias()
- fa.append(from_obj)
+ info = inspect(from_obj)
+
+ if hasattr(info, 'mapper') and \
+ (info.is_mapper or info.is_aliased_class):
+ self._select_from_entity = from_obj
+ fa.append(info.selectable)
+ elif not info.is_selectable:
+ raise sa_exc.ArgumentError(
+ "argument is not a mapped class, mapper, "
+ "aliased(), or FromClause instance.")
+ else:
+ if isinstance(from_obj, expression.SelectBase):
+ from_obj = from_obj.alias()
+ select_from_alias = from_obj
+ fa.append(from_obj)
self._from_obj = tuple(fa)
if len(self._from_obj) == 1 and \
- isinstance(self._from_obj[0], expression.Alias):
+ isinstance(select_from_alias, expression.Alias):
equivs = self.__all_equivs()
self._from_obj_alias = sql_util.ColumnAdapter(
self._from_obj[0], equivs)
usage of :meth:`~.Query.select_from`.
"""
- obj = []
- for fo in from_obj:
- info = inspect(fo)
- if hasattr(info, 'mapper') and \
- (info.is_mapper or info.is_aliased_class):
- self._select_from_entity = fo
- obj.append(info.selectable)
- elif not info.is_selectable:
- raise sa_exc.ArgumentError(
- "select_from() accepts FromClause objects only.")
- else:
- obj.append(fo)
- self._set_select_from(*obj)
+ self._set_select_from(*from_obj)
def __getitem__(self, item):
if isinstance(item, slice):
)
+ def test_aliased_class_vs_nonaliased(self):
+ User, users = self.classes.User, self.tables.users
+ mapper(User, users)
+
+ ua = aliased(User)
+
+ sess = create_session()
+ self.assert_compile(
+ sess.query(User).select_from(ua).join(User, ua.name > User.name),
+ "SELECT users.id AS users_id, users.name AS users_name "
+ "FROM users AS users_1 JOIN users ON users.name < users_1.name"
+ )
+
+ self.assert_compile(
+ sess.query(User.name).select_from(ua).join(User, ua.name > User.name),
+ "SELECT users.name AS users_name FROM users AS users_1 "
+ "JOIN users ON users.name < users_1.name"
+ )
+
+ self.assert_compile(
+ sess.query(ua.name).select_from(ua).join(User, ua.name > User.name),
+ "SELECT users_1.name AS users_1_name FROM users AS users_1 "
+ "JOIN users ON users.name < users_1.name"
+ )
+
+ self.assert_compile(
+ sess.query(ua).select_from(User).join(ua, ua.name > User.name),
+ "SELECT users_1.id AS users_1_id, users_1.name AS users_1_name "
+ "FROM users JOIN users AS users_1 ON users.name < users_1.name"
+ )
+
+ # this is tested in many other places here, just adding it
+ # here for comparison
+ self.assert_compile(
+ sess.query(User.name).\
+ select_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"
+ )
def test_join_no_order_by(self):
User, users = self.classes.User, self.tables.users