util.py 78 KB

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