util.py 79 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403
  1. # orm/util.py
  2. # Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  7. # mypy: allow-untyped-defs, allow-untyped-calls
  8. from __future__ import annotations
  9. import enum
  10. import functools
  11. import re
  12. import types
  13. import typing
  14. from typing import AbstractSet
  15. from typing import Any
  16. from typing import Callable
  17. from typing import cast
  18. from typing import Dict
  19. from typing import FrozenSet
  20. from typing import Generic
  21. from typing import Iterable
  22. from typing import Iterator
  23. from typing import List
  24. from typing import Match
  25. from typing import Optional
  26. from typing import Sequence
  27. from typing import Tuple
  28. from typing import Type
  29. from typing import TYPE_CHECKING
  30. from typing import TypeVar
  31. from typing import Union
  32. import weakref
  33. from . import attributes # noqa
  34. from . import exc
  35. from . import exc as orm_exc
  36. from ._typing import _O
  37. from ._typing import insp_is_aliased_class
  38. from ._typing import insp_is_mapper
  39. from ._typing import prop_is_relationship
  40. from .base import _class_to_mapper as _class_to_mapper
  41. from .base import _MappedAnnotationBase
  42. from .base import _never_set as _never_set # noqa: F401
  43. from .base import _none_only_set as _none_only_set # noqa: F401
  44. from .base import _none_set as _none_set # noqa: F401
  45. from .base import attribute_str as attribute_str # noqa: F401
  46. from .base import class_mapper as class_mapper
  47. from .base import DynamicMapped
  48. from .base import InspectionAttr as InspectionAttr
  49. from .base import instance_str as instance_str # noqa: F401
  50. from .base import Mapped
  51. from .base import object_mapper as object_mapper
  52. from .base import object_state as object_state # noqa: F401
  53. from .base import opt_manager_of_class
  54. from .base import ORMDescriptor
  55. from .base import state_attribute_str as state_attribute_str # noqa: F401
  56. from .base import state_class_str as state_class_str # noqa: F401
  57. from .base import state_str as state_str # noqa: F401
  58. from .base import WriteOnlyMapped
  59. from .interfaces import CriteriaOption
  60. from .interfaces import MapperProperty as MapperProperty
  61. from .interfaces import ORMColumnsClauseRole
  62. from .interfaces import ORMEntityColumnsClauseRole
  63. from .interfaces import ORMFromClauseRole
  64. from .path_registry import PathRegistry as PathRegistry
  65. from .. import event
  66. from .. import exc as sa_exc
  67. from .. import inspection
  68. from .. import sql
  69. from .. import util
  70. from ..engine.result import result_tuple
  71. from ..sql import coercions
  72. from ..sql import expression
  73. from ..sql import lambdas
  74. from ..sql import roles
  75. from ..sql import util as sql_util
  76. from ..sql import visitors
  77. from ..sql._typing import is_selectable
  78. from ..sql.annotation import SupportsCloneAnnotations
  79. from ..sql.base import ColumnCollection
  80. from ..sql.cache_key import HasCacheKey
  81. from ..sql.cache_key import MemoizedHasCacheKey
  82. from ..sql.elements import ColumnElement
  83. from ..sql.elements import KeyedColumnElement
  84. from ..sql.selectable import FromClause
  85. from ..util.langhelpers import MemoizedSlots
  86. from ..util.typing import de_stringify_annotation as _de_stringify_annotation
  87. from ..util.typing import eval_name_only as _eval_name_only
  88. from ..util.typing import fixup_container_fwd_refs
  89. from ..util.typing import get_origin
  90. from ..util.typing import is_origin_of_cls
  91. from ..util.typing import Literal
  92. from ..util.typing import Protocol
  93. if typing.TYPE_CHECKING:
  94. from ._typing import _EntityType
  95. from ._typing import _IdentityKeyType
  96. from ._typing import _InternalEntityType
  97. from ._typing import _ORMCOLEXPR
  98. from .context import _MapperEntity
  99. from .context import ORMCompileState
  100. from .mapper import Mapper
  101. from .path_registry import AbstractEntityRegistry
  102. from .query import Query
  103. from .relationships import RelationshipProperty
  104. from ..engine import Row
  105. from ..engine import RowMapping
  106. from ..sql._typing import _CE
  107. from ..sql._typing import _ColumnExpressionArgument
  108. from ..sql._typing import _EquivalentColumnMap
  109. from ..sql._typing import _FromClauseArgument
  110. from ..sql._typing import _OnClauseArgument
  111. from ..sql._typing import _PropagateAttrsType
  112. from ..sql.annotation import _SA
  113. from ..sql.base import ReadOnlyColumnCollection
  114. from ..sql.elements import BindParameter
  115. from ..sql.selectable import _ColumnsClauseElement
  116. from ..sql.selectable import Select
  117. from ..sql.selectable import Selectable
  118. from ..sql.visitors import anon_map
  119. from ..util.typing import _AnnotationScanType
  120. _T = TypeVar("_T", bound=Any)
  121. all_cascades = frozenset(
  122. (
  123. "delete",
  124. "delete-orphan",
  125. "all",
  126. "merge",
  127. "expunge",
  128. "save-update",
  129. "refresh-expire",
  130. "none",
  131. )
  132. )
  133. _de_stringify_partial = functools.partial(
  134. functools.partial,
  135. locals_=util.immutabledict(
  136. {
  137. "Mapped": Mapped,
  138. "WriteOnlyMapped": WriteOnlyMapped,
  139. "DynamicMapped": DynamicMapped,
  140. }
  141. ),
  142. )
  143. # partial is practically useless as we have to write out the whole
  144. # function and maintain the signature anyway
  145. class _DeStringifyAnnotation(Protocol):
  146. def __call__(
  147. self,
  148. cls: Type[Any],
  149. annotation: _AnnotationScanType,
  150. originating_module: str,
  151. *,
  152. str_cleanup_fn: Optional[Callable[[str, str], str]] = None,
  153. include_generic: bool = False,
  154. ) -> Type[Any]: ...
  155. de_stringify_annotation = cast(
  156. _DeStringifyAnnotation, _de_stringify_partial(_de_stringify_annotation)
  157. )
  158. class _EvalNameOnly(Protocol):
  159. def __call__(self, name: str, module_name: str) -> Any: ...
  160. eval_name_only = cast(_EvalNameOnly, _de_stringify_partial(_eval_name_only))
  161. class CascadeOptions(FrozenSet[str]):
  162. """Keeps track of the options sent to
  163. :paramref:`.relationship.cascade`"""
  164. _add_w_all_cascades = all_cascades.difference(
  165. ["all", "none", "delete-orphan"]
  166. )
  167. _allowed_cascades = all_cascades
  168. _viewonly_cascades = ["expunge", "all", "none", "refresh-expire", "merge"]
  169. __slots__ = (
  170. "save_update",
  171. "delete",
  172. "refresh_expire",
  173. "merge",
  174. "expunge",
  175. "delete_orphan",
  176. )
  177. save_update: bool
  178. delete: bool
  179. refresh_expire: bool
  180. merge: bool
  181. expunge: bool
  182. delete_orphan: bool
  183. def __new__(
  184. cls, value_list: Optional[Union[Iterable[str], str]]
  185. ) -> CascadeOptions:
  186. if isinstance(value_list, str) or value_list is None:
  187. return cls.from_string(value_list) # type: ignore
  188. values = set(value_list)
  189. if values.difference(cls._allowed_cascades):
  190. raise sa_exc.ArgumentError(
  191. "Invalid cascade option(s): %s"
  192. % ", ".join(
  193. [
  194. repr(x)
  195. for x in sorted(
  196. values.difference(cls._allowed_cascades)
  197. )
  198. ]
  199. )
  200. )
  201. if "all" in values:
  202. values.update(cls._add_w_all_cascades)
  203. if "none" in values:
  204. values.clear()
  205. values.discard("all")
  206. self = super().__new__(cls, values)
  207. self.save_update = "save-update" in values
  208. self.delete = "delete" in values
  209. self.refresh_expire = "refresh-expire" in values
  210. self.merge = "merge" in values
  211. self.expunge = "expunge" in values
  212. self.delete_orphan = "delete-orphan" in values
  213. if self.delete_orphan and not self.delete:
  214. util.warn("The 'delete-orphan' cascade option requires 'delete'.")
  215. return self
  216. def __repr__(self):
  217. return "CascadeOptions(%r)" % (",".join([x for x in sorted(self)]))
  218. @classmethod
  219. def from_string(cls, arg):
  220. values = [c for c in re.split(r"\s*,\s*", arg or "") if c]
  221. return cls(values)
  222. def _validator_events(desc, key, validator, include_removes, include_backrefs):
  223. """Runs a validation method on an attribute value to be set or
  224. appended.
  225. """
  226. if not include_backrefs:
  227. def detect_is_backref(state, initiator):
  228. impl = state.manager[key].impl
  229. return initiator.impl is not impl
  230. if include_removes:
  231. def append(state, value, initiator):
  232. if initiator.op is not attributes.OP_BULK_REPLACE and (
  233. include_backrefs or not detect_is_backref(state, initiator)
  234. ):
  235. return validator(state.obj(), key, value, False)
  236. else:
  237. return value
  238. def bulk_set(state, values, initiator):
  239. if include_backrefs or not detect_is_backref(state, initiator):
  240. obj = state.obj()
  241. values[:] = [
  242. validator(obj, key, value, False) for value in values
  243. ]
  244. def set_(state, value, oldvalue, initiator):
  245. if include_backrefs or not detect_is_backref(state, initiator):
  246. return validator(state.obj(), key, value, False)
  247. else:
  248. return value
  249. def remove(state, value, initiator):
  250. if include_backrefs or not detect_is_backref(state, initiator):
  251. validator(state.obj(), key, value, True)
  252. else:
  253. def append(state, value, initiator):
  254. if initiator.op is not attributes.OP_BULK_REPLACE and (
  255. include_backrefs or not detect_is_backref(state, initiator)
  256. ):
  257. return validator(state.obj(), key, value)
  258. else:
  259. return value
  260. def bulk_set(state, values, initiator):
  261. if include_backrefs or not detect_is_backref(state, initiator):
  262. obj = state.obj()
  263. values[:] = [validator(obj, key, value) for value in values]
  264. def set_(state, value, oldvalue, initiator):
  265. if include_backrefs or not detect_is_backref(state, initiator):
  266. return validator(state.obj(), key, value)
  267. else:
  268. return value
  269. event.listen(desc, "append", append, raw=True, retval=True)
  270. event.listen(desc, "bulk_replace", bulk_set, raw=True)
  271. event.listen(desc, "set", set_, raw=True, retval=True)
  272. if include_removes:
  273. event.listen(desc, "remove", remove, raw=True, retval=True)
  274. def polymorphic_union(
  275. table_map, typecolname, aliasname="p_union", cast_nulls=True
  276. ):
  277. """Create a ``UNION`` statement used by a polymorphic mapper.
  278. See :ref:`concrete_inheritance` for an example of how
  279. this is used.
  280. :param table_map: mapping of polymorphic identities to
  281. :class:`_schema.Table` objects.
  282. :param typecolname: string name of a "discriminator" column, which will be
  283. derived from the query, producing the polymorphic identity for
  284. each row. If ``None``, no polymorphic discriminator is generated.
  285. :param aliasname: name of the :func:`~sqlalchemy.sql.expression.alias()`
  286. construct generated.
  287. :param cast_nulls: if True, non-existent columns, which are represented
  288. as labeled NULLs, will be passed into CAST. This is a legacy behavior
  289. that is problematic on some backends such as Oracle - in which case it
  290. can be set to False.
  291. """
  292. colnames: util.OrderedSet[str] = util.OrderedSet()
  293. colnamemaps = {}
  294. types = {}
  295. for key in table_map:
  296. table = table_map[key]
  297. table = coercions.expect(
  298. roles.StrictFromClauseRole, table, allow_select=True
  299. )
  300. table_map[key] = table
  301. m = {}
  302. for c in table.c:
  303. if c.key == typecolname:
  304. raise sa_exc.InvalidRequestError(
  305. "Polymorphic union can't use '%s' as the discriminator "
  306. "column due to mapped column %r; please apply the "
  307. "'typecolname' "
  308. "argument; this is available on "
  309. "ConcreteBase as '_concrete_discriminator_name'"
  310. % (typecolname, c)
  311. )
  312. colnames.add(c.key)
  313. m[c.key] = c
  314. types[c.key] = c.type
  315. colnamemaps[table] = m
  316. def col(name, table):
  317. try:
  318. return colnamemaps[table][name]
  319. except KeyError:
  320. if cast_nulls:
  321. return sql.cast(sql.null(), types[name]).label(name)
  322. else:
  323. return sql.type_coerce(sql.null(), types[name]).label(name)
  324. result = []
  325. for type_, table in table_map.items():
  326. if typecolname is not None:
  327. result.append(
  328. sql.select(
  329. *(
  330. [col(name, table) for name in colnames]
  331. + [
  332. sql.literal_column(
  333. sql_util._quote_ddl_expr(type_)
  334. ).label(typecolname)
  335. ]
  336. )
  337. ).select_from(table)
  338. )
  339. else:
  340. result.append(
  341. sql.select(
  342. *[col(name, table) for name in colnames]
  343. ).select_from(table)
  344. )
  345. return sql.union_all(*result).alias(aliasname)
  346. def identity_key(
  347. class_: Optional[Type[_T]] = None,
  348. ident: Union[Any, Tuple[Any, ...]] = None,
  349. *,
  350. instance: Optional[_T] = None,
  351. row: Optional[Union[Row[Any], RowMapping]] = None,
  352. identity_token: Optional[Any] = None,
  353. ) -> _IdentityKeyType[_T]:
  354. r"""Generate "identity key" tuples, as are used as keys in the
  355. :attr:`.Session.identity_map` dictionary.
  356. This function has several call styles:
  357. * ``identity_key(class, ident, identity_token=token)``
  358. This form receives a mapped class and a primary key scalar or
  359. tuple as an argument.
  360. E.g.::
  361. >>> identity_key(MyClass, (1, 2))
  362. (<class '__main__.MyClass'>, (1, 2), None)
  363. :param class: mapped class (must be a positional argument)
  364. :param ident: primary key, may be a scalar or tuple argument.
  365. :param identity_token: optional identity token
  366. .. versionadded:: 1.2 added identity_token
  367. * ``identity_key(instance=instance)``
  368. This form will produce the identity key for a given instance. The
  369. instance need not be persistent, only that its primary key attributes
  370. are populated (else the key will contain ``None`` for those missing
  371. values).
  372. E.g.::
  373. >>> instance = MyClass(1, 2)
  374. >>> identity_key(instance=instance)
  375. (<class '__main__.MyClass'>, (1, 2), None)
  376. In this form, the given instance is ultimately run though
  377. :meth:`_orm.Mapper.identity_key_from_instance`, which will have the
  378. effect of performing a database check for the corresponding row
  379. if the object is expired.
  380. :param instance: object instance (must be given as a keyword arg)
  381. * ``identity_key(class, row=row, identity_token=token)``
  382. This form is similar to the class/tuple form, except is passed a
  383. database result row as a :class:`.Row` or :class:`.RowMapping` object.
  384. E.g.::
  385. >>> row = engine.execute(text("select * from table where a=1 and b=2")).first()
  386. >>> identity_key(MyClass, row=row)
  387. (<class '__main__.MyClass'>, (1, 2), None)
  388. :param class: mapped class (must be a positional argument)
  389. :param row: :class:`.Row` row returned by a :class:`_engine.CursorResult`
  390. (must be given as a keyword arg)
  391. :param identity_token: optional identity token
  392. .. versionadded:: 1.2 added identity_token
  393. """ # noqa: E501
  394. if class_ is not None:
  395. mapper = class_mapper(class_)
  396. if row is None:
  397. if ident is None:
  398. raise sa_exc.ArgumentError("ident or row is required")
  399. return mapper.identity_key_from_primary_key(
  400. tuple(util.to_list(ident)), identity_token=identity_token
  401. )
  402. else:
  403. return mapper.identity_key_from_row(
  404. row, identity_token=identity_token
  405. )
  406. elif instance is not None:
  407. mapper = object_mapper(instance)
  408. return mapper.identity_key_from_instance(instance)
  409. else:
  410. raise sa_exc.ArgumentError("class or instance is required")
  411. class _TraceAdaptRole(enum.Enum):
  412. """Enumeration of all the use cases for ORMAdapter.
  413. ORMAdapter remains one of the most complicated aspects of the ORM, as it is
  414. used for in-place adaption of column expressions to be applied to a SELECT,
  415. replacing :class:`.Table` and other objects that are mapped to classes with
  416. aliases of those tables in the case of joined eager loading, or in the case
  417. of polymorphic loading as used with concrete mappings or other custom "with
  418. polymorphic" parameters, with whole user-defined subqueries. The
  419. enumerations provide an overview of all the use cases used by ORMAdapter, a
  420. layer of formality as to the introduction of new ORMAdapter use cases (of
  421. which none are anticipated), as well as a means to trace the origins of a
  422. particular ORMAdapter within runtime debugging.
  423. SQLAlchemy 2.0 has greatly scaled back ORM features which relied heavily on
  424. open-ended statement adaption, including the ``Query.with_polymorphic()``
  425. method and the ``Query.select_from_entity()`` methods, favoring
  426. user-explicit aliasing schemes using the ``aliased()`` and
  427. ``with_polymorphic()`` standalone constructs; these still use adaption,
  428. however the adaption is applied in a narrower scope.
  429. """
  430. # aliased() use that is used to adapt individual attributes at query
  431. # construction time
  432. ALIASED_INSP = enum.auto()
  433. # joinedload cases; typically adapt an ON clause of a relationship
  434. # join
  435. JOINEDLOAD_USER_DEFINED_ALIAS = enum.auto()
  436. JOINEDLOAD_PATH_WITH_POLYMORPHIC = enum.auto()
  437. JOINEDLOAD_MEMOIZED_ADAPTER = enum.auto()
  438. # polymorphic cases - these are complex ones that replace FROM
  439. # clauses, replacing tables with subqueries
  440. MAPPER_POLYMORPHIC_ADAPTER = enum.auto()
  441. WITH_POLYMORPHIC_ADAPTER = enum.auto()
  442. WITH_POLYMORPHIC_ADAPTER_RIGHT_JOIN = enum.auto()
  443. DEPRECATED_JOIN_ADAPT_RIGHT_SIDE = enum.auto()
  444. # the from_statement() case, used only to adapt individual attributes
  445. # from a given statement to local ORM attributes at result fetching
  446. # time. assigned to ORMCompileState._from_obj_alias
  447. ADAPT_FROM_STATEMENT = enum.auto()
  448. # the joinedload for queries that have LIMIT/OFFSET/DISTINCT case;
  449. # the query is placed inside of a subquery with the LIMIT/OFFSET/etc.,
  450. # joinedloads are then placed on the outside.
  451. # assigned to ORMCompileState.compound_eager_adapter
  452. COMPOUND_EAGER_STATEMENT = enum.auto()
  453. # the legacy Query._set_select_from() case.
  454. # this is needed for Query's set operations (i.e. UNION, etc. )
  455. # as well as "legacy from_self()", which while removed from 2.0 as
  456. # public API, is used for the Query.count() method. this one
  457. # still does full statement traversal
  458. # assigned to ORMCompileState._from_obj_alias
  459. LEGACY_SELECT_FROM_ALIAS = enum.auto()
  460. class ORMStatementAdapter(sql_util.ColumnAdapter):
  461. """ColumnAdapter which includes a role attribute."""
  462. __slots__ = ("role",)
  463. def __init__(
  464. self,
  465. role: _TraceAdaptRole,
  466. selectable: Selectable,
  467. *,
  468. equivalents: Optional[_EquivalentColumnMap] = None,
  469. adapt_required: bool = False,
  470. allow_label_resolve: bool = True,
  471. anonymize_labels: bool = False,
  472. adapt_on_names: bool = False,
  473. adapt_from_selectables: Optional[AbstractSet[FromClause]] = None,
  474. ):
  475. self.role = role
  476. super().__init__(
  477. selectable,
  478. equivalents=equivalents,
  479. adapt_required=adapt_required,
  480. allow_label_resolve=allow_label_resolve,
  481. anonymize_labels=anonymize_labels,
  482. adapt_on_names=adapt_on_names,
  483. adapt_from_selectables=adapt_from_selectables,
  484. )
  485. class ORMAdapter(sql_util.ColumnAdapter):
  486. """ColumnAdapter subclass which excludes adaptation of entities from
  487. non-matching mappers.
  488. """
  489. __slots__ = ("role", "mapper", "is_aliased_class", "aliased_insp")
  490. is_aliased_class: bool
  491. aliased_insp: Optional[AliasedInsp[Any]]
  492. def __init__(
  493. self,
  494. role: _TraceAdaptRole,
  495. entity: _InternalEntityType[Any],
  496. *,
  497. equivalents: Optional[_EquivalentColumnMap] = None,
  498. adapt_required: bool = False,
  499. allow_label_resolve: bool = True,
  500. anonymize_labels: bool = False,
  501. selectable: Optional[Selectable] = None,
  502. limit_on_entity: bool = True,
  503. adapt_on_names: bool = False,
  504. adapt_from_selectables: Optional[AbstractSet[FromClause]] = None,
  505. ):
  506. self.role = role
  507. self.mapper = entity.mapper
  508. if selectable is None:
  509. selectable = entity.selectable
  510. if insp_is_aliased_class(entity):
  511. self.is_aliased_class = True
  512. self.aliased_insp = entity
  513. else:
  514. self.is_aliased_class = False
  515. self.aliased_insp = None
  516. super().__init__(
  517. selectable,
  518. equivalents,
  519. adapt_required=adapt_required,
  520. allow_label_resolve=allow_label_resolve,
  521. anonymize_labels=anonymize_labels,
  522. include_fn=self._include_fn if limit_on_entity else None,
  523. adapt_on_names=adapt_on_names,
  524. adapt_from_selectables=adapt_from_selectables,
  525. )
  526. def _include_fn(self, elem):
  527. entity = elem._annotations.get("parentmapper", None)
  528. return not entity or entity.isa(self.mapper) or self.mapper.isa(entity)
  529. class AliasedClass(
  530. inspection.Inspectable["AliasedInsp[_O]"], ORMColumnsClauseRole[_O]
  531. ):
  532. r"""Represents an "aliased" form of a mapped class for usage with Query.
  533. The ORM equivalent of a :func:`~sqlalchemy.sql.expression.alias`
  534. construct, this object mimics the mapped class using a
  535. ``__getattr__`` scheme and maintains a reference to a
  536. real :class:`~sqlalchemy.sql.expression.Alias` object.
  537. A primary purpose of :class:`.AliasedClass` is to serve as an alternate
  538. within a SQL statement generated by the ORM, such that an existing
  539. mapped entity can be used in multiple contexts. A simple example::
  540. # find all pairs of users with the same name
  541. user_alias = aliased(User)
  542. session.query(User, user_alias).join(
  543. (user_alias, User.id > user_alias.id)
  544. ).filter(User.name == user_alias.name)
  545. :class:`.AliasedClass` is also capable of mapping an existing mapped
  546. class to an entirely new selectable, provided this selectable is column-
  547. compatible with the existing mapped selectable, and it can also be
  548. configured in a mapping as the target of a :func:`_orm.relationship`.
  549. See the links below for examples.
  550. The :class:`.AliasedClass` object is constructed typically using the
  551. :func:`_orm.aliased` function. It also is produced with additional
  552. configuration when using the :func:`_orm.with_polymorphic` function.
  553. The resulting object is an instance of :class:`.AliasedClass`.
  554. This object implements an attribute scheme which produces the
  555. same attribute and method interface as the original mapped
  556. class, allowing :class:`.AliasedClass` to be compatible
  557. with any attribute technique which works on the original class,
  558. including hybrid attributes (see :ref:`hybrids_toplevel`).
  559. The :class:`.AliasedClass` can be inspected for its underlying
  560. :class:`_orm.Mapper`, aliased selectable, and other information
  561. using :func:`_sa.inspect`::
  562. from sqlalchemy import inspect
  563. my_alias = aliased(MyClass)
  564. insp = inspect(my_alias)
  565. The resulting inspection object is an instance of :class:`.AliasedInsp`.
  566. .. seealso::
  567. :func:`.aliased`
  568. :func:`.with_polymorphic`
  569. :ref:`relationship_aliased_class`
  570. :ref:`relationship_to_window_function`
  571. """
  572. __name__: str
  573. def __init__(
  574. self,
  575. mapped_class_or_ac: _EntityType[_O],
  576. alias: Optional[FromClause] = None,
  577. name: Optional[str] = None,
  578. flat: bool = False,
  579. adapt_on_names: bool = False,
  580. with_polymorphic_mappers: Optional[Sequence[Mapper[Any]]] = None,
  581. with_polymorphic_discriminator: Optional[ColumnElement[Any]] = None,
  582. base_alias: Optional[AliasedInsp[Any]] = None,
  583. use_mapper_path: bool = False,
  584. represents_outer_join: bool = False,
  585. ):
  586. insp = cast(
  587. "_InternalEntityType[_O]", inspection.inspect(mapped_class_or_ac)
  588. )
  589. mapper = insp.mapper
  590. nest_adapters = False
  591. if alias is None:
  592. if insp.is_aliased_class and insp.selectable._is_subquery:
  593. alias = insp.selectable.alias()
  594. else:
  595. alias = (
  596. mapper._with_polymorphic_selectable._anonymous_fromclause(
  597. name=name,
  598. flat=flat,
  599. )
  600. )
  601. elif insp.is_aliased_class:
  602. nest_adapters = True
  603. assert alias is not None
  604. self._aliased_insp = AliasedInsp(
  605. self,
  606. insp,
  607. alias,
  608. name,
  609. (
  610. with_polymorphic_mappers
  611. if with_polymorphic_mappers
  612. else mapper.with_polymorphic_mappers
  613. ),
  614. (
  615. with_polymorphic_discriminator
  616. if with_polymorphic_discriminator is not None
  617. else mapper.polymorphic_on
  618. ),
  619. base_alias,
  620. use_mapper_path,
  621. adapt_on_names,
  622. represents_outer_join,
  623. nest_adapters,
  624. )
  625. self.__name__ = f"aliased({mapper.class_.__name__})"
  626. @classmethod
  627. def _reconstitute_from_aliased_insp(
  628. cls, aliased_insp: AliasedInsp[_O]
  629. ) -> AliasedClass[_O]:
  630. obj = cls.__new__(cls)
  631. obj.__name__ = f"aliased({aliased_insp.mapper.class_.__name__})"
  632. obj._aliased_insp = aliased_insp
  633. if aliased_insp._is_with_polymorphic:
  634. for sub_aliased_insp in aliased_insp._with_polymorphic_entities:
  635. if sub_aliased_insp is not aliased_insp:
  636. ent = AliasedClass._reconstitute_from_aliased_insp(
  637. sub_aliased_insp
  638. )
  639. setattr(obj, sub_aliased_insp.class_.__name__, ent)
  640. return obj
  641. def __getattr__(self, key: str) -> Any:
  642. try:
  643. _aliased_insp = self.__dict__["_aliased_insp"]
  644. except KeyError:
  645. raise AttributeError()
  646. else:
  647. target = _aliased_insp._target
  648. # maintain all getattr mechanics
  649. attr = getattr(target, key)
  650. # attribute is a method, that will be invoked against a
  651. # "self"; so just return a new method with the same function and
  652. # new self
  653. if hasattr(attr, "__call__") and hasattr(attr, "__self__"):
  654. return types.MethodType(attr.__func__, self)
  655. # attribute is a descriptor, that will be invoked against a
  656. # "self"; so invoke the descriptor against this self
  657. if hasattr(attr, "__get__"):
  658. attr = attr.__get__(None, self)
  659. # attributes within the QueryableAttribute system will want this
  660. # to be invoked so the object can be adapted
  661. if hasattr(attr, "adapt_to_entity"):
  662. attr = attr.adapt_to_entity(_aliased_insp)
  663. setattr(self, key, attr)
  664. return attr
  665. def _get_from_serialized(
  666. self, key: str, mapped_class: _O, aliased_insp: AliasedInsp[_O]
  667. ) -> Any:
  668. # this method is only used in terms of the
  669. # sqlalchemy.ext.serializer extension
  670. attr = getattr(mapped_class, key)
  671. if hasattr(attr, "__call__") and hasattr(attr, "__self__"):
  672. return types.MethodType(attr.__func__, self)
  673. # attribute is a descriptor, that will be invoked against a
  674. # "self"; so invoke the descriptor against this self
  675. if hasattr(attr, "__get__"):
  676. attr = attr.__get__(None, self)
  677. # attributes within the QueryableAttribute system will want this
  678. # to be invoked so the object can be adapted
  679. if hasattr(attr, "adapt_to_entity"):
  680. aliased_insp._weak_entity = weakref.ref(self)
  681. attr = attr.adapt_to_entity(aliased_insp)
  682. setattr(self, key, attr)
  683. return attr
  684. def __repr__(self) -> str:
  685. return "<AliasedClass at 0x%x; %s>" % (
  686. id(self),
  687. self._aliased_insp._target.__name__,
  688. )
  689. def __str__(self) -> str:
  690. return str(self._aliased_insp)
  691. @inspection._self_inspects
  692. class AliasedInsp(
  693. ORMEntityColumnsClauseRole[_O],
  694. ORMFromClauseRole,
  695. HasCacheKey,
  696. InspectionAttr,
  697. MemoizedSlots,
  698. inspection.Inspectable["AliasedInsp[_O]"],
  699. Generic[_O],
  700. ):
  701. """Provide an inspection interface for an
  702. :class:`.AliasedClass` object.
  703. The :class:`.AliasedInsp` object is returned
  704. given an :class:`.AliasedClass` using the
  705. :func:`_sa.inspect` function::
  706. from sqlalchemy import inspect
  707. from sqlalchemy.orm import aliased
  708. my_alias = aliased(MyMappedClass)
  709. insp = inspect(my_alias)
  710. Attributes on :class:`.AliasedInsp`
  711. include:
  712. * ``entity`` - the :class:`.AliasedClass` represented.
  713. * ``mapper`` - the :class:`_orm.Mapper` mapping the underlying class.
  714. * ``selectable`` - the :class:`_expression.Alias`
  715. construct which ultimately
  716. represents an aliased :class:`_schema.Table` or
  717. :class:`_expression.Select`
  718. construct.
  719. * ``name`` - the name of the alias. Also is used as the attribute
  720. name when returned in a result tuple from :class:`_query.Query`.
  721. * ``with_polymorphic_mappers`` - collection of :class:`_orm.Mapper`
  722. objects
  723. indicating all those mappers expressed in the select construct
  724. for the :class:`.AliasedClass`.
  725. * ``polymorphic_on`` - an alternate column or SQL expression which
  726. will be used as the "discriminator" for a polymorphic load.
  727. .. seealso::
  728. :ref:`inspection_toplevel`
  729. """
  730. __slots__ = (
  731. "__weakref__",
  732. "_weak_entity",
  733. "mapper",
  734. "selectable",
  735. "name",
  736. "_adapt_on_names",
  737. "with_polymorphic_mappers",
  738. "polymorphic_on",
  739. "_use_mapper_path",
  740. "_base_alias",
  741. "represents_outer_join",
  742. "persist_selectable",
  743. "local_table",
  744. "_is_with_polymorphic",
  745. "_with_polymorphic_entities",
  746. "_adapter",
  747. "_target",
  748. "__clause_element__",
  749. "_memoized_values",
  750. "_all_column_expressions",
  751. "_nest_adapters",
  752. )
  753. _cache_key_traversal = [
  754. ("name", visitors.ExtendedInternalTraversal.dp_string),
  755. ("_adapt_on_names", visitors.ExtendedInternalTraversal.dp_boolean),
  756. ("_use_mapper_path", visitors.ExtendedInternalTraversal.dp_boolean),
  757. ("_target", visitors.ExtendedInternalTraversal.dp_inspectable),
  758. ("selectable", visitors.ExtendedInternalTraversal.dp_clauseelement),
  759. (
  760. "with_polymorphic_mappers",
  761. visitors.InternalTraversal.dp_has_cache_key_list,
  762. ),
  763. ("polymorphic_on", visitors.InternalTraversal.dp_clauseelement),
  764. ]
  765. mapper: Mapper[_O]
  766. selectable: FromClause
  767. _adapter: ORMAdapter
  768. with_polymorphic_mappers: Sequence[Mapper[Any]]
  769. _with_polymorphic_entities: Sequence[AliasedInsp[Any]]
  770. _weak_entity: weakref.ref[AliasedClass[_O]]
  771. """the AliasedClass that refers to this AliasedInsp"""
  772. _target: Union[Type[_O], AliasedClass[_O]]
  773. """the thing referenced by the AliasedClass/AliasedInsp.
  774. In the vast majority of cases, this is the mapped class. However
  775. it may also be another AliasedClass (alias of alias).
  776. """
  777. def __init__(
  778. self,
  779. entity: AliasedClass[_O],
  780. inspected: _InternalEntityType[_O],
  781. selectable: FromClause,
  782. name: Optional[str],
  783. with_polymorphic_mappers: Optional[Sequence[Mapper[Any]]],
  784. polymorphic_on: Optional[ColumnElement[Any]],
  785. _base_alias: Optional[AliasedInsp[Any]],
  786. _use_mapper_path: bool,
  787. adapt_on_names: bool,
  788. represents_outer_join: bool,
  789. nest_adapters: bool,
  790. ):
  791. mapped_class_or_ac = inspected.entity
  792. mapper = inspected.mapper
  793. self._weak_entity = weakref.ref(entity)
  794. self.mapper = mapper
  795. self.selectable = self.persist_selectable = self.local_table = (
  796. selectable
  797. )
  798. self.name = name
  799. self.polymorphic_on = polymorphic_on
  800. self._base_alias = weakref.ref(_base_alias or self)
  801. self._use_mapper_path = _use_mapper_path
  802. self.represents_outer_join = represents_outer_join
  803. self._nest_adapters = nest_adapters
  804. if with_polymorphic_mappers:
  805. self._is_with_polymorphic = True
  806. self.with_polymorphic_mappers = with_polymorphic_mappers
  807. self._with_polymorphic_entities = []
  808. for poly in self.with_polymorphic_mappers:
  809. if poly is not mapper:
  810. ent = AliasedClass(
  811. poly.class_,
  812. selectable,
  813. base_alias=self,
  814. adapt_on_names=adapt_on_names,
  815. use_mapper_path=_use_mapper_path,
  816. )
  817. setattr(self.entity, poly.class_.__name__, ent)
  818. self._with_polymorphic_entities.append(ent._aliased_insp)
  819. else:
  820. self._is_with_polymorphic = False
  821. self.with_polymorphic_mappers = [mapper]
  822. self._adapter = ORMAdapter(
  823. _TraceAdaptRole.ALIASED_INSP,
  824. mapper,
  825. selectable=selectable,
  826. equivalents=mapper._equivalent_columns,
  827. adapt_on_names=adapt_on_names,
  828. anonymize_labels=True,
  829. # make sure the adapter doesn't try to grab other tables that
  830. # are not even the thing we are mapping, such as embedded
  831. # selectables in subqueries or CTEs. See issue #6060
  832. adapt_from_selectables={
  833. m.selectable
  834. for m in self.with_polymorphic_mappers
  835. if not adapt_on_names
  836. },
  837. limit_on_entity=False,
  838. )
  839. if nest_adapters:
  840. # supports "aliased class of aliased class" use case
  841. assert isinstance(inspected, AliasedInsp)
  842. self._adapter = inspected._adapter.wrap(self._adapter)
  843. self._adapt_on_names = adapt_on_names
  844. self._target = mapped_class_or_ac
  845. @classmethod
  846. def _alias_factory(
  847. cls,
  848. element: Union[_EntityType[_O], FromClause],
  849. alias: Optional[FromClause] = None,
  850. name: Optional[str] = None,
  851. flat: bool = False,
  852. adapt_on_names: bool = False,
  853. ) -> Union[AliasedClass[_O], FromClause]:
  854. if isinstance(element, FromClause):
  855. if adapt_on_names:
  856. raise sa_exc.ArgumentError(
  857. "adapt_on_names only applies to ORM elements"
  858. )
  859. if name:
  860. return element.alias(name=name, flat=flat)
  861. else:
  862. return coercions.expect(
  863. roles.AnonymizedFromClauseRole, element, flat=flat
  864. )
  865. else:
  866. return AliasedClass(
  867. element,
  868. alias=alias,
  869. flat=flat,
  870. name=name,
  871. adapt_on_names=adapt_on_names,
  872. )
  873. @classmethod
  874. def _with_polymorphic_factory(
  875. cls,
  876. base: Union[Type[_O], Mapper[_O]],
  877. classes: Union[Literal["*"], Iterable[_EntityType[Any]]],
  878. selectable: Union[Literal[False, None], FromClause] = False,
  879. flat: bool = False,
  880. polymorphic_on: Optional[ColumnElement[Any]] = None,
  881. aliased: bool = False,
  882. innerjoin: bool = False,
  883. adapt_on_names: bool = False,
  884. name: Optional[str] = None,
  885. _use_mapper_path: bool = False,
  886. ) -> AliasedClass[_O]:
  887. primary_mapper = _class_to_mapper(base)
  888. if selectable not in (None, False) and flat:
  889. raise sa_exc.ArgumentError(
  890. "the 'flat' and 'selectable' arguments cannot be passed "
  891. "simultaneously to with_polymorphic()"
  892. )
  893. mappers, selectable = primary_mapper._with_polymorphic_args(
  894. classes, selectable, innerjoin=innerjoin
  895. )
  896. if aliased or flat:
  897. assert selectable is not None
  898. selectable = selectable._anonymous_fromclause(flat=flat)
  899. return AliasedClass(
  900. base,
  901. selectable,
  902. name=name,
  903. with_polymorphic_mappers=mappers,
  904. adapt_on_names=adapt_on_names,
  905. with_polymorphic_discriminator=polymorphic_on,
  906. use_mapper_path=_use_mapper_path,
  907. represents_outer_join=not innerjoin,
  908. )
  909. @property
  910. def entity(self) -> AliasedClass[_O]:
  911. # to eliminate reference cycles, the AliasedClass is held weakly.
  912. # this produces some situations where the AliasedClass gets lost,
  913. # particularly when one is created internally and only the AliasedInsp
  914. # is passed around.
  915. # to work around this case, we just generate a new one when we need
  916. # it, as it is a simple class with very little initial state on it.
  917. ent = self._weak_entity()
  918. if ent is None:
  919. ent = AliasedClass._reconstitute_from_aliased_insp(self)
  920. self._weak_entity = weakref.ref(ent)
  921. return ent
  922. is_aliased_class = True
  923. "always returns True"
  924. def _memoized_method___clause_element__(self) -> FromClause:
  925. return self.selectable._annotate(
  926. {
  927. "parentmapper": self.mapper,
  928. "parententity": self,
  929. "entity_namespace": self,
  930. }
  931. )._set_propagate_attrs(
  932. {"compile_state_plugin": "orm", "plugin_subject": self}
  933. )
  934. @property
  935. def entity_namespace(self) -> AliasedClass[_O]:
  936. return self.entity
  937. @property
  938. def class_(self) -> Type[_O]:
  939. """Return the mapped class ultimately represented by this
  940. :class:`.AliasedInsp`."""
  941. return self.mapper.class_
  942. @property
  943. def _path_registry(self) -> AbstractEntityRegistry:
  944. if self._use_mapper_path:
  945. return self.mapper._path_registry
  946. else:
  947. return PathRegistry.per_mapper(self)
  948. def __getstate__(self) -> Dict[str, Any]:
  949. return {
  950. "entity": self.entity,
  951. "mapper": self.mapper,
  952. "alias": self.selectable,
  953. "name": self.name,
  954. "adapt_on_names": self._adapt_on_names,
  955. "with_polymorphic_mappers": self.with_polymorphic_mappers,
  956. "with_polymorphic_discriminator": self.polymorphic_on,
  957. "base_alias": self._base_alias(),
  958. "use_mapper_path": self._use_mapper_path,
  959. "represents_outer_join": self.represents_outer_join,
  960. "nest_adapters": self._nest_adapters,
  961. }
  962. def __setstate__(self, state: Dict[str, Any]) -> None:
  963. self.__init__( # type: ignore
  964. state["entity"],
  965. state["mapper"],
  966. state["alias"],
  967. state["name"],
  968. state["with_polymorphic_mappers"],
  969. state["with_polymorphic_discriminator"],
  970. state["base_alias"],
  971. state["use_mapper_path"],
  972. state["adapt_on_names"],
  973. state["represents_outer_join"],
  974. state["nest_adapters"],
  975. )
  976. def _merge_with(self, other: AliasedInsp[_O]) -> AliasedInsp[_O]:
  977. # assert self._is_with_polymorphic
  978. # assert other._is_with_polymorphic
  979. primary_mapper = other.mapper
  980. assert self.mapper is primary_mapper
  981. our_classes = util.to_set(
  982. mp.class_ for mp in self.with_polymorphic_mappers
  983. )
  984. new_classes = {mp.class_ for mp in other.with_polymorphic_mappers}
  985. if our_classes == new_classes:
  986. return other
  987. else:
  988. classes = our_classes.union(new_classes)
  989. mappers, selectable = primary_mapper._with_polymorphic_args(
  990. classes, None, innerjoin=not other.represents_outer_join
  991. )
  992. selectable = selectable._anonymous_fromclause(flat=True)
  993. return AliasedClass(
  994. primary_mapper,
  995. selectable,
  996. with_polymorphic_mappers=mappers,
  997. with_polymorphic_discriminator=other.polymorphic_on,
  998. use_mapper_path=other._use_mapper_path,
  999. represents_outer_join=other.represents_outer_join,
  1000. )._aliased_insp
  1001. def _adapt_element(
  1002. self, expr: _ORMCOLEXPR, key: Optional[str] = None
  1003. ) -> _ORMCOLEXPR:
  1004. assert isinstance(expr, ColumnElement)
  1005. d: Dict[str, Any] = {
  1006. "parententity": self,
  1007. "parentmapper": self.mapper,
  1008. }
  1009. if key:
  1010. d["proxy_key"] = key
  1011. # IMO mypy should see this one also as returning the same type
  1012. # we put into it, but it's not
  1013. return (
  1014. self._adapter.traverse(expr)
  1015. ._annotate(d)
  1016. ._set_propagate_attrs(
  1017. {"compile_state_plugin": "orm", "plugin_subject": self}
  1018. )
  1019. )
  1020. if TYPE_CHECKING:
  1021. # establish compatibility with the _ORMAdapterProto protocol,
  1022. # which in turn is compatible with _CoreAdapterProto.
  1023. def _orm_adapt_element(
  1024. self,
  1025. obj: _CE,
  1026. key: Optional[str] = None,
  1027. ) -> _CE: ...
  1028. else:
  1029. _orm_adapt_element = _adapt_element
  1030. def _entity_for_mapper(self, mapper):
  1031. self_poly = self.with_polymorphic_mappers
  1032. if mapper in self_poly:
  1033. if mapper is self.mapper:
  1034. return self
  1035. else:
  1036. return getattr(
  1037. self.entity, mapper.class_.__name__
  1038. )._aliased_insp
  1039. elif mapper.isa(self.mapper):
  1040. return self
  1041. else:
  1042. assert False, "mapper %s doesn't correspond to %s" % (mapper, self)
  1043. def _memoized_attr__get_clause(self):
  1044. onclause, replacemap = self.mapper._get_clause
  1045. return (
  1046. self._adapter.traverse(onclause),
  1047. {
  1048. self._adapter.traverse(col): param
  1049. for col, param in replacemap.items()
  1050. },
  1051. )
  1052. def _memoized_attr__memoized_values(self):
  1053. return {}
  1054. def _memoized_attr__all_column_expressions(self):
  1055. if self._is_with_polymorphic:
  1056. cols_plus_keys = self.mapper._columns_plus_keys(
  1057. [ent.mapper for ent in self._with_polymorphic_entities]
  1058. )
  1059. else:
  1060. cols_plus_keys = self.mapper._columns_plus_keys()
  1061. cols_plus_keys = [
  1062. (key, self._adapt_element(col)) for key, col in cols_plus_keys
  1063. ]
  1064. return ColumnCollection(cols_plus_keys)
  1065. def _memo(self, key, callable_, *args, **kw):
  1066. if key in self._memoized_values:
  1067. return self._memoized_values[key]
  1068. else:
  1069. self._memoized_values[key] = value = callable_(*args, **kw)
  1070. return value
  1071. def __repr__(self):
  1072. if self.with_polymorphic_mappers:
  1073. with_poly = "(%s)" % ", ".join(
  1074. mp.class_.__name__ for mp in self.with_polymorphic_mappers
  1075. )
  1076. else:
  1077. with_poly = ""
  1078. return "<AliasedInsp at 0x%x; %s%s>" % (
  1079. id(self),
  1080. self.class_.__name__,
  1081. with_poly,
  1082. )
  1083. def __str__(self):
  1084. if self._is_with_polymorphic:
  1085. return "with_polymorphic(%s, [%s])" % (
  1086. self._target.__name__,
  1087. ", ".join(
  1088. mp.class_.__name__
  1089. for mp in self.with_polymorphic_mappers
  1090. if mp is not self.mapper
  1091. ),
  1092. )
  1093. else:
  1094. return "aliased(%s)" % (self._target.__name__,)
  1095. class _WrapUserEntity:
  1096. """A wrapper used within the loader_criteria lambda caller so that
  1097. we can bypass declared_attr descriptors on unmapped mixins, which
  1098. normally emit a warning for such use.
  1099. might also be useful for other per-lambda instrumentations should
  1100. the need arise.
  1101. """
  1102. __slots__ = ("subject",)
  1103. def __init__(self, subject):
  1104. self.subject = subject
  1105. @util.preload_module("sqlalchemy.orm.decl_api")
  1106. def __getattribute__(self, name):
  1107. decl_api = util.preloaded.orm.decl_api
  1108. subject = object.__getattribute__(self, "subject")
  1109. if name in subject.__dict__ and isinstance(
  1110. subject.__dict__[name], decl_api.declared_attr
  1111. ):
  1112. return subject.__dict__[name].fget(subject)
  1113. else:
  1114. return getattr(subject, name)
  1115. class LoaderCriteriaOption(CriteriaOption):
  1116. """Add additional WHERE criteria to the load for all occurrences of
  1117. a particular entity.
  1118. :class:`_orm.LoaderCriteriaOption` is invoked using the
  1119. :func:`_orm.with_loader_criteria` function; see that function for
  1120. details.
  1121. .. versionadded:: 1.4
  1122. """
  1123. __slots__ = (
  1124. "root_entity",
  1125. "entity",
  1126. "deferred_where_criteria",
  1127. "where_criteria",
  1128. "_where_crit_orig",
  1129. "include_aliases",
  1130. "propagate_to_loaders",
  1131. )
  1132. _traverse_internals = [
  1133. ("root_entity", visitors.ExtendedInternalTraversal.dp_plain_obj),
  1134. ("entity", visitors.ExtendedInternalTraversal.dp_has_cache_key),
  1135. ("where_criteria", visitors.InternalTraversal.dp_clauseelement),
  1136. ("include_aliases", visitors.InternalTraversal.dp_boolean),
  1137. ("propagate_to_loaders", visitors.InternalTraversal.dp_boolean),
  1138. ]
  1139. root_entity: Optional[Type[Any]]
  1140. entity: Optional[_InternalEntityType[Any]]
  1141. where_criteria: Union[ColumnElement[bool], lambdas.DeferredLambdaElement]
  1142. deferred_where_criteria: bool
  1143. include_aliases: bool
  1144. propagate_to_loaders: bool
  1145. _where_crit_orig: Any
  1146. def __init__(
  1147. self,
  1148. entity_or_base: _EntityType[Any],
  1149. where_criteria: Union[
  1150. _ColumnExpressionArgument[bool],
  1151. Callable[[Any], _ColumnExpressionArgument[bool]],
  1152. ],
  1153. loader_only: bool = False,
  1154. include_aliases: bool = False,
  1155. propagate_to_loaders: bool = True,
  1156. track_closure_variables: bool = True,
  1157. ):
  1158. entity = cast(
  1159. "_InternalEntityType[Any]",
  1160. inspection.inspect(entity_or_base, False),
  1161. )
  1162. if entity is None:
  1163. self.root_entity = cast("Type[Any]", entity_or_base)
  1164. self.entity = None
  1165. else:
  1166. self.root_entity = None
  1167. self.entity = entity
  1168. self._where_crit_orig = where_criteria
  1169. if callable(where_criteria):
  1170. if self.root_entity is not None:
  1171. wrap_entity = self.root_entity
  1172. else:
  1173. assert entity is not None
  1174. wrap_entity = entity.entity
  1175. self.deferred_where_criteria = True
  1176. self.where_criteria = lambdas.DeferredLambdaElement(
  1177. where_criteria,
  1178. roles.WhereHavingRole,
  1179. lambda_args=(_WrapUserEntity(wrap_entity),),
  1180. opts=lambdas.LambdaOptions(
  1181. track_closure_variables=track_closure_variables
  1182. ),
  1183. )
  1184. else:
  1185. self.deferred_where_criteria = False
  1186. self.where_criteria = coercions.expect(
  1187. roles.WhereHavingRole, where_criteria
  1188. )
  1189. self.include_aliases = include_aliases
  1190. self.propagate_to_loaders = propagate_to_loaders
  1191. @classmethod
  1192. def _unreduce(
  1193. cls, entity, where_criteria, include_aliases, propagate_to_loaders
  1194. ):
  1195. return LoaderCriteriaOption(
  1196. entity,
  1197. where_criteria,
  1198. include_aliases=include_aliases,
  1199. propagate_to_loaders=propagate_to_loaders,
  1200. )
  1201. def __reduce__(self):
  1202. return (
  1203. LoaderCriteriaOption._unreduce,
  1204. (
  1205. self.entity.class_ if self.entity else self.root_entity,
  1206. self._where_crit_orig,
  1207. self.include_aliases,
  1208. self.propagate_to_loaders,
  1209. ),
  1210. )
  1211. def _all_mappers(self) -> Iterator[Mapper[Any]]:
  1212. if self.entity:
  1213. yield from self.entity.mapper.self_and_descendants
  1214. else:
  1215. assert self.root_entity
  1216. stack = list(self.root_entity.__subclasses__())
  1217. while stack:
  1218. subclass = stack.pop(0)
  1219. ent = cast(
  1220. "_InternalEntityType[Any]",
  1221. inspection.inspect(subclass, raiseerr=False),
  1222. )
  1223. if ent:
  1224. yield from ent.mapper.self_and_descendants
  1225. else:
  1226. stack.extend(subclass.__subclasses__())
  1227. def _should_include(self, compile_state: ORMCompileState) -> bool:
  1228. if (
  1229. compile_state.select_statement._annotations.get(
  1230. "for_loader_criteria", None
  1231. )
  1232. is self
  1233. ):
  1234. return False
  1235. return True
  1236. def _resolve_where_criteria(
  1237. self, ext_info: _InternalEntityType[Any]
  1238. ) -> ColumnElement[bool]:
  1239. if self.deferred_where_criteria:
  1240. crit = cast(
  1241. "ColumnElement[bool]",
  1242. self.where_criteria._resolve_with_args(ext_info.entity),
  1243. )
  1244. else:
  1245. crit = self.where_criteria # type: ignore
  1246. assert isinstance(crit, ColumnElement)
  1247. return sql_util._deep_annotate(
  1248. crit,
  1249. {"for_loader_criteria": self},
  1250. detect_subquery_cols=True,
  1251. ind_cols_on_fromclause=True,
  1252. )
  1253. def process_compile_state_replaced_entities(
  1254. self,
  1255. compile_state: ORMCompileState,
  1256. mapper_entities: Iterable[_MapperEntity],
  1257. ) -> None:
  1258. self.process_compile_state(compile_state)
  1259. def process_compile_state(self, compile_state: ORMCompileState) -> None:
  1260. """Apply a modification to a given :class:`.CompileState`."""
  1261. # if options to limit the criteria to immediate query only,
  1262. # use compile_state.attributes instead
  1263. self.get_global_criteria(compile_state.global_attributes)
  1264. def get_global_criteria(self, attributes: Dict[Any, Any]) -> None:
  1265. for mp in self._all_mappers():
  1266. load_criteria = attributes.setdefault(
  1267. ("additional_entity_criteria", mp), []
  1268. )
  1269. load_criteria.append(self)
  1270. inspection._inspects(AliasedClass)(lambda target: target._aliased_insp)
  1271. @inspection._inspects(type)
  1272. def _inspect_mc(
  1273. class_: Type[_O],
  1274. ) -> Optional[Mapper[_O]]:
  1275. try:
  1276. class_manager = opt_manager_of_class(class_)
  1277. if class_manager is None or not class_manager.is_mapped:
  1278. return None
  1279. mapper = class_manager.mapper
  1280. except exc.NO_STATE:
  1281. return None
  1282. else:
  1283. return mapper
  1284. GenericAlias = type(List[Any])
  1285. @inspection._inspects(GenericAlias)
  1286. def _inspect_generic_alias(
  1287. class_: Type[_O],
  1288. ) -> Optional[Mapper[_O]]:
  1289. origin = cast("Type[_O]", get_origin(class_))
  1290. return _inspect_mc(origin)
  1291. @inspection._self_inspects
  1292. class Bundle(
  1293. ORMColumnsClauseRole[_T],
  1294. SupportsCloneAnnotations,
  1295. MemoizedHasCacheKey,
  1296. inspection.Inspectable["Bundle[_T]"],
  1297. InspectionAttr,
  1298. ):
  1299. """A grouping of SQL expressions that are returned by a :class:`.Query`
  1300. under one namespace.
  1301. The :class:`.Bundle` essentially allows nesting of the tuple-based
  1302. results returned by a column-oriented :class:`_query.Query` object.
  1303. It also
  1304. is extensible via simple subclassing, where the primary capability
  1305. to override is that of how the set of expressions should be returned,
  1306. allowing post-processing as well as custom return types, without
  1307. involving ORM identity-mapped classes.
  1308. .. seealso::
  1309. :ref:`bundles`
  1310. """
  1311. single_entity = False
  1312. """If True, queries for a single Bundle will be returned as a single
  1313. entity, rather than an element within a keyed tuple."""
  1314. is_clause_element = False
  1315. is_mapper = False
  1316. is_aliased_class = False
  1317. is_bundle = True
  1318. _propagate_attrs: _PropagateAttrsType = util.immutabledict()
  1319. proxy_set = util.EMPTY_SET
  1320. exprs: List[_ColumnsClauseElement]
  1321. def __init__(
  1322. self, name: str, *exprs: _ColumnExpressionArgument[Any], **kw: Any
  1323. ):
  1324. r"""Construct a new :class:`.Bundle`.
  1325. e.g.::
  1326. bn = Bundle("mybundle", MyClass.x, MyClass.y)
  1327. for row in session.query(bn).filter(bn.c.x == 5).filter(bn.c.y == 4):
  1328. print(row.mybundle.x, row.mybundle.y)
  1329. :param name: name of the bundle.
  1330. :param \*exprs: columns or SQL expressions comprising the bundle.
  1331. :param single_entity=False: if True, rows for this :class:`.Bundle`
  1332. can be returned as a "single entity" outside of any enclosing tuple
  1333. in the same manner as a mapped entity.
  1334. """ # noqa: E501
  1335. self.name = self._label = name
  1336. coerced_exprs = [
  1337. coercions.expect(
  1338. roles.ColumnsClauseRole, expr, apply_propagate_attrs=self
  1339. )
  1340. for expr in exprs
  1341. ]
  1342. self.exprs = coerced_exprs
  1343. self.c = self.columns = ColumnCollection(
  1344. (getattr(col, "key", col._label), col)
  1345. for col in [e._annotations.get("bundle", e) for e in coerced_exprs]
  1346. ).as_readonly()
  1347. self.single_entity = kw.pop("single_entity", self.single_entity)
  1348. def _gen_cache_key(
  1349. self, anon_map: anon_map, bindparams: List[BindParameter[Any]]
  1350. ) -> Tuple[Any, ...]:
  1351. return (self.__class__, self.name, self.single_entity) + tuple(
  1352. [expr._gen_cache_key(anon_map, bindparams) for expr in self.exprs]
  1353. )
  1354. @property
  1355. def mapper(self) -> Optional[Mapper[Any]]:
  1356. mp: Optional[Mapper[Any]] = self.exprs[0]._annotations.get(
  1357. "parentmapper", None
  1358. )
  1359. return mp
  1360. @property
  1361. def entity(self) -> Optional[_InternalEntityType[Any]]:
  1362. ie: Optional[_InternalEntityType[Any]] = self.exprs[
  1363. 0
  1364. ]._annotations.get("parententity", None)
  1365. return ie
  1366. @property
  1367. def entity_namespace(
  1368. self,
  1369. ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
  1370. return self.c
  1371. columns: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]
  1372. """A namespace of SQL expressions referred to by this :class:`.Bundle`.
  1373. e.g.::
  1374. bn = Bundle("mybundle", MyClass.x, MyClass.y)
  1375. q = sess.query(bn).filter(bn.c.x == 5)
  1376. Nesting of bundles is also supported::
  1377. b1 = Bundle(
  1378. "b1",
  1379. Bundle("b2", MyClass.a, MyClass.b),
  1380. Bundle("b3", MyClass.x, MyClass.y),
  1381. )
  1382. q = sess.query(b1).filter(b1.c.b2.c.a == 5).filter(b1.c.b3.c.y == 9)
  1383. .. seealso::
  1384. :attr:`.Bundle.c`
  1385. """ # noqa: E501
  1386. c: ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]
  1387. """An alias for :attr:`.Bundle.columns`."""
  1388. def _clone(self, **kw):
  1389. cloned = self.__class__.__new__(self.__class__)
  1390. cloned.__dict__.update(self.__dict__)
  1391. return cloned
  1392. def __clause_element__(self):
  1393. # ensure existing entity_namespace remains
  1394. annotations = {"bundle": self, "entity_namespace": self}
  1395. annotations.update(self._annotations)
  1396. plugin_subject = self.exprs[0]._propagate_attrs.get(
  1397. "plugin_subject", self.entity
  1398. )
  1399. return (
  1400. expression.ClauseList(
  1401. _literal_as_text_role=roles.ColumnsClauseRole,
  1402. group=False,
  1403. *[e._annotations.get("bundle", e) for e in self.exprs],
  1404. )
  1405. ._annotate(annotations)
  1406. ._set_propagate_attrs(
  1407. # the Bundle *must* use the orm plugin no matter what. the
  1408. # subject can be None but it's much better if it's not.
  1409. {
  1410. "compile_state_plugin": "orm",
  1411. "plugin_subject": plugin_subject,
  1412. }
  1413. )
  1414. )
  1415. @property
  1416. def clauses(self):
  1417. return self.__clause_element__().clauses
  1418. def label(self, name):
  1419. """Provide a copy of this :class:`.Bundle` passing a new label."""
  1420. cloned = self._clone()
  1421. cloned.name = name
  1422. return cloned
  1423. def create_row_processor(
  1424. self,
  1425. query: Select[Any],
  1426. procs: Sequence[Callable[[Row[Any]], Any]],
  1427. labels: Sequence[str],
  1428. ) -> Callable[[Row[Any]], Any]:
  1429. """Produce the "row processing" function for this :class:`.Bundle`.
  1430. May be overridden by subclasses to provide custom behaviors when
  1431. results are fetched. The method is passed the statement object and a
  1432. set of "row processor" functions at query execution time; these
  1433. processor functions when given a result row will return the individual
  1434. attribute value, which can then be adapted into any kind of return data
  1435. structure.
  1436. The example below illustrates replacing the usual :class:`.Row`
  1437. return structure with a straight Python dictionary::
  1438. from sqlalchemy.orm import Bundle
  1439. class DictBundle(Bundle):
  1440. def create_row_processor(self, query, procs, labels):
  1441. "Override create_row_processor to return values as dictionaries"
  1442. def proc(row):
  1443. return dict(zip(labels, (proc(row) for proc in procs)))
  1444. return proc
  1445. A result from the above :class:`_orm.Bundle` will return dictionary
  1446. values::
  1447. bn = DictBundle("mybundle", MyClass.data1, MyClass.data2)
  1448. for row in session.execute(select(bn)).where(bn.c.data1 == "d1"):
  1449. print(row.mybundle["data1"], row.mybundle["data2"])
  1450. """ # noqa: E501
  1451. keyed_tuple = result_tuple(labels, [() for l in labels])
  1452. def proc(row: Row[Any]) -> Any:
  1453. return keyed_tuple([proc(row) for proc in procs])
  1454. return proc
  1455. def _orm_annotate(element: _SA, exclude: Optional[Any] = None) -> _SA:
  1456. """Deep copy the given ClauseElement, annotating each element with the
  1457. "_orm_adapt" flag.
  1458. Elements within the exclude collection will be cloned but not annotated.
  1459. """
  1460. return sql_util._deep_annotate(element, {"_orm_adapt": True}, exclude)
  1461. def _orm_deannotate(element: _SA) -> _SA:
  1462. """Remove annotations that link a column to a particular mapping.
  1463. Note this doesn't affect "remote" and "foreign" annotations
  1464. passed by the :func:`_orm.foreign` and :func:`_orm.remote`
  1465. annotators.
  1466. """
  1467. return sql_util._deep_deannotate(
  1468. element, values=("_orm_adapt", "parententity")
  1469. )
  1470. def _orm_full_deannotate(element: _SA) -> _SA:
  1471. return sql_util._deep_deannotate(element)
  1472. class _ORMJoin(expression.Join):
  1473. """Extend Join to support ORM constructs as input."""
  1474. __visit_name__ = expression.Join.__visit_name__
  1475. inherit_cache = True
  1476. def __init__(
  1477. self,
  1478. left: _FromClauseArgument,
  1479. right: _FromClauseArgument,
  1480. onclause: Optional[_OnClauseArgument] = None,
  1481. isouter: bool = False,
  1482. full: bool = False,
  1483. _left_memo: Optional[Any] = None,
  1484. _right_memo: Optional[Any] = None,
  1485. _extra_criteria: Tuple[ColumnElement[bool], ...] = (),
  1486. ):
  1487. left_info = cast(
  1488. "Union[FromClause, _InternalEntityType[Any]]",
  1489. inspection.inspect(left),
  1490. )
  1491. right_info = cast(
  1492. "Union[FromClause, _InternalEntityType[Any]]",
  1493. inspection.inspect(right),
  1494. )
  1495. adapt_to = right_info.selectable
  1496. # used by joined eager loader
  1497. self._left_memo = _left_memo
  1498. self._right_memo = _right_memo
  1499. if isinstance(onclause, attributes.QueryableAttribute):
  1500. if TYPE_CHECKING:
  1501. assert isinstance(
  1502. onclause.comparator, RelationshipProperty.Comparator
  1503. )
  1504. on_selectable = onclause.comparator._source_selectable()
  1505. prop = onclause.property
  1506. _extra_criteria += onclause._extra_criteria
  1507. elif isinstance(onclause, MapperProperty):
  1508. # used internally by joined eager loader...possibly not ideal
  1509. prop = onclause
  1510. on_selectable = prop.parent.selectable
  1511. else:
  1512. prop = None
  1513. on_selectable = None
  1514. left_selectable = left_info.selectable
  1515. if prop:
  1516. adapt_from: Optional[FromClause]
  1517. if sql_util.clause_is_present(on_selectable, left_selectable):
  1518. adapt_from = on_selectable
  1519. else:
  1520. assert isinstance(left_selectable, FromClause)
  1521. adapt_from = left_selectable
  1522. (
  1523. pj,
  1524. sj,
  1525. source,
  1526. dest,
  1527. secondary,
  1528. target_adapter,
  1529. ) = prop._create_joins(
  1530. source_selectable=adapt_from,
  1531. dest_selectable=adapt_to,
  1532. source_polymorphic=True,
  1533. of_type_entity=right_info,
  1534. alias_secondary=True,
  1535. extra_criteria=_extra_criteria,
  1536. )
  1537. if sj is not None:
  1538. if isouter:
  1539. # note this is an inner join from secondary->right
  1540. right = sql.join(secondary, right, sj)
  1541. onclause = pj
  1542. else:
  1543. left = sql.join(left, secondary, pj, isouter)
  1544. onclause = sj
  1545. else:
  1546. onclause = pj
  1547. self._target_adapter = target_adapter
  1548. # we don't use the normal coercions logic for _ORMJoin
  1549. # (probably should), so do some gymnastics to get the entity.
  1550. # logic here is for #8721, which was a major bug in 1.4
  1551. # for almost two years, not reported/fixed until 1.4.43 (!)
  1552. if is_selectable(left_info):
  1553. parententity = left_selectable._annotations.get(
  1554. "parententity", None
  1555. )
  1556. elif insp_is_mapper(left_info) or insp_is_aliased_class(left_info):
  1557. parententity = left_info
  1558. else:
  1559. parententity = None
  1560. if parententity is not None:
  1561. self._annotations = self._annotations.union(
  1562. {"parententity": parententity}
  1563. )
  1564. augment_onclause = bool(_extra_criteria) and not prop
  1565. expression.Join.__init__(self, left, right, onclause, isouter, full)
  1566. assert self.onclause is not None
  1567. if augment_onclause:
  1568. self.onclause &= sql.and_(*_extra_criteria)
  1569. if (
  1570. not prop
  1571. and getattr(right_info, "mapper", None)
  1572. and right_info.mapper.single # type: ignore
  1573. ):
  1574. right_info = cast("_InternalEntityType[Any]", right_info)
  1575. # if single inheritance target and we are using a manual
  1576. # or implicit ON clause, augment it the same way we'd augment the
  1577. # WHERE.
  1578. single_crit = right_info.mapper._single_table_criterion
  1579. if single_crit is not None:
  1580. if insp_is_aliased_class(right_info):
  1581. single_crit = right_info._adapter.traverse(single_crit)
  1582. self.onclause = self.onclause & single_crit
  1583. def _splice_into_center(self, other):
  1584. """Splice a join into the center.
  1585. Given join(a, b) and join(b, c), return join(a, b).join(c)
  1586. """
  1587. leftmost = other
  1588. while isinstance(leftmost, sql.Join):
  1589. leftmost = leftmost.left
  1590. assert self.right is leftmost
  1591. left = _ORMJoin(
  1592. self.left,
  1593. other.left,
  1594. self.onclause,
  1595. isouter=self.isouter,
  1596. _left_memo=self._left_memo,
  1597. _right_memo=other._left_memo._path_registry,
  1598. )
  1599. return _ORMJoin(
  1600. left,
  1601. other.right,
  1602. other.onclause,
  1603. isouter=other.isouter,
  1604. _right_memo=other._right_memo,
  1605. )
  1606. def join(
  1607. self,
  1608. right: _FromClauseArgument,
  1609. onclause: Optional[_OnClauseArgument] = None,
  1610. isouter: bool = False,
  1611. full: bool = False,
  1612. ) -> _ORMJoin:
  1613. return _ORMJoin(self, right, onclause, full=full, isouter=isouter)
  1614. def outerjoin(
  1615. self,
  1616. right: _FromClauseArgument,
  1617. onclause: Optional[_OnClauseArgument] = None,
  1618. full: bool = False,
  1619. ) -> _ORMJoin:
  1620. return _ORMJoin(self, right, onclause, isouter=True, full=full)
  1621. def with_parent(
  1622. instance: object,
  1623. prop: attributes.QueryableAttribute[Any],
  1624. from_entity: Optional[_EntityType[Any]] = None,
  1625. ) -> ColumnElement[bool]:
  1626. """Create filtering criterion that relates this query's primary entity
  1627. to the given related instance, using established
  1628. :func:`_orm.relationship()`
  1629. configuration.
  1630. E.g.::
  1631. stmt = select(Address).where(with_parent(some_user, User.addresses))
  1632. The SQL rendered is the same as that rendered when a lazy loader
  1633. would fire off from the given parent on that attribute, meaning
  1634. that the appropriate state is taken from the parent object in
  1635. Python without the need to render joins to the parent table
  1636. in the rendered statement.
  1637. The given property may also make use of :meth:`_orm.PropComparator.of_type`
  1638. to indicate the left side of the criteria::
  1639. a1 = aliased(Address)
  1640. a2 = aliased(Address)
  1641. stmt = select(a1, a2).where(with_parent(u1, User.addresses.of_type(a2)))
  1642. The above use is equivalent to using the
  1643. :func:`_orm.with_parent.from_entity` argument::
  1644. a1 = aliased(Address)
  1645. a2 = aliased(Address)
  1646. stmt = select(a1, a2).where(
  1647. with_parent(u1, User.addresses, from_entity=a2)
  1648. )
  1649. :param instance:
  1650. An instance which has some :func:`_orm.relationship`.
  1651. :param property:
  1652. Class-bound attribute, which indicates
  1653. what relationship from the instance should be used to reconcile the
  1654. parent/child relationship.
  1655. :param from_entity:
  1656. Entity in which to consider as the left side. This defaults to the
  1657. "zero" entity of the :class:`_query.Query` itself.
  1658. .. versionadded:: 1.2
  1659. """ # noqa: E501
  1660. prop_t: RelationshipProperty[Any]
  1661. if isinstance(prop, str):
  1662. raise sa_exc.ArgumentError(
  1663. "with_parent() accepts class-bound mapped attributes, not strings"
  1664. )
  1665. elif isinstance(prop, attributes.QueryableAttribute):
  1666. if prop._of_type:
  1667. from_entity = prop._of_type
  1668. mapper_property = prop.property
  1669. if mapper_property is None or not prop_is_relationship(
  1670. mapper_property
  1671. ):
  1672. raise sa_exc.ArgumentError(
  1673. f"Expected relationship property for with_parent(), "
  1674. f"got {mapper_property}"
  1675. )
  1676. prop_t = mapper_property
  1677. else:
  1678. prop_t = prop
  1679. return prop_t._with_parent(instance, from_entity=from_entity)
  1680. def has_identity(object_: object) -> bool:
  1681. """Return True if the given object has a database
  1682. identity.
  1683. This typically corresponds to the object being
  1684. in either the persistent or detached state.
  1685. .. seealso::
  1686. :func:`.was_deleted`
  1687. """
  1688. state = attributes.instance_state(object_)
  1689. return state.has_identity
  1690. def was_deleted(object_: object) -> bool:
  1691. """Return True if the given object was deleted
  1692. within a session flush.
  1693. This is regardless of whether or not the object is
  1694. persistent or detached.
  1695. .. seealso::
  1696. :attr:`.InstanceState.was_deleted`
  1697. """
  1698. state = attributes.instance_state(object_)
  1699. return state.was_deleted
  1700. def _entity_corresponds_to(
  1701. given: _InternalEntityType[Any], entity: _InternalEntityType[Any]
  1702. ) -> bool:
  1703. """determine if 'given' corresponds to 'entity', in terms
  1704. of an entity passed to Query that would match the same entity
  1705. being referred to elsewhere in the query.
  1706. """
  1707. if insp_is_aliased_class(entity):
  1708. if insp_is_aliased_class(given):
  1709. if entity._base_alias() is given._base_alias():
  1710. return True
  1711. return False
  1712. elif insp_is_aliased_class(given):
  1713. if given._use_mapper_path:
  1714. return entity in given.with_polymorphic_mappers
  1715. else:
  1716. return entity is given
  1717. assert insp_is_mapper(given)
  1718. return entity.common_parent(given)
  1719. def _entity_corresponds_to_use_path_impl(
  1720. given: _InternalEntityType[Any], entity: _InternalEntityType[Any]
  1721. ) -> bool:
  1722. """determine if 'given' corresponds to 'entity', in terms
  1723. of a path of loader options where a mapped attribute is taken to
  1724. be a member of a parent entity.
  1725. e.g.::
  1726. someoption(A).someoption(A.b) # -> fn(A, A) -> True
  1727. someoption(A).someoption(C.d) # -> fn(A, C) -> False
  1728. a1 = aliased(A)
  1729. someoption(a1).someoption(A.b) # -> fn(a1, A) -> False
  1730. someoption(a1).someoption(a1.b) # -> fn(a1, a1) -> True
  1731. wp = with_polymorphic(A, [A1, A2])
  1732. someoption(wp).someoption(A1.foo) # -> fn(wp, A1) -> False
  1733. someoption(wp).someoption(wp.A1.foo) # -> fn(wp, wp.A1) -> True
  1734. """
  1735. if insp_is_aliased_class(given):
  1736. return (
  1737. insp_is_aliased_class(entity)
  1738. and not entity._use_mapper_path
  1739. and (given is entity or entity in given._with_polymorphic_entities)
  1740. )
  1741. elif not insp_is_aliased_class(entity):
  1742. return given.isa(entity.mapper)
  1743. else:
  1744. return (
  1745. entity._use_mapper_path
  1746. and given in entity.with_polymorphic_mappers
  1747. )
  1748. def _entity_isa(given: _InternalEntityType[Any], mapper: Mapper[Any]) -> bool:
  1749. """determine if 'given' "is a" mapper, in terms of the given
  1750. would load rows of type 'mapper'.
  1751. """
  1752. if given.is_aliased_class:
  1753. return mapper in given.with_polymorphic_mappers or given.mapper.isa(
  1754. mapper
  1755. )
  1756. elif given.with_polymorphic_mappers:
  1757. return mapper in given.with_polymorphic_mappers or given.isa(mapper)
  1758. else:
  1759. return given.isa(mapper)
  1760. def _getitem(iterable_query: Query[Any], item: Any) -> Any:
  1761. """calculate __getitem__ in terms of an iterable query object
  1762. that also has a slice() method.
  1763. """
  1764. def _no_negative_indexes():
  1765. raise IndexError(
  1766. "negative indexes are not accepted by SQL "
  1767. "index / slice operators"
  1768. )
  1769. if isinstance(item, slice):
  1770. start, stop, step = util.decode_slice(item)
  1771. if (
  1772. isinstance(stop, int)
  1773. and isinstance(start, int)
  1774. and stop - start <= 0
  1775. ):
  1776. return []
  1777. elif (isinstance(start, int) and start < 0) or (
  1778. isinstance(stop, int) and stop < 0
  1779. ):
  1780. _no_negative_indexes()
  1781. res = iterable_query.slice(start, stop)
  1782. if step is not None:
  1783. return list(res)[None : None : item.step]
  1784. else:
  1785. return list(res)
  1786. else:
  1787. if item == -1:
  1788. _no_negative_indexes()
  1789. else:
  1790. return list(iterable_query[item : item + 1])[0]
  1791. def _is_mapped_annotation(
  1792. raw_annotation: _AnnotationScanType,
  1793. cls: Type[Any],
  1794. originating_cls: Type[Any],
  1795. ) -> bool:
  1796. try:
  1797. annotated = de_stringify_annotation(
  1798. cls, raw_annotation, originating_cls.__module__
  1799. )
  1800. except NameError:
  1801. # in most cases, at least within our own tests, we can raise
  1802. # here, which is more accurate as it prevents us from returning
  1803. # false negatives. However, in the real world, try to avoid getting
  1804. # involved with end-user annotations that have nothing to do with us.
  1805. # see issue #8888 where we bypass using this function in the case
  1806. # that we want to detect an unresolvable Mapped[] type.
  1807. return False
  1808. else:
  1809. return is_origin_of_cls(annotated, _MappedAnnotationBase)
  1810. class _CleanupError(Exception):
  1811. pass
  1812. def _cleanup_mapped_str_annotation(
  1813. annotation: str, originating_module: str
  1814. ) -> str:
  1815. # fix up an annotation that comes in as the form:
  1816. # 'Mapped[List[Address]]' so that it instead looks like:
  1817. # 'Mapped[List["Address"]]' , which will allow us to get
  1818. # "Address" as a string
  1819. # additionally, resolve symbols for these names since this is where
  1820. # we'd have to do it
  1821. inner: Optional[Match[str]]
  1822. mm = re.match(r"^([^ \|]+?)\[(.+)\]$", annotation)
  1823. if not mm:
  1824. return annotation
  1825. # ticket #8759. Resolve the Mapped name to a real symbol.
  1826. # originally this just checked the name.
  1827. try:
  1828. obj = eval_name_only(mm.group(1), originating_module)
  1829. except NameError as ne:
  1830. raise _CleanupError(
  1831. f'For annotation "{annotation}", could not resolve '
  1832. f'container type "{mm.group(1)}". '
  1833. "Please ensure this type is imported at the module level "
  1834. "outside of TYPE_CHECKING blocks"
  1835. ) from ne
  1836. if obj is typing.ClassVar:
  1837. real_symbol = "ClassVar"
  1838. else:
  1839. try:
  1840. if issubclass(obj, _MappedAnnotationBase):
  1841. real_symbol = obj.__name__
  1842. else:
  1843. return annotation
  1844. except TypeError:
  1845. # avoid isinstance(obj, type) check, just catch TypeError
  1846. return annotation
  1847. # note: if one of the codepaths above didn't define real_symbol and
  1848. # then didn't return, real_symbol raises UnboundLocalError
  1849. # which is actually a NameError, and the calling routines don't
  1850. # notice this since they are catching NameError anyway. Just in case
  1851. # this is being modified in the future, something to be aware of.
  1852. stack = []
  1853. inner = mm
  1854. while True:
  1855. stack.append(real_symbol if mm is inner else inner.group(1))
  1856. g2 = inner.group(2)
  1857. inner = re.match(r"^([^ \|]+?)\[(.+)\]$", g2)
  1858. if inner is None:
  1859. stack.append(g2)
  1860. break
  1861. # stacks we want to rewrite, that is, quote the last entry which
  1862. # we think is a relationship class name:
  1863. #
  1864. # ['Mapped', 'List', 'Address']
  1865. # ['Mapped', 'A']
  1866. #
  1867. # stacks we dont want to rewrite, which are generally MappedColumn
  1868. # use cases:
  1869. #
  1870. # ['Mapped', "'Optional[Dict[str, str]]'"]
  1871. # ['Mapped', 'dict[str, str] | None']
  1872. if (
  1873. # avoid already quoted symbols such as
  1874. # ['Mapped', "'Optional[Dict[str, str]]'"]
  1875. not re.match(r"""^["'].*["']$""", stack[-1])
  1876. # avoid further generics like Dict[] such as
  1877. # ['Mapped', 'dict[str, str] | None'],
  1878. # ['Mapped', 'list[int] | list[str]'],
  1879. # ['Mapped', 'Union[list[int], list[str]]'],
  1880. and not re.search(r"[\[\]]", stack[-1])
  1881. ):
  1882. stripchars = "\"' "
  1883. stack[-1] = ", ".join(
  1884. f'"{elem.strip(stripchars)}"' for elem in stack[-1].split(",")
  1885. )
  1886. annotation = "[".join(stack) + ("]" * (len(stack) - 1))
  1887. return annotation
  1888. def _extract_mapped_subtype(
  1889. raw_annotation: Optional[_AnnotationScanType],
  1890. cls: type,
  1891. originating_module: str,
  1892. key: str,
  1893. attr_cls: Type[Any],
  1894. required: bool,
  1895. is_dataclass_field: bool,
  1896. expect_mapped: bool = True,
  1897. raiseerr: bool = True,
  1898. ) -> Optional[Tuple[Union[_AnnotationScanType, str], Optional[type]]]:
  1899. """given an annotation, figure out if it's ``Mapped[something]`` and if
  1900. so, return the ``something`` part.
  1901. Includes error raise scenarios and other options.
  1902. """
  1903. if raw_annotation is None:
  1904. if required:
  1905. raise orm_exc.MappedAnnotationError(
  1906. f"Python typing annotation is required for attribute "
  1907. f'"{cls.__name__}.{key}" when primary argument(s) for '
  1908. f'"{attr_cls.__name__}" construct are None or not present'
  1909. )
  1910. return None
  1911. try:
  1912. # destringify the "outside" of the annotation. note we are not
  1913. # adding include_generic so it will *not* dig into generic contents,
  1914. # which will remain as ForwardRef or plain str under future annotations
  1915. # mode. The full destringify happens later when mapped_column goes
  1916. # to do a full lookup in the registry type_annotations_map.
  1917. annotated = de_stringify_annotation(
  1918. cls,
  1919. raw_annotation,
  1920. originating_module,
  1921. str_cleanup_fn=_cleanup_mapped_str_annotation,
  1922. )
  1923. except _CleanupError as ce:
  1924. raise orm_exc.MappedAnnotationError(
  1925. f"Could not interpret annotation {raw_annotation}. "
  1926. "Check that it uses names that are correctly imported at the "
  1927. "module level. See chained stack trace for more hints."
  1928. ) from ce
  1929. except NameError as ne:
  1930. if raiseerr and "Mapped[" in raw_annotation: # type: ignore
  1931. raise orm_exc.MappedAnnotationError(
  1932. f"Could not interpret annotation {raw_annotation}. "
  1933. "Check that it uses names that are correctly imported at the "
  1934. "module level. See chained stack trace for more hints."
  1935. ) from ne
  1936. annotated = raw_annotation # type: ignore
  1937. if is_dataclass_field:
  1938. return annotated, None
  1939. else:
  1940. if not hasattr(annotated, "__origin__") or not is_origin_of_cls(
  1941. annotated, _MappedAnnotationBase
  1942. ):
  1943. if expect_mapped:
  1944. if not raiseerr:
  1945. return None
  1946. origin = getattr(annotated, "__origin__", None)
  1947. if origin is typing.ClassVar:
  1948. return None
  1949. # check for other kind of ORM descriptor like AssociationProxy,
  1950. # don't raise for that (issue #9957)
  1951. elif isinstance(origin, type) and issubclass(
  1952. origin, ORMDescriptor
  1953. ):
  1954. return None
  1955. raise orm_exc.MappedAnnotationError(
  1956. f'Type annotation for "{cls.__name__}.{key}" '
  1957. "can't be correctly interpreted for "
  1958. "Annotated Declarative Table form. ORM annotations "
  1959. "should normally make use of the ``Mapped[]`` generic "
  1960. "type, or other ORM-compatible generic type, as a "
  1961. "container for the actual type, which indicates the "
  1962. "intent that the attribute is mapped. "
  1963. "Class variables that are not intended to be mapped "
  1964. "by the ORM should use ClassVar[]. "
  1965. "To allow Annotated Declarative to disregard legacy "
  1966. "annotations which don't use Mapped[] to pass, set "
  1967. '"__allow_unmapped__ = True" on the class or a '
  1968. "superclass this class.",
  1969. code="zlpr",
  1970. )
  1971. else:
  1972. return annotated, None
  1973. if len(annotated.__args__) != 1:
  1974. raise orm_exc.MappedAnnotationError(
  1975. "Expected sub-type for Mapped[] annotation"
  1976. )
  1977. return (
  1978. # fix dict/list/set args to be ForwardRef, see #11814
  1979. fixup_container_fwd_refs(annotated.__args__[0]),
  1980. annotated.__origin__,
  1981. )
  1982. def _mapper_property_as_plain_name(prop: Type[Any]) -> str:
  1983. if hasattr(prop, "_mapper_property_name"):
  1984. name = prop._mapper_property_name()
  1985. else:
  1986. name = None
  1987. return util.clsname_as_plain_name(prop, name)