diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-06-13 12:37:22 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2019-07-06 13:02:22 -0400 |
| commit | ef7ff058eb67d73ebeac7b125ab2a7806e14629c (patch) | |
| tree | 9a09162961f7bcdb6d16837adacabb99f10b4410 /lib/sqlalchemy/orm | |
| parent | 1ce98ca83a4b2da12e52aa0f4ab181c83063abc2 (diff) | |
| download | sqlalchemy-ef7ff058eb67d73ebeac7b125ab2a7806e14629c.tar.gz | |
SelectBase no longer a FromClause
As part of the SQLAlchemy 2.0 migration project, a conceptual change has
been made to the role of the :class:`.SelectBase` class hierarchy,
which is the root of all "SELECT" statement constructs, in that they no
longer serve directly as FROM clauses, that is, they no longer subclass
:class:`.FromClause`. For end users, the change mostly means that any
placement of a :func:`.select` construct in the FROM clause of another
:func:`.select` requires first that it be wrapped in a subquery first,
which historically is through the use of the :meth:`.SelectBase.alias`
method, and is now also available through the use of
:meth:`.SelectBase.subquery`. This was usually a requirement in any
case since several databases don't accept unnamed SELECT subqueries
in their FROM clause in any case.
See the documentation in this change for lots more detail.
Fixes: #4617
Change-Id: I0f6174ee24b9a1a4529168e52e855e12abd60667
Diffstat (limited to 'lib/sqlalchemy/orm')
| -rw-r--r-- | lib/sqlalchemy/orm/query.py | 34 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/strategies.py | 1 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/util.py | 4 |
3 files changed, 22 insertions, 17 deletions
diff --git a/lib/sqlalchemy/orm/query.py b/lib/sqlalchemy/orm/query.py index c73a8147c..13402e7f4 100644 --- a/lib/sqlalchemy/orm/query.py +++ b/lib/sqlalchemy/orm/query.py @@ -253,11 +253,6 @@ class Query(object): "expected when the base alias is being set." ) fa.append(info.selectable) - elif not info.is_clause_element or not info._is_from_clause: - raise sa_exc.ArgumentError( - "argument is not a mapped class, mapper, " - "aliased(), or FromClause instance." - ) else: from_obj = coercions.expect( roles.StrictFromClauseRole, from_obj, allow_select=True @@ -271,7 +266,9 @@ class Query(object): if ( set_base_alias and len(self._from_obj) == 1 - and isinstance(select_from_alias, expression.Alias) + and isinstance( + select_from_alias, sql.selectable.AliasedReturnsRows + ) ): equivs = self.__all_equivs() self._from_obj_alias = sql_util.ColumnAdapter( @@ -2302,7 +2299,14 @@ class Query(object): if ( len(keys) == 2 and isinstance( - keys[0], (expression.FromClause, type, AliasedClass) + keys[0], + ( + # note this would be FromClause once + # coercion of SELECT is removed + expression.Selectable, + type, + AliasedClass, + ), ) and isinstance( keys[1], @@ -2761,7 +2765,9 @@ class Query(object): # if the destination selectable is a plain select(), # turn it into an alias(). if isinstance(right_selectable, expression.SelectBase): - right_selectable = right_selectable.alias() + right_selectable = coercions.expect( + roles.FromClauseRole, right_selectable + ) need_adapter = True # make the right hand side target into an ORM entity @@ -2781,7 +2787,8 @@ class Query(object): and ( right_mapper.with_polymorphic and isinstance( - right_mapper._with_polymorphic_selectable, expression.Alias + right_mapper._with_polymorphic_selectable, + expression.AliasedReturnsRows, ) or overlap # test for overlap: @@ -3201,13 +3208,10 @@ class Query(object): """ statement = coercions.expect(roles.SelectStatementRole, statement) - if not isinstance( + # TODO: coercions above should have this handled + assert isinstance( statement, (expression.TextClause, expression.SelectBase) - ): - raise sa_exc.ArgumentError( - "from_statement accepts text(), select(), " - "and union() objects only." - ) + ) self._statement = statement diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py index b0dffe5dd..7e150414b 100644 --- a/lib/sqlalchemy/orm/strategies.py +++ b/lib/sqlalchemy/orm/strategies.py @@ -1629,7 +1629,6 @@ class JoinedLoader(AbstractRelationshipLoader): # the object becomes shared among threads. this prevents # races for column identities. inspect(to_adapt).selectable.c - self._aliased_class_pool.append(to_adapt) return self._aliased_class_pool[idx] diff --git a/lib/sqlalchemy/orm/util.py b/lib/sqlalchemy/orm/util.py index 80d4b26b2..4b4fa4052 100644 --- a/lib/sqlalchemy/orm/util.py +++ b/lib/sqlalchemy/orm/util.py @@ -825,7 +825,9 @@ def aliased(element, alias=None, name=None, flat=False, adapt_on_names=False): raise sa_exc.ArgumentError( "adapt_on_names only applies to ORM elements" ) - return element._anonymous_fromclause(name=name, flat=flat) + return coercions.expect( + roles.AnonymizedFromClauseRole, element, name=name, flat=flat + ) else: return AliasedClass( element, |
