--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 6274
+
+ Calling :func:`_orm.aliased` against a :func:`_sql.select` or
+ :func:`_sql.union` / :class:`_sql.CompoundSelect` construct, which
+ previously failed with an obscure ``AttributeError`` regarding a missing
+ ``.mapper`` attribute, now raises when using SQLAlchemy 2.1, and emits a
+ deprecation warning under SQLAlchemy 2.0 as it coerces the construct into a
+ subquery instead. This matches the behavior of other similar implicit
+ SELECT-to-FROM coercions. Pull request courtesy Rens Groothuijsen.
from ..sql.elements import KeyedColumnElement
from ..sql.schema import MetaData
from ..sql.selectable import FromClause
+from ..sql.selectable import GenerativeSelect
from ..util.langhelpers import MemoizedSlots
from ..util.typing import de_stringify_annotation as _de_stringify_annotation
from ..util.typing import eval_name_only as _eval_name_only
flat: bool = False,
adapt_on_names: bool = False,
) -> Union[AliasedClass[_O], FromClause]:
- if isinstance(element, FromClause):
+ if isinstance(element, GenerativeSelect):
+ return coercions.expect(roles.FromClauseRole, element, flat=flat)
+ elif isinstance(element, FromClause):
if adapt_on_names:
raise sa_exc.ArgumentError(
"adapt_on_names only applies to ORM elements"
from sqlalchemy import select
from sqlalchemy import Table
from sqlalchemy import testing
+from sqlalchemy import union
from sqlalchemy.engine import result
+from sqlalchemy.exc import ArgumentError
from sqlalchemy.ext.hybrid import hybrid_method
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import aliased
from sqlalchemy.testing import AssertsCompiledSQL
from sqlalchemy.testing import eq_
from sqlalchemy.testing import expect_raises
+from sqlalchemy.testing import expect_raises_message
from sqlalchemy.testing import expect_warnings
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import is_
inspect(Point),
)
+ def test_aliased_select_raises(self):
+ """test for #6274"""
+
+ class Point:
+ pass
+
+ self._fixture(Point)
+
+ with expect_raises_message(
+ ArgumentError, r"use the \.subquery\(\) method"
+ ):
+ aliased(select(Point.x).filter(Point.id == 1))
+
+ def test_aliased_compound_select_raises(self):
+ """test for #6274"""
+
+ class Point:
+ pass
+
+ self._fixture(Point)
+
+ q1 = select(Point.x).filter(Point.id == 1)
+ q2 = select(Point.y).filter(Point.id == 2)
+ with expect_raises_message(
+ ArgumentError, r"use the \.subquery\(\) method"
+ ):
+ aliased(union(q1, q2))
+
+ def test_aliased_select_subquery(self):
+ """test for #6274"""
+
+ class Point:
+ pass
+
+ self._fixture(Point)
+
+ subq = select(Point.x).filter(Point.id == 1).subquery()
+ q1 = aliased(subq, name="point_alias")
+ self.assert_compile(
+ select(q1),
+ "SELECT point_alias.x FROM (SELECT point.x AS x "
+ "FROM point WHERE point.id = :id_1) AS point_alias",
+ )
+
class IdentityKeyTest(_fixtures.FixtureTest):
run_inserts = None