from ..sql import coercions
from ..sql import roles
from ..sql import visitors
+from ..sql.cache_key import HasCacheKey
+from ..sql.visitors import _TraverseInternalsType
+from ..sql.visitors import InternalTraversal
from ..util.typing import Literal
from ..util.typing import Self
from ..util.typing import TypeGuard
# non-string keys.
# ideally Proxy() would have a separate set of methods to deal
# with this case.
+ entity_namespace = self._entity_namespace
+ assert isinstance(entity_namespace, HasCacheKey)
+
if self.key is _UNKNOWN_ATTR_KEY: # type: ignore[comparison-overlap]
- annotations = {"entity_namespace": self._entity_namespace}
+ annotations = {"entity_namespace": entity_namespace}
else:
annotations = {
"proxy_key": self.key,
"proxy_owner": self._parententity,
- "entity_namespace": self._entity_namespace,
+ "entity_namespace": entity_namespace,
}
ce = self.comparator.__clause_element__()
@dataclasses.dataclass(frozen=True)
-class AdHocHasEntityNamespace:
+class AdHocHasEntityNamespace(HasCacheKey):
+ _traverse_internals: ClassVar[_TraverseInternalsType] = [
+ ("_entity_namespace", InternalTraversal.dp_has_cache_key),
+ ]
+
# py37 compat, no slots=True on dataclass
- __slots__ = ("entity_namespace",)
- entity_namespace: _ExternalEntityType[Any]
+ __slots__ = ("_entity_namespace",)
+ _entity_namespace: _InternalEntityType[Any]
is_mapper: ClassVar[bool] = False
is_aliased_class: ClassVar[bool] = False
+ @property
+ def entity_namespace(self):
+ return self._entity_namespace.entity_namespace
+
def create_proxied_attribute(
descriptor: Any,
else:
# used by hybrid attributes which try to remain
# agnostic of any ORM concepts like mappers
- return AdHocHasEntityNamespace(self.class_)
+ return AdHocHasEntityNamespace(self._parententity)
@property
def property(self):
import sqlalchemy as sa
from sqlalchemy import Column
+from sqlalchemy import column
from sqlalchemy import func
from sqlalchemy import inspect
from sqlalchemy import Integer
from sqlalchemy import update
from sqlalchemy import util
from sqlalchemy.ext.declarative import ConcreteBase
+from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import aliased
from sqlalchemy.orm import Bundle
from sqlalchemy.orm import defaultload
compare_values=True,
)
+ @testing.variation(
+ "exprtype", ["plain_column", "self_standing_case", "case_w_columns"]
+ )
+ def test_hybrid_w_case_ac(self, decl_base, exprtype):
+ """test #9728"""
+
+ class Employees(decl_base):
+ __tablename__ = "employees"
+ id = Column(String(128), primary_key=True)
+ first_name = Column(String(length=64))
+
+ @hybrid_property
+ def name(self):
+ return self.first_name
+
+ @name.expression
+ def name(
+ cls,
+ ):
+ if exprtype.plain_column:
+ return cls.first_name
+ elif exprtype.self_standing_case:
+ return case(
+ (column("x") == 1, column("q")),
+ else_=column("q"),
+ )
+ elif exprtype.case_w_columns:
+ return case(
+ (column("x") == 1, column("q")),
+ else_=cls.first_name,
+ )
+ else:
+ exprtype.fail()
+
+ def go1():
+ employees_2 = aliased(Employees, name="employees_2")
+ stmt = select(employees_2.name)
+ return stmt
+
+ def go2():
+ employees_2 = aliased(Employees, name="employees_2")
+ stmt = select(employees_2)
+ return stmt
+
+ self._run_cache_key_fixture(
+ lambda: stmt_20(go1(), go2()),
+ compare_values=True,
+ )
+
class RoundTripTest(QueryTest, AssertsCompiledSQL):
__dialect__ = "default"