util.py 74 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176
  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. import re
  8. import types
  9. import weakref
  10. from . import attributes # noqa
  11. from .base import _class_to_mapper # noqa
  12. from .base import _never_set # noqa
  13. from .base import _none_set # noqa
  14. from .base import attribute_str # noqa
  15. from .base import class_mapper # noqa
  16. from .base import InspectionAttr # noqa
  17. from .base import instance_str # noqa
  18. from .base import object_mapper # noqa
  19. from .base import object_state # noqa
  20. from .base import state_attribute_str # noqa
  21. from .base import state_class_str # noqa
  22. from .base import state_str # noqa
  23. from .interfaces import CriteriaOption
  24. from .interfaces import MapperProperty # noqa
  25. from .interfaces import ORMColumnsClauseRole
  26. from .interfaces import ORMEntityColumnsClauseRole
  27. from .interfaces import ORMFromClauseRole
  28. from .interfaces import PropComparator # noqa
  29. from .path_registry import PathRegistry # noqa
  30. from .. import event
  31. from .. import exc as sa_exc
  32. from .. import inspection
  33. from .. import sql
  34. from .. import util
  35. from ..engine.result import result_tuple
  36. from ..sql import base as sql_base
  37. from ..sql import coercions
  38. from ..sql import expression
  39. from ..sql import lambdas
  40. from ..sql import roles
  41. from ..sql import util as sql_util
  42. from ..sql import visitors
  43. from ..sql.annotation import SupportsCloneAnnotations
  44. from ..sql.base import ColumnCollection
  45. all_cascades = frozenset(
  46. (
  47. "delete",
  48. "delete-orphan",
  49. "all",
  50. "merge",
  51. "expunge",
  52. "save-update",
  53. "refresh-expire",
  54. "none",
  55. )
  56. )
  57. class CascadeOptions(frozenset):
  58. """Keeps track of the options sent to
  59. :paramref:`.relationship.cascade`"""
  60. _add_w_all_cascades = all_cascades.difference(
  61. ["all", "none", "delete-orphan"]
  62. )
  63. _allowed_cascades = all_cascades
  64. _viewonly_cascades = ["expunge", "all", "none", "refresh-expire", "merge"]
  65. __slots__ = (
  66. "save_update",
  67. "delete",
  68. "refresh_expire",
  69. "merge",
  70. "expunge",
  71. "delete_orphan",
  72. )
  73. def __new__(cls, value_list):
  74. if isinstance(value_list, util.string_types) or value_list is None:
  75. return cls.from_string(value_list)
  76. values = set(value_list)
  77. if values.difference(cls._allowed_cascades):
  78. raise sa_exc.ArgumentError(
  79. "Invalid cascade option(s): %s"
  80. % ", ".join(
  81. [
  82. repr(x)
  83. for x in sorted(
  84. values.difference(cls._allowed_cascades)
  85. )
  86. ]
  87. )
  88. )
  89. if "all" in values:
  90. values.update(cls._add_w_all_cascades)
  91. if "none" in values:
  92. values.clear()
  93. values.discard("all")
  94. self = frozenset.__new__(CascadeOptions, values)
  95. self.save_update = "save-update" in values
  96. self.delete = "delete" in values
  97. self.refresh_expire = "refresh-expire" in values
  98. self.merge = "merge" in values
  99. self.expunge = "expunge" in values
  100. self.delete_orphan = "delete-orphan" in values
  101. if self.delete_orphan and not self.delete:
  102. util.warn(
  103. "The 'delete-orphan' cascade " "option requires 'delete'."
  104. )
  105. return self
  106. def __repr__(self):
  107. return "CascadeOptions(%r)" % (",".join([x for x in sorted(self)]))
  108. @classmethod
  109. def from_string(cls, arg):
  110. values = [c for c in re.split(r"\s*,\s*", arg or "") if c]
  111. return cls(values)
  112. def _validator_events(desc, key, validator, include_removes, include_backrefs):
  113. """Runs a validation method on an attribute value to be set or
  114. appended.
  115. """
  116. if not include_backrefs:
  117. def detect_is_backref(state, initiator):
  118. impl = state.manager[key].impl
  119. return initiator.impl is not impl
  120. if include_removes:
  121. def append(state, value, initiator):
  122. if initiator.op is not attributes.OP_BULK_REPLACE and (
  123. include_backrefs or not detect_is_backref(state, initiator)
  124. ):
  125. return validator(state.obj(), key, value, False)
  126. else:
  127. return value
  128. def bulk_set(state, values, initiator):
  129. if include_backrefs or not detect_is_backref(state, initiator):
  130. obj = state.obj()
  131. values[:] = [
  132. validator(obj, key, value, False) for value in values
  133. ]
  134. def set_(state, value, oldvalue, initiator):
  135. if include_backrefs or not detect_is_backref(state, initiator):
  136. return validator(state.obj(), key, value, False)
  137. else:
  138. return value
  139. def remove(state, value, initiator):
  140. if include_backrefs or not detect_is_backref(state, initiator):
  141. validator(state.obj(), key, value, True)
  142. else:
  143. def append(state, value, initiator):
  144. if initiator.op is not attributes.OP_BULK_REPLACE and (
  145. include_backrefs or not detect_is_backref(state, initiator)
  146. ):
  147. return validator(state.obj(), key, value)
  148. else:
  149. return value
  150. def bulk_set(state, values, initiator):
  151. if include_backrefs or not detect_is_backref(state, initiator):
  152. obj = state.obj()
  153. values[:] = [validator(obj, key, value) for value in values]
  154. def set_(state, value, oldvalue, initiator):
  155. if include_backrefs or not detect_is_backref(state, initiator):
  156. return validator(state.obj(), key, value)
  157. else:
  158. return value
  159. event.listen(desc, "append", append, raw=True, retval=True)
  160. event.listen(desc, "bulk_replace", bulk_set, raw=True)
  161. event.listen(desc, "set", set_, raw=True, retval=True)
  162. if include_removes:
  163. event.listen(desc, "remove", remove, raw=True, retval=True)
  164. def polymorphic_union(
  165. table_map, typecolname, aliasname="p_union", cast_nulls=True
  166. ):
  167. """Create a ``UNION`` statement used by a polymorphic mapper.
  168. See :ref:`concrete_inheritance` for an example of how
  169. this is used.
  170. :param table_map: mapping of polymorphic identities to
  171. :class:`_schema.Table` objects.
  172. :param typecolname: string name of a "discriminator" column, which will be
  173. derived from the query, producing the polymorphic identity for
  174. each row. If ``None``, no polymorphic discriminator is generated.
  175. :param aliasname: name of the :func:`~sqlalchemy.sql.expression.alias()`
  176. construct generated.
  177. :param cast_nulls: if True, non-existent columns, which are represented
  178. as labeled NULLs, will be passed into CAST. This is a legacy behavior
  179. that is problematic on some backends such as Oracle - in which case it
  180. can be set to False.
  181. """
  182. colnames = util.OrderedSet()
  183. colnamemaps = {}
  184. types = {}
  185. for key in table_map:
  186. table = table_map[key]
  187. table = coercions.expect(
  188. roles.StrictFromClauseRole, table, allow_select=True
  189. )
  190. table_map[key] = table
  191. m = {}
  192. for c in table.c:
  193. if c.key == typecolname:
  194. raise sa_exc.InvalidRequestError(
  195. "Polymorphic union can't use '%s' as the discriminator "
  196. "column due to mapped column %r; please apply the "
  197. "'typecolname' "
  198. "argument; this is available on "
  199. "ConcreteBase as '_concrete_discriminator_name'"
  200. % (typecolname, c)
  201. )
  202. colnames.add(c.key)
  203. m[c.key] = c
  204. types[c.key] = c.type
  205. colnamemaps[table] = m
  206. def col(name, table):
  207. try:
  208. return colnamemaps[table][name]
  209. except KeyError:
  210. if cast_nulls:
  211. return sql.cast(sql.null(), types[name]).label(name)
  212. else:
  213. return sql.type_coerce(sql.null(), types[name]).label(name)
  214. result = []
  215. for type_, table in table_map.items():
  216. if typecolname is not None:
  217. result.append(
  218. sql.select(
  219. *(
  220. [col(name, table) for name in colnames]
  221. + [
  222. sql.literal_column(
  223. sql_util._quote_ddl_expr(type_)
  224. ).label(typecolname)
  225. ]
  226. )
  227. ).select_from(table)
  228. )
  229. else:
  230. result.append(
  231. sql.select(
  232. *[col(name, table) for name in colnames]
  233. ).select_from(table)
  234. )
  235. return sql.union_all(*result).alias(aliasname)
  236. def identity_key(*args, **kwargs):
  237. r"""Generate "identity key" tuples, as are used as keys in the
  238. :attr:`.Session.identity_map` dictionary.
  239. This function has several call styles:
  240. * ``identity_key(class, ident, identity_token=token)``
  241. This form receives a mapped class and a primary key scalar or
  242. tuple as an argument.
  243. E.g.::
  244. >>> identity_key(MyClass, (1, 2))
  245. (<class '__main__.MyClass'>, (1, 2), None)
  246. :param class: mapped class (must be a positional argument)
  247. :param ident: primary key, may be a scalar or tuple argument.
  248. :param identity_token: optional identity token
  249. .. versionadded:: 1.2 added identity_token
  250. * ``identity_key(instance=instance)``
  251. This form will produce the identity key for a given instance. The
  252. instance need not be persistent, only that its primary key attributes
  253. are populated (else the key will contain ``None`` for those missing
  254. values).
  255. E.g.::
  256. >>> instance = MyClass(1, 2)
  257. >>> identity_key(instance=instance)
  258. (<class '__main__.MyClass'>, (1, 2), None)
  259. In this form, the given instance is ultimately run though
  260. :meth:`_orm.Mapper.identity_key_from_instance`, which will have the
  261. effect of performing a database check for the corresponding row
  262. if the object is expired.
  263. :param instance: object instance (must be given as a keyword arg)
  264. * ``identity_key(class, row=row, identity_token=token)``
  265. This form is similar to the class/tuple form, except is passed a
  266. database result row as a :class:`.Row` object.
  267. E.g.::
  268. >>> row = engine.execute(\
  269. text("select * from table where a=1 and b=2")\
  270. ).first()
  271. >>> identity_key(MyClass, row=row)
  272. (<class '__main__.MyClass'>, (1, 2), None)
  273. :param class: mapped class (must be a positional argument)
  274. :param row: :class:`.Row` row returned by a :class:`_engine.CursorResult`
  275. (must be given as a keyword arg)
  276. :param identity_token: optional identity token
  277. .. versionadded:: 1.2 added identity_token
  278. """
  279. if args:
  280. row = None
  281. largs = len(args)
  282. if largs == 1:
  283. class_ = args[0]
  284. try:
  285. row = kwargs.pop("row")
  286. except KeyError:
  287. ident = kwargs.pop("ident")
  288. elif largs in (2, 3):
  289. class_, ident = args
  290. else:
  291. raise sa_exc.ArgumentError(
  292. "expected up to three positional arguments, " "got %s" % largs
  293. )
  294. identity_token = kwargs.pop("identity_token", None)
  295. if kwargs:
  296. raise sa_exc.ArgumentError(
  297. "unknown keyword arguments: %s" % ", ".join(kwargs)
  298. )
  299. mapper = class_mapper(class_)
  300. if row is None:
  301. return mapper.identity_key_from_primary_key(
  302. util.to_list(ident), identity_token=identity_token
  303. )
  304. else:
  305. return mapper.identity_key_from_row(
  306. row, identity_token=identity_token
  307. )
  308. else:
  309. instance = kwargs.pop("instance")
  310. if kwargs:
  311. raise sa_exc.ArgumentError(
  312. "unknown keyword arguments: %s" % ", ".join(kwargs.keys)
  313. )
  314. mapper = object_mapper(instance)
  315. return mapper.identity_key_from_instance(instance)
  316. class ORMAdapter(sql_util.ColumnAdapter):
  317. """ColumnAdapter subclass which excludes adaptation of entities from
  318. non-matching mappers.
  319. """
  320. def __init__(
  321. self,
  322. entity,
  323. equivalents=None,
  324. adapt_required=False,
  325. allow_label_resolve=True,
  326. anonymize_labels=False,
  327. ):
  328. info = inspection.inspect(entity)
  329. self.mapper = info.mapper
  330. selectable = info.selectable
  331. is_aliased_class = info.is_aliased_class
  332. if is_aliased_class:
  333. self.aliased_class = entity
  334. else:
  335. self.aliased_class = None
  336. sql_util.ColumnAdapter.__init__(
  337. self,
  338. selectable,
  339. equivalents,
  340. adapt_required=adapt_required,
  341. allow_label_resolve=allow_label_resolve,
  342. anonymize_labels=anonymize_labels,
  343. include_fn=self._include_fn,
  344. )
  345. def _include_fn(self, elem):
  346. entity = elem._annotations.get("parentmapper", None)
  347. return not entity or entity.isa(self.mapper) or self.mapper.isa(entity)
  348. class AliasedClass(object):
  349. r"""Represents an "aliased" form of a mapped class for usage with Query.
  350. The ORM equivalent of a :func:`~sqlalchemy.sql.expression.alias`
  351. construct, this object mimics the mapped class using a
  352. ``__getattr__`` scheme and maintains a reference to a
  353. real :class:`~sqlalchemy.sql.expression.Alias` object.
  354. A primary purpose of :class:`.AliasedClass` is to serve as an alternate
  355. within a SQL statement generated by the ORM, such that an existing
  356. mapped entity can be used in multiple contexts. A simple example::
  357. # find all pairs of users with the same name
  358. user_alias = aliased(User)
  359. session.query(User, user_alias).\
  360. join((user_alias, User.id > user_alias.id)).\
  361. filter(User.name == user_alias.name)
  362. :class:`.AliasedClass` is also capable of mapping an existing mapped
  363. class to an entirely new selectable, provided this selectable is column-
  364. compatible with the existing mapped selectable, and it can also be
  365. configured in a mapping as the target of a :func:`_orm.relationship`.
  366. See the links below for examples.
  367. The :class:`.AliasedClass` object is constructed typically using the
  368. :func:`_orm.aliased` function. It also is produced with additional
  369. configuration when using the :func:`_orm.with_polymorphic` function.
  370. The resulting object is an instance of :class:`.AliasedClass`.
  371. This object implements an attribute scheme which produces the
  372. same attribute and method interface as the original mapped
  373. class, allowing :class:`.AliasedClass` to be compatible
  374. with any attribute technique which works on the original class,
  375. including hybrid attributes (see :ref:`hybrids_toplevel`).
  376. The :class:`.AliasedClass` can be inspected for its underlying
  377. :class:`_orm.Mapper`, aliased selectable, and other information
  378. using :func:`_sa.inspect`::
  379. from sqlalchemy import inspect
  380. my_alias = aliased(MyClass)
  381. insp = inspect(my_alias)
  382. The resulting inspection object is an instance of :class:`.AliasedInsp`.
  383. .. seealso::
  384. :func:`.aliased`
  385. :func:`.with_polymorphic`
  386. :ref:`relationship_aliased_class`
  387. :ref:`relationship_to_window_function`
  388. """
  389. def __init__(
  390. self,
  391. mapped_class_or_ac,
  392. alias=None,
  393. name=None,
  394. flat=False,
  395. adapt_on_names=False,
  396. # TODO: None for default here?
  397. with_polymorphic_mappers=(),
  398. with_polymorphic_discriminator=None,
  399. base_alias=None,
  400. use_mapper_path=False,
  401. represents_outer_join=False,
  402. ):
  403. insp = inspection.inspect(mapped_class_or_ac)
  404. mapper = insp.mapper
  405. nest_adapters = False
  406. if alias is None:
  407. if insp.is_aliased_class and insp.selectable._is_subquery:
  408. alias = insp.selectable.alias()
  409. else:
  410. alias = (
  411. mapper._with_polymorphic_selectable._anonymous_fromclause(
  412. name=name,
  413. flat=flat,
  414. )
  415. )
  416. elif insp.is_aliased_class:
  417. nest_adapters = True
  418. self._aliased_insp = AliasedInsp(
  419. self,
  420. insp,
  421. alias,
  422. name,
  423. with_polymorphic_mappers
  424. if with_polymorphic_mappers
  425. else mapper.with_polymorphic_mappers,
  426. with_polymorphic_discriminator
  427. if with_polymorphic_discriminator is not None
  428. else mapper.polymorphic_on,
  429. base_alias,
  430. use_mapper_path,
  431. adapt_on_names,
  432. represents_outer_join,
  433. nest_adapters,
  434. )
  435. self.__name__ = "AliasedClass_%s" % mapper.class_.__name__
  436. @classmethod
  437. def _reconstitute_from_aliased_insp(cls, aliased_insp):
  438. obj = cls.__new__(cls)
  439. obj.__name__ = "AliasedClass_%s" % aliased_insp.mapper.class_.__name__
  440. obj._aliased_insp = aliased_insp
  441. if aliased_insp._is_with_polymorphic:
  442. for sub_aliased_insp in aliased_insp._with_polymorphic_entities:
  443. if sub_aliased_insp is not aliased_insp:
  444. ent = AliasedClass._reconstitute_from_aliased_insp(
  445. sub_aliased_insp
  446. )
  447. setattr(obj, sub_aliased_insp.class_.__name__, ent)
  448. return obj
  449. def __getattr__(self, key):
  450. try:
  451. _aliased_insp = self.__dict__["_aliased_insp"]
  452. except KeyError:
  453. raise AttributeError()
  454. else:
  455. target = _aliased_insp._target
  456. # maintain all getattr mechanics
  457. attr = getattr(target, key)
  458. # attribute is a method, that will be invoked against a
  459. # "self"; so just return a new method with the same function and
  460. # new self
  461. if hasattr(attr, "__call__") and hasattr(attr, "__self__"):
  462. return types.MethodType(attr.__func__, self)
  463. # attribute is a descriptor, that will be invoked against a
  464. # "self"; so invoke the descriptor against this self
  465. if hasattr(attr, "__get__"):
  466. attr = attr.__get__(None, self)
  467. # attributes within the QueryableAttribute system will want this
  468. # to be invoked so the object can be adapted
  469. if hasattr(attr, "adapt_to_entity"):
  470. attr = attr.adapt_to_entity(_aliased_insp)
  471. setattr(self, key, attr)
  472. return attr
  473. def _get_from_serialized(self, key, mapped_class, aliased_insp):
  474. # this method is only used in terms of the
  475. # sqlalchemy.ext.serializer extension
  476. attr = getattr(mapped_class, key)
  477. if hasattr(attr, "__call__") and hasattr(attr, "__self__"):
  478. return types.MethodType(attr.__func__, self)
  479. # attribute is a descriptor, that will be invoked against a
  480. # "self"; so invoke the descriptor against this self
  481. if hasattr(attr, "__get__"):
  482. attr = attr.__get__(None, self)
  483. # attributes within the QueryableAttribute system will want this
  484. # to be invoked so the object can be adapted
  485. if hasattr(attr, "adapt_to_entity"):
  486. aliased_insp._weak_entity = weakref.ref(self)
  487. attr = attr.adapt_to_entity(aliased_insp)
  488. setattr(self, key, attr)
  489. return attr
  490. def __repr__(self):
  491. return "<AliasedClass at 0x%x; %s>" % (
  492. id(self),
  493. self._aliased_insp._target.__name__,
  494. )
  495. def __str__(self):
  496. return str(self._aliased_insp)
  497. class AliasedInsp(
  498. ORMEntityColumnsClauseRole,
  499. ORMFromClauseRole,
  500. sql_base.MemoizedHasCacheKey,
  501. InspectionAttr,
  502. ):
  503. """Provide an inspection interface for an
  504. :class:`.AliasedClass` object.
  505. The :class:`.AliasedInsp` object is returned
  506. given an :class:`.AliasedClass` using the
  507. :func:`_sa.inspect` function::
  508. from sqlalchemy import inspect
  509. from sqlalchemy.orm import aliased
  510. my_alias = aliased(MyMappedClass)
  511. insp = inspect(my_alias)
  512. Attributes on :class:`.AliasedInsp`
  513. include:
  514. * ``entity`` - the :class:`.AliasedClass` represented.
  515. * ``mapper`` - the :class:`_orm.Mapper` mapping the underlying class.
  516. * ``selectable`` - the :class:`_expression.Alias`
  517. construct which ultimately
  518. represents an aliased :class:`_schema.Table` or
  519. :class:`_expression.Select`
  520. construct.
  521. * ``name`` - the name of the alias. Also is used as the attribute
  522. name when returned in a result tuple from :class:`_query.Query`.
  523. * ``with_polymorphic_mappers`` - collection of :class:`_orm.Mapper`
  524. objects
  525. indicating all those mappers expressed in the select construct
  526. for the :class:`.AliasedClass`.
  527. * ``polymorphic_on`` - an alternate column or SQL expression which
  528. will be used as the "discriminator" for a polymorphic load.
  529. .. seealso::
  530. :ref:`inspection_toplevel`
  531. """
  532. _cache_key_traversal = [
  533. ("name", visitors.ExtendedInternalTraversal.dp_string),
  534. ("_adapt_on_names", visitors.ExtendedInternalTraversal.dp_boolean),
  535. ("_use_mapper_path", visitors.ExtendedInternalTraversal.dp_boolean),
  536. ("_target", visitors.ExtendedInternalTraversal.dp_inspectable),
  537. ("selectable", visitors.ExtendedInternalTraversal.dp_clauseelement),
  538. (
  539. "with_polymorphic_mappers",
  540. visitors.InternalTraversal.dp_has_cache_key_list,
  541. ),
  542. ("polymorphic_on", visitors.InternalTraversal.dp_clauseelement),
  543. ]
  544. def __init__(
  545. self,
  546. entity,
  547. inspected,
  548. selectable,
  549. name,
  550. with_polymorphic_mappers,
  551. polymorphic_on,
  552. _base_alias,
  553. _use_mapper_path,
  554. adapt_on_names,
  555. represents_outer_join,
  556. nest_adapters,
  557. ):
  558. mapped_class_or_ac = inspected.entity
  559. mapper = inspected.mapper
  560. self._weak_entity = weakref.ref(entity)
  561. self.mapper = mapper
  562. self.selectable = (
  563. self.persist_selectable
  564. ) = self.local_table = selectable
  565. self.name = name
  566. self.polymorphic_on = polymorphic_on
  567. self._base_alias = weakref.ref(_base_alias or self)
  568. self._use_mapper_path = _use_mapper_path
  569. self.represents_outer_join = represents_outer_join
  570. self._nest_adapters = nest_adapters
  571. if with_polymorphic_mappers:
  572. self._is_with_polymorphic = True
  573. self.with_polymorphic_mappers = with_polymorphic_mappers
  574. self._with_polymorphic_entities = []
  575. for poly in self.with_polymorphic_mappers:
  576. if poly is not mapper:
  577. ent = AliasedClass(
  578. poly.class_,
  579. selectable,
  580. base_alias=self,
  581. adapt_on_names=adapt_on_names,
  582. use_mapper_path=_use_mapper_path,
  583. )
  584. setattr(self.entity, poly.class_.__name__, ent)
  585. self._with_polymorphic_entities.append(ent._aliased_insp)
  586. else:
  587. self._is_with_polymorphic = False
  588. self.with_polymorphic_mappers = [mapper]
  589. self._adapter = sql_util.ColumnAdapter(
  590. selectable,
  591. equivalents=mapper._equivalent_columns,
  592. adapt_on_names=adapt_on_names,
  593. anonymize_labels=True,
  594. # make sure the adapter doesn't try to grab other tables that
  595. # are not even the thing we are mapping, such as embedded
  596. # selectables in subqueries or CTEs. See issue #6060
  597. adapt_from_selectables={
  598. m.selectable
  599. for m in self.with_polymorphic_mappers
  600. if not adapt_on_names
  601. },
  602. )
  603. if nest_adapters:
  604. self._adapter = inspected._adapter.wrap(self._adapter)
  605. self._adapt_on_names = adapt_on_names
  606. self._target = mapped_class_or_ac
  607. # self._target = mapper.class_ # mapped_class_or_ac
  608. @property
  609. def entity(self):
  610. # to eliminate reference cycles, the AliasedClass is held weakly.
  611. # this produces some situations where the AliasedClass gets lost,
  612. # particularly when one is created internally and only the AliasedInsp
  613. # is passed around.
  614. # to work around this case, we just generate a new one when we need
  615. # it, as it is a simple class with very little initial state on it.
  616. ent = self._weak_entity()
  617. if ent is None:
  618. ent = AliasedClass._reconstitute_from_aliased_insp(self)
  619. self._weak_entity = weakref.ref(ent)
  620. return ent
  621. is_aliased_class = True
  622. "always returns True"
  623. @util.memoized_instancemethod
  624. def __clause_element__(self):
  625. return self.selectable._annotate(
  626. {
  627. "parentmapper": self.mapper,
  628. "parententity": self,
  629. "entity_namespace": self,
  630. }
  631. )._set_propagate_attrs(
  632. {"compile_state_plugin": "orm", "plugin_subject": self}
  633. )
  634. @property
  635. def entity_namespace(self):
  636. return self.entity
  637. @property
  638. def class_(self):
  639. """Return the mapped class ultimately represented by this
  640. :class:`.AliasedInsp`."""
  641. return self.mapper.class_
  642. @property
  643. def _path_registry(self):
  644. if self._use_mapper_path:
  645. return self.mapper._path_registry
  646. else:
  647. return PathRegistry.per_mapper(self)
  648. def __getstate__(self):
  649. return {
  650. "entity": self.entity,
  651. "mapper": self.mapper,
  652. "alias": self.selectable,
  653. "name": self.name,
  654. "adapt_on_names": self._adapt_on_names,
  655. "with_polymorphic_mappers": self.with_polymorphic_mappers,
  656. "with_polymorphic_discriminator": self.polymorphic_on,
  657. "base_alias": self._base_alias(),
  658. "use_mapper_path": self._use_mapper_path,
  659. "represents_outer_join": self.represents_outer_join,
  660. "nest_adapters": self._nest_adapters,
  661. }
  662. def __setstate__(self, state):
  663. self.__init__(
  664. state["entity"],
  665. state["mapper"],
  666. state["alias"],
  667. state["name"],
  668. state["with_polymorphic_mappers"],
  669. state["with_polymorphic_discriminator"],
  670. state["base_alias"],
  671. state["use_mapper_path"],
  672. state["adapt_on_names"],
  673. state["represents_outer_join"],
  674. state["nest_adapters"],
  675. )
  676. def _adapt_element(self, elem, key=None):
  677. d = {
  678. "parententity": self,
  679. "parentmapper": self.mapper,
  680. }
  681. if key:
  682. d["proxy_key"] = key
  683. return (
  684. self._adapter.traverse(elem)
  685. ._annotate(d)
  686. ._set_propagate_attrs(
  687. {"compile_state_plugin": "orm", "plugin_subject": self}
  688. )
  689. )
  690. def _entity_for_mapper(self, mapper):
  691. self_poly = self.with_polymorphic_mappers
  692. if mapper in self_poly:
  693. if mapper is self.mapper:
  694. return self
  695. else:
  696. return getattr(
  697. self.entity, mapper.class_.__name__
  698. )._aliased_insp
  699. elif mapper.isa(self.mapper):
  700. return self
  701. else:
  702. assert False, "mapper %s doesn't correspond to %s" % (mapper, self)
  703. @util.memoized_property
  704. def _get_clause(self):
  705. onclause, replacemap = self.mapper._get_clause
  706. return (
  707. self._adapter.traverse(onclause),
  708. {
  709. self._adapter.traverse(col): param
  710. for col, param in replacemap.items()
  711. },
  712. )
  713. @util.memoized_property
  714. def _memoized_values(self):
  715. return {}
  716. @util.memoized_property
  717. def _all_column_expressions(self):
  718. if self._is_with_polymorphic:
  719. cols_plus_keys = self.mapper._columns_plus_keys(
  720. [ent.mapper for ent in self._with_polymorphic_entities]
  721. )
  722. else:
  723. cols_plus_keys = self.mapper._columns_plus_keys()
  724. cols_plus_keys = [
  725. (key, self._adapt_element(col)) for key, col in cols_plus_keys
  726. ]
  727. return ColumnCollection(cols_plus_keys)
  728. def _memo(self, key, callable_, *args, **kw):
  729. if key in self._memoized_values:
  730. return self._memoized_values[key]
  731. else:
  732. self._memoized_values[key] = value = callable_(*args, **kw)
  733. return value
  734. def __repr__(self):
  735. if self.with_polymorphic_mappers:
  736. with_poly = "(%s)" % ", ".join(
  737. mp.class_.__name__ for mp in self.with_polymorphic_mappers
  738. )
  739. else:
  740. with_poly = ""
  741. return "<AliasedInsp at 0x%x; %s%s>" % (
  742. id(self),
  743. self.class_.__name__,
  744. with_poly,
  745. )
  746. def __str__(self):
  747. if self._is_with_polymorphic:
  748. return "with_polymorphic(%s, [%s])" % (
  749. self._target.__name__,
  750. ", ".join(
  751. mp.class_.__name__
  752. for mp in self.with_polymorphic_mappers
  753. if mp is not self.mapper
  754. ),
  755. )
  756. else:
  757. return "aliased(%s)" % (self._target.__name__,)
  758. class _WrapUserEntity(object):
  759. """A wrapper used within the loader_criteria lambda caller so that
  760. we can bypass declared_attr descriptors on unmapped mixins, which
  761. normally emit a warning for such use.
  762. might also be useful for other per-lambda instrumentations should
  763. the need arise.
  764. """
  765. __slots__ = ("subject",)
  766. def __init__(self, subject):
  767. self.subject = subject
  768. @util.preload_module("sqlalchemy.orm.decl_api")
  769. def __getattribute__(self, name):
  770. decl_api = util.preloaded.orm.decl_api
  771. subject = object.__getattribute__(self, "subject")
  772. if name in subject.__dict__ and isinstance(
  773. subject.__dict__[name], decl_api.declared_attr
  774. ):
  775. return subject.__dict__[name].fget(subject)
  776. else:
  777. return getattr(subject, name)
  778. class LoaderCriteriaOption(CriteriaOption):
  779. """Add additional WHERE criteria to the load for all occurrences of
  780. a particular entity.
  781. :class:`_orm.LoaderCriteriaOption` is invoked using the
  782. :func:`_orm.with_loader_criteria` function; see that function for
  783. details.
  784. .. versionadded:: 1.4
  785. """
  786. _traverse_internals = [
  787. ("root_entity", visitors.ExtendedInternalTraversal.dp_plain_obj),
  788. ("entity", visitors.ExtendedInternalTraversal.dp_has_cache_key),
  789. ("where_criteria", visitors.InternalTraversal.dp_clauseelement),
  790. ("include_aliases", visitors.InternalTraversal.dp_boolean),
  791. ("propagate_to_loaders", visitors.InternalTraversal.dp_boolean),
  792. ]
  793. def __init__(
  794. self,
  795. entity_or_base,
  796. where_criteria,
  797. loader_only=False,
  798. include_aliases=False,
  799. propagate_to_loaders=True,
  800. track_closure_variables=True,
  801. ):
  802. """Add additional WHERE criteria to the load for all occurrences of
  803. a particular entity.
  804. .. versionadded:: 1.4
  805. The :func:`_orm.with_loader_criteria` option is intended to add
  806. limiting criteria to a particular kind of entity in a query,
  807. **globally**, meaning it will apply to the entity as it appears
  808. in the SELECT query as well as within any subqueries, join
  809. conditions, and relationship loads, including both eager and lazy
  810. loaders, without the need for it to be specified in any particular
  811. part of the query. The rendering logic uses the same system used by
  812. single table inheritance to ensure a certain discriminator is applied
  813. to a table.
  814. E.g., using :term:`2.0-style` queries, we can limit the way the
  815. ``User.addresses`` collection is loaded, regardless of the kind
  816. of loading used::
  817. from sqlalchemy.orm import with_loader_criteria
  818. stmt = select(User).options(
  819. selectinload(User.addresses),
  820. with_loader_criteria(Address, Address.email_address != 'foo'))
  821. )
  822. Above, the "selectinload" for ``User.addresses`` will apply the
  823. given filtering criteria to the WHERE clause.
  824. Another example, where the filtering will be applied to the
  825. ON clause of the join, in this example using :term:`1.x style`
  826. queries::
  827. q = session.query(User).outerjoin(User.addresses).options(
  828. with_loader_criteria(Address, Address.email_address != 'foo'))
  829. )
  830. The primary purpose of :func:`_orm.with_loader_criteria` is to use
  831. it in the :meth:`_orm.SessionEvents.do_orm_execute` event handler
  832. to ensure that all occurrences of a particular entity are filtered
  833. in a certain way, such as filtering for access control roles. It
  834. also can be used to apply criteria to relationship loads. In the
  835. example below, we can apply a certain set of rules to all queries
  836. emitted by a particular :class:`_orm.Session`::
  837. session = Session(bind=engine)
  838. @event.listens_for("do_orm_execute", session)
  839. def _add_filtering_criteria(execute_state):
  840. if (
  841. execute_state.is_select
  842. and not execute_state.is_column_load
  843. and not execute_state.is_relationship_load
  844. ):
  845. execute_state.statement = execute_state.statement.options(
  846. with_loader_criteria(
  847. SecurityRole,
  848. lambda cls: cls.role.in_(['some_role']),
  849. include_aliases=True
  850. )
  851. )
  852. In the above example, the :meth:`_orm.SessionEvents.do_orm_execute`
  853. event will intercept all queries emitted using the
  854. :class:`_orm.Session`. For those queries which are SELECT statements
  855. and are not attribute or relationship loads a custom
  856. :func:`_orm.with_loader_criteria` option is added to the query. The
  857. :func:`_orm.with_loader_criteria` option will be used in the given
  858. statement and will also be automatically propagated to all relationship
  859. loads that descend from this query.
  860. The criteria argument given is a ``lambda`` that accepts a ``cls``
  861. argument. The given class will expand to include all mapped subclass
  862. and need not itself be a mapped class.
  863. .. tip::
  864. When using :func:`_orm.with_loader_criteria` option in
  865. conjunction with the :func:`_orm.contains_eager` loader option,
  866. it's important to note that :func:`_orm.with_loader_criteria` only
  867. affects the part of the query that determines what SQL is rendered
  868. in terms of the WHERE and FROM clauses. The
  869. :func:`_orm.contains_eager` option does not affect the rendering of
  870. the SELECT statement outside of the columns clause, so does not have
  871. any interaction with the :func:`_orm.with_loader_criteria` option.
  872. However, the way things "work" is that :func:`_orm.contains_eager`
  873. is meant to be used with a query that is already selecting from the
  874. additional entities in some way, where
  875. :func:`_orm.with_loader_criteria` can apply it's additional
  876. criteria.
  877. In the example below, assuming a mapping relationship as
  878. ``A -> A.bs -> B``, the given :func:`_orm.with_loader_criteria`
  879. option will affect the way in which the JOIN is rendered::
  880. stmt = select(A).join(A.bs).options(
  881. contains_eager(A.bs),
  882. with_loader_criteria(B, B.flag == 1)
  883. )
  884. Above, the given :func:`_orm.with_loader_criteria` option will
  885. affect the ON clause of the JOIN that is specified by
  886. ``.join(A.bs)``, so is applied as expected. The
  887. :func:`_orm.contains_eager` option has the effect that columns from
  888. ``B`` are added to the columns clause::
  889. SELECT
  890. b.id, b.a_id, b.data, b.flag,
  891. a.id AS id_1,
  892. a.data AS data_1
  893. FROM a JOIN b ON a.id = b.a_id AND b.flag = :flag_1
  894. The use of the :func:`_orm.contains_eager` option within the above
  895. statement has no effect on the behavior of the
  896. :func:`_orm.with_loader_criteria` option. If the
  897. :func:`_orm.contains_eager` option were omitted, the SQL would be
  898. the same as regards the FROM and WHERE clauses, where
  899. :func:`_orm.with_loader_criteria` continues to add its criteria to
  900. the ON clause of the JOIN. The addition of
  901. :func:`_orm.contains_eager` only affects the columns clause, in that
  902. additional columns against ``b`` are added which are then consumed
  903. by the ORM to produce ``B`` instances.
  904. .. warning:: The use of a lambda inside of the call to
  905. :func:`_orm.with_loader_criteria` is only invoked **once per unique
  906. class**. Custom functions should not be invoked within this lambda.
  907. See :ref:`engine_lambda_caching` for an overview of the "lambda SQL"
  908. feature, which is for advanced use only.
  909. :param entity_or_base: a mapped class, or a class that is a super
  910. class of a particular set of mapped classes, to which the rule
  911. will apply.
  912. :param where_criteria: a Core SQL expression that applies limiting
  913. criteria. This may also be a "lambda:" or Python function that
  914. accepts a target class as an argument, when the given class is
  915. a base with many different mapped subclasses.
  916. .. note:: To support pickling, use a module-level Python function to
  917. produce the SQL expression instead of a lambda or a fixed SQL
  918. expression, which tend to not be picklable.
  919. :param include_aliases: if True, apply the rule to :func:`_orm.aliased`
  920. constructs as well.
  921. :param propagate_to_loaders: defaults to True, apply to relationship
  922. loaders such as lazy loaders. This indicates that the
  923. option object itself including SQL expression is carried along with
  924. each loaded instance. Set to ``False`` to prevent the object from
  925. being assigned to individual instances.
  926. .. seealso::
  927. :ref:`examples_session_orm_events` - includes examples of using
  928. :func:`_orm.with_loader_criteria`.
  929. :ref:`do_orm_execute_global_criteria` - basic example on how to
  930. combine :func:`_orm.with_loader_criteria` with the
  931. :meth:`_orm.SessionEvents.do_orm_execute` event.
  932. :param track_closure_variables: when False, closure variables inside
  933. of a lambda expression will not be used as part of
  934. any cache key. This allows more complex expressions to be used
  935. inside of a lambda expression but requires that the lambda ensures
  936. it returns the identical SQL every time given a particular class.
  937. .. versionadded:: 1.4.0b2
  938. """
  939. entity = inspection.inspect(entity_or_base, False)
  940. if entity is None:
  941. self.root_entity = entity_or_base
  942. self.entity = None
  943. else:
  944. self.root_entity = None
  945. self.entity = entity
  946. self._where_crit_orig = where_criteria
  947. if callable(where_criteria):
  948. self.deferred_where_criteria = True
  949. self.where_criteria = lambdas.DeferredLambdaElement(
  950. where_criteria,
  951. roles.WhereHavingRole,
  952. lambda_args=(
  953. _WrapUserEntity(
  954. self.root_entity
  955. if self.root_entity is not None
  956. else self.entity.entity,
  957. ),
  958. ),
  959. opts=lambdas.LambdaOptions(
  960. track_closure_variables=track_closure_variables
  961. ),
  962. )
  963. else:
  964. self.deferred_where_criteria = False
  965. self.where_criteria = coercions.expect(
  966. roles.WhereHavingRole, where_criteria
  967. )
  968. self.include_aliases = include_aliases
  969. self.propagate_to_loaders = propagate_to_loaders
  970. @classmethod
  971. def _unreduce(
  972. cls, entity, where_criteria, include_aliases, propagate_to_loaders
  973. ):
  974. return LoaderCriteriaOption(
  975. entity,
  976. where_criteria,
  977. include_aliases=include_aliases,
  978. propagate_to_loaders=propagate_to_loaders,
  979. )
  980. def __reduce__(self):
  981. return (
  982. LoaderCriteriaOption._unreduce,
  983. (
  984. self.entity.class_ if self.entity else self.root_entity,
  985. self._where_crit_orig,
  986. self.include_aliases,
  987. self.propagate_to_loaders,
  988. ),
  989. )
  990. def _all_mappers(self):
  991. if self.entity:
  992. for ent in self.entity.mapper.self_and_descendants:
  993. yield ent
  994. else:
  995. stack = list(self.root_entity.__subclasses__())
  996. while stack:
  997. subclass = stack.pop(0)
  998. ent = inspection.inspect(subclass, raiseerr=False)
  999. if ent:
  1000. for mp in ent.mapper.self_and_descendants:
  1001. yield mp
  1002. else:
  1003. stack.extend(subclass.__subclasses__())
  1004. def _should_include(self, compile_state):
  1005. if (
  1006. compile_state.select_statement._annotations.get(
  1007. "for_loader_criteria", None
  1008. )
  1009. is self
  1010. ):
  1011. return False
  1012. return True
  1013. def _resolve_where_criteria(self, ext_info):
  1014. if self.deferred_where_criteria:
  1015. crit = self.where_criteria._resolve_with_args(ext_info.entity)
  1016. else:
  1017. crit = self.where_criteria
  1018. return sql_util._deep_annotate(
  1019. crit, {"for_loader_criteria": self}, detect_subquery_cols=True
  1020. )
  1021. def process_compile_state_replaced_entities(
  1022. self, compile_state, mapper_entities
  1023. ):
  1024. return self.process_compile_state(compile_state)
  1025. def process_compile_state(self, compile_state):
  1026. """Apply a modification to a given :class:`.CompileState`."""
  1027. # if options to limit the criteria to immediate query only,
  1028. # use compile_state.attributes instead
  1029. if compile_state.compile_options._with_polymorphic_adapt_map:
  1030. util.warn(
  1031. "The with_loader_criteria() function may not work "
  1032. "correctly with the legacy Query.with_polymorphic() feature. "
  1033. "Please migrate code to use the with_polymorphic() standalone "
  1034. "function before using with_loader_criteria()."
  1035. )
  1036. self.get_global_criteria(compile_state.global_attributes)
  1037. def get_global_criteria(self, attributes):
  1038. for mp in self._all_mappers():
  1039. load_criteria = attributes.setdefault(
  1040. ("additional_entity_criteria", mp), []
  1041. )
  1042. load_criteria.append(self)
  1043. inspection._inspects(AliasedClass)(lambda target: target._aliased_insp)
  1044. inspection._inspects(AliasedInsp)(lambda target: target)
  1045. def aliased(element, alias=None, name=None, flat=False, adapt_on_names=False):
  1046. """Produce an alias of the given element, usually an :class:`.AliasedClass`
  1047. instance.
  1048. E.g.::
  1049. my_alias = aliased(MyClass)
  1050. session.query(MyClass, my_alias).filter(MyClass.id > my_alias.id)
  1051. The :func:`.aliased` function is used to create an ad-hoc mapping of a
  1052. mapped class to a new selectable. By default, a selectable is generated
  1053. from the normally mapped selectable (typically a :class:`_schema.Table`
  1054. ) using the
  1055. :meth:`_expression.FromClause.alias` method. However, :func:`.aliased`
  1056. can also be
  1057. used to link the class to a new :func:`_expression.select` statement.
  1058. Also, the :func:`.with_polymorphic` function is a variant of
  1059. :func:`.aliased` that is intended to specify a so-called "polymorphic
  1060. selectable", that corresponds to the union of several joined-inheritance
  1061. subclasses at once.
  1062. For convenience, the :func:`.aliased` function also accepts plain
  1063. :class:`_expression.FromClause` constructs, such as a
  1064. :class:`_schema.Table` or
  1065. :func:`_expression.select` construct. In those cases, the
  1066. :meth:`_expression.FromClause.alias`
  1067. method is called on the object and the new
  1068. :class:`_expression.Alias` object returned. The returned
  1069. :class:`_expression.Alias` is not
  1070. ORM-mapped in this case.
  1071. .. seealso::
  1072. :ref:`tutorial_orm_entity_aliases` - in the :ref:`unified_tutorial`
  1073. :ref:`orm_queryguide_orm_aliases` - in the :ref:`queryguide_toplevel`
  1074. :param element: element to be aliased. Is normally a mapped class,
  1075. but for convenience can also be a :class:`_expression.FromClause`
  1076. element.
  1077. :param alias: Optional selectable unit to map the element to. This is
  1078. usually used to link the object to a subquery, and should be an aliased
  1079. select construct as one would produce from the
  1080. :meth:`_query.Query.subquery` method or
  1081. the :meth:`_expression.Select.subquery` or
  1082. :meth:`_expression.Select.alias` methods of the :func:`_expression.select`
  1083. construct.
  1084. :param name: optional string name to use for the alias, if not specified
  1085. by the ``alias`` parameter. The name, among other things, forms the
  1086. attribute name that will be accessible via tuples returned by a
  1087. :class:`_query.Query` object. Not supported when creating aliases
  1088. of :class:`_sql.Join` objects.
  1089. :param flat: Boolean, will be passed through to the
  1090. :meth:`_expression.FromClause.alias` call so that aliases of
  1091. :class:`_expression.Join` objects will alias the individual tables
  1092. inside the join, rather than creating a subquery. This is generally
  1093. supported by all modern databases with regards to right-nested joins
  1094. and generally produces more efficient queries.
  1095. :param adapt_on_names: if True, more liberal "matching" will be used when
  1096. mapping the mapped columns of the ORM entity to those of the
  1097. given selectable - a name-based match will be performed if the
  1098. given selectable doesn't otherwise have a column that corresponds
  1099. to one on the entity. The use case for this is when associating
  1100. an entity with some derived selectable such as one that uses
  1101. aggregate functions::
  1102. class UnitPrice(Base):
  1103. __tablename__ = 'unit_price'
  1104. ...
  1105. unit_id = Column(Integer)
  1106. price = Column(Numeric)
  1107. aggregated_unit_price = Session.query(
  1108. func.sum(UnitPrice.price).label('price')
  1109. ).group_by(UnitPrice.unit_id).subquery()
  1110. aggregated_unit_price = aliased(UnitPrice,
  1111. alias=aggregated_unit_price, adapt_on_names=True)
  1112. Above, functions on ``aggregated_unit_price`` which refer to
  1113. ``.price`` will return the
  1114. ``func.sum(UnitPrice.price).label('price')`` column, as it is
  1115. matched on the name "price". Ordinarily, the "price" function
  1116. wouldn't have any "column correspondence" to the actual
  1117. ``UnitPrice.price`` column as it is not a proxy of the original.
  1118. """
  1119. if isinstance(element, expression.FromClause):
  1120. if adapt_on_names:
  1121. raise sa_exc.ArgumentError(
  1122. "adapt_on_names only applies to ORM elements"
  1123. )
  1124. if name:
  1125. return element.alias(name=name, flat=flat)
  1126. else:
  1127. return coercions.expect(
  1128. roles.AnonymizedFromClauseRole, element, flat=flat
  1129. )
  1130. else:
  1131. return AliasedClass(
  1132. element,
  1133. alias=alias,
  1134. flat=flat,
  1135. name=name,
  1136. adapt_on_names=adapt_on_names,
  1137. )
  1138. def with_polymorphic(
  1139. base,
  1140. classes,
  1141. selectable=False,
  1142. flat=False,
  1143. polymorphic_on=None,
  1144. aliased=False,
  1145. adapt_on_names=False,
  1146. innerjoin=False,
  1147. _use_mapper_path=False,
  1148. _existing_alias=None,
  1149. ):
  1150. """Produce an :class:`.AliasedClass` construct which specifies
  1151. columns for descendant mappers of the given base.
  1152. Using this method will ensure that each descendant mapper's
  1153. tables are included in the FROM clause, and will allow filter()
  1154. criterion to be used against those tables. The resulting
  1155. instances will also have those columns already loaded so that
  1156. no "post fetch" of those columns will be required.
  1157. .. seealso::
  1158. :ref:`with_polymorphic` - full discussion of
  1159. :func:`_orm.with_polymorphic`.
  1160. :param base: Base class to be aliased.
  1161. :param classes: a single class or mapper, or list of
  1162. class/mappers, which inherit from the base class.
  1163. Alternatively, it may also be the string ``'*'``, in which case
  1164. all descending mapped classes will be added to the FROM clause.
  1165. :param aliased: when True, the selectable will be aliased. For a
  1166. JOIN, this means the JOIN will be SELECTed from inside of a subquery
  1167. unless the :paramref:`_orm.with_polymorphic.flat` flag is set to
  1168. True, which is recommended for simpler use cases.
  1169. :param flat: Boolean, will be passed through to the
  1170. :meth:`_expression.FromClause.alias` call so that aliases of
  1171. :class:`_expression.Join` objects will alias the individual tables
  1172. inside the join, rather than creating a subquery. This is generally
  1173. supported by all modern databases with regards to right-nested joins
  1174. and generally produces more efficient queries. Setting this flag is
  1175. recommended as long as the resulting SQL is functional.
  1176. :param selectable: a table or subquery that will
  1177. be used in place of the generated FROM clause. This argument is
  1178. required if any of the desired classes use concrete table
  1179. inheritance, since SQLAlchemy currently cannot generate UNIONs
  1180. among tables automatically. If used, the ``selectable`` argument
  1181. must represent the full set of tables and columns mapped by every
  1182. mapped class. Otherwise, the unaccounted mapped columns will
  1183. result in their table being appended directly to the FROM clause
  1184. which will usually lead to incorrect results.
  1185. When left at its default value of ``False``, the polymorphic
  1186. selectable assigned to the base mapper is used for selecting rows.
  1187. However, it may also be passed as ``None``, which will bypass the
  1188. configured polymorphic selectable and instead construct an ad-hoc
  1189. selectable for the target classes given; for joined table inheritance
  1190. this will be a join that includes all target mappers and their
  1191. subclasses.
  1192. :param polymorphic_on: a column to be used as the "discriminator"
  1193. column for the given selectable. If not given, the polymorphic_on
  1194. attribute of the base classes' mapper will be used, if any. This
  1195. is useful for mappings that don't have polymorphic loading
  1196. behavior by default.
  1197. :param innerjoin: if True, an INNER JOIN will be used. This should
  1198. only be specified if querying for one specific subtype only
  1199. :param adapt_on_names: Passes through the
  1200. :paramref:`_orm.aliased.adapt_on_names`
  1201. parameter to the aliased object. This may be useful in situations where
  1202. the given selectable is not directly related to the existing mapped
  1203. selectable.
  1204. .. versionadded:: 1.4.33
  1205. """
  1206. primary_mapper = _class_to_mapper(base)
  1207. if selectable not in (None, False) and flat:
  1208. raise sa_exc.ArgumentError(
  1209. "the 'flat' and 'selectable' arguments cannot be passed "
  1210. "simultaneously to with_polymorphic()"
  1211. )
  1212. if _existing_alias:
  1213. assert _existing_alias.mapper is primary_mapper
  1214. classes = util.to_set(classes)
  1215. new_classes = set(
  1216. [mp.class_ for mp in _existing_alias.with_polymorphic_mappers]
  1217. )
  1218. if classes == new_classes:
  1219. return _existing_alias
  1220. else:
  1221. classes = classes.union(new_classes)
  1222. mappers, selectable = primary_mapper._with_polymorphic_args(
  1223. classes, selectable, innerjoin=innerjoin
  1224. )
  1225. if aliased or flat:
  1226. selectable = selectable._anonymous_fromclause(flat=flat)
  1227. return AliasedClass(
  1228. base,
  1229. selectable,
  1230. adapt_on_names=adapt_on_names,
  1231. with_polymorphic_mappers=mappers,
  1232. with_polymorphic_discriminator=polymorphic_on,
  1233. use_mapper_path=_use_mapper_path,
  1234. represents_outer_join=not innerjoin,
  1235. )
  1236. @inspection._self_inspects
  1237. class Bundle(
  1238. ORMColumnsClauseRole,
  1239. SupportsCloneAnnotations,
  1240. sql_base.MemoizedHasCacheKey,
  1241. InspectionAttr,
  1242. ):
  1243. """A grouping of SQL expressions that are returned by a :class:`.Query`
  1244. under one namespace.
  1245. The :class:`.Bundle` essentially allows nesting of the tuple-based
  1246. results returned by a column-oriented :class:`_query.Query` object.
  1247. It also
  1248. is extensible via simple subclassing, where the primary capability
  1249. to override is that of how the set of expressions should be returned,
  1250. allowing post-processing as well as custom return types, without
  1251. involving ORM identity-mapped classes.
  1252. .. versionadded:: 0.9.0
  1253. .. seealso::
  1254. :ref:`bundles`
  1255. """
  1256. single_entity = False
  1257. """If True, queries for a single Bundle will be returned as a single
  1258. entity, rather than an element within a keyed tuple."""
  1259. is_clause_element = False
  1260. is_mapper = False
  1261. is_aliased_class = False
  1262. is_bundle = True
  1263. _propagate_attrs = util.immutabledict()
  1264. def __init__(self, name, *exprs, **kw):
  1265. r"""Construct a new :class:`.Bundle`.
  1266. e.g.::
  1267. bn = Bundle("mybundle", MyClass.x, MyClass.y)
  1268. for row in session.query(bn).filter(
  1269. bn.c.x == 5).filter(bn.c.y == 4):
  1270. print(row.mybundle.x, row.mybundle.y)
  1271. :param name: name of the bundle.
  1272. :param \*exprs: columns or SQL expressions comprising the bundle.
  1273. :param single_entity=False: if True, rows for this :class:`.Bundle`
  1274. can be returned as a "single entity" outside of any enclosing tuple
  1275. in the same manner as a mapped entity.
  1276. """
  1277. self.name = self._label = name
  1278. self.exprs = exprs = [
  1279. coercions.expect(
  1280. roles.ColumnsClauseRole, expr, apply_propagate_attrs=self
  1281. )
  1282. for expr in exprs
  1283. ]
  1284. self.c = self.columns = ColumnCollection(
  1285. (getattr(col, "key", col._label), col)
  1286. for col in [e._annotations.get("bundle", e) for e in exprs]
  1287. )
  1288. self.single_entity = kw.pop("single_entity", self.single_entity)
  1289. def _gen_cache_key(self, anon_map, bindparams):
  1290. return (self.__class__, self.name, self.single_entity) + tuple(
  1291. [expr._gen_cache_key(anon_map, bindparams) for expr in self.exprs]
  1292. )
  1293. @property
  1294. def mapper(self):
  1295. return self.exprs[0]._annotations.get("parentmapper", None)
  1296. @property
  1297. def entity(self):
  1298. return self.exprs[0]._annotations.get("parententity", None)
  1299. @property
  1300. def entity_namespace(self):
  1301. return self.c
  1302. columns = None
  1303. """A namespace of SQL expressions referred to by this :class:`.Bundle`.
  1304. e.g.::
  1305. bn = Bundle("mybundle", MyClass.x, MyClass.y)
  1306. q = sess.query(bn).filter(bn.c.x == 5)
  1307. Nesting of bundles is also supported::
  1308. b1 = Bundle("b1",
  1309. Bundle('b2', MyClass.a, MyClass.b),
  1310. Bundle('b3', MyClass.x, MyClass.y)
  1311. )
  1312. q = sess.query(b1).filter(
  1313. b1.c.b2.c.a == 5).filter(b1.c.b3.c.y == 9)
  1314. .. seealso::
  1315. :attr:`.Bundle.c`
  1316. """
  1317. c = None
  1318. """An alias for :attr:`.Bundle.columns`."""
  1319. def _clone(self):
  1320. cloned = self.__class__.__new__(self.__class__)
  1321. cloned.__dict__.update(self.__dict__)
  1322. return cloned
  1323. def __clause_element__(self):
  1324. # ensure existing entity_namespace remains
  1325. annotations = {"bundle": self, "entity_namespace": self}
  1326. annotations.update(self._annotations)
  1327. plugin_subject = self.exprs[0]._propagate_attrs.get(
  1328. "plugin_subject", self.entity
  1329. )
  1330. return (
  1331. expression.ClauseList(
  1332. _literal_as_text_role=roles.ColumnsClauseRole,
  1333. group=False,
  1334. *[e._annotations.get("bundle", e) for e in self.exprs]
  1335. )
  1336. ._annotate(annotations)
  1337. ._set_propagate_attrs(
  1338. # the Bundle *must* use the orm plugin no matter what. the
  1339. # subject can be None but it's much better if it's not.
  1340. {
  1341. "compile_state_plugin": "orm",
  1342. "plugin_subject": plugin_subject,
  1343. }
  1344. )
  1345. )
  1346. @property
  1347. def clauses(self):
  1348. return self.__clause_element__().clauses
  1349. def label(self, name):
  1350. """Provide a copy of this :class:`.Bundle` passing a new label."""
  1351. cloned = self._clone()
  1352. cloned.name = name
  1353. return cloned
  1354. def create_row_processor(self, query, procs, labels):
  1355. """Produce the "row processing" function for this :class:`.Bundle`.
  1356. May be overridden by subclasses.
  1357. .. seealso::
  1358. :ref:`bundles` - includes an example of subclassing.
  1359. """
  1360. keyed_tuple = result_tuple(labels, [() for l in labels])
  1361. def proc(row):
  1362. return keyed_tuple([proc(row) for proc in procs])
  1363. return proc
  1364. def _orm_annotate(element, exclude=None):
  1365. """Deep copy the given ClauseElement, annotating each element with the
  1366. "_orm_adapt" flag.
  1367. Elements within the exclude collection will be cloned but not annotated.
  1368. """
  1369. return sql_util._deep_annotate(element, {"_orm_adapt": True}, exclude)
  1370. def _orm_deannotate(element):
  1371. """Remove annotations that link a column to a particular mapping.
  1372. Note this doesn't affect "remote" and "foreign" annotations
  1373. passed by the :func:`_orm.foreign` and :func:`_orm.remote`
  1374. annotators.
  1375. """
  1376. return sql_util._deep_deannotate(
  1377. element, values=("_orm_adapt", "parententity")
  1378. )
  1379. def _orm_full_deannotate(element):
  1380. return sql_util._deep_deannotate(element)
  1381. class _ORMJoin(expression.Join):
  1382. """Extend Join to support ORM constructs as input."""
  1383. __visit_name__ = expression.Join.__visit_name__
  1384. inherit_cache = True
  1385. def __init__(
  1386. self,
  1387. left,
  1388. right,
  1389. onclause=None,
  1390. isouter=False,
  1391. full=False,
  1392. _left_memo=None,
  1393. _right_memo=None,
  1394. _extra_criteria=(),
  1395. ):
  1396. left_info = inspection.inspect(left)
  1397. right_info = inspection.inspect(right)
  1398. adapt_to = right_info.selectable
  1399. # used by joined eager loader
  1400. self._left_memo = _left_memo
  1401. self._right_memo = _right_memo
  1402. # legacy, for string attr name ON clause. if that's removed
  1403. # then the "_joined_from_info" concept can go
  1404. left_orm_info = getattr(left, "_joined_from_info", left_info)
  1405. self._joined_from_info = right_info
  1406. if isinstance(onclause, util.string_types):
  1407. onclause = getattr(left_orm_info.entity, onclause)
  1408. # ####
  1409. if isinstance(onclause, attributes.QueryableAttribute):
  1410. on_selectable = onclause.comparator._source_selectable()
  1411. prop = onclause.property
  1412. _extra_criteria += onclause._extra_criteria
  1413. elif isinstance(onclause, MapperProperty):
  1414. # used internally by joined eager loader...possibly not ideal
  1415. prop = onclause
  1416. on_selectable = prop.parent.selectable
  1417. else:
  1418. prop = None
  1419. left_selectable = left_info.selectable
  1420. if prop:
  1421. if sql_util.clause_is_present(on_selectable, left_selectable):
  1422. adapt_from = on_selectable
  1423. else:
  1424. adapt_from = left_selectable
  1425. (
  1426. pj,
  1427. sj,
  1428. source,
  1429. dest,
  1430. secondary,
  1431. target_adapter,
  1432. ) = prop._create_joins(
  1433. source_selectable=adapt_from,
  1434. dest_selectable=adapt_to,
  1435. source_polymorphic=True,
  1436. of_type_entity=right_info,
  1437. alias_secondary=True,
  1438. extra_criteria=_extra_criteria,
  1439. )
  1440. if sj is not None:
  1441. if isouter:
  1442. # note this is an inner join from secondary->right
  1443. right = sql.join(secondary, right, sj)
  1444. onclause = pj
  1445. else:
  1446. left = sql.join(left, secondary, pj, isouter)
  1447. onclause = sj
  1448. else:
  1449. onclause = pj
  1450. self._target_adapter = target_adapter
  1451. # we don't use the normal coercions logic for _ORMJoin
  1452. # (probably should), so do some gymnastics to get the entity.
  1453. # logic here is for #8721, which was a major bug in 1.4
  1454. # for almost two years, not reported/fixed until 1.4.43 (!)
  1455. if left_info.is_selectable:
  1456. parententity = left_selectable._annotations.get(
  1457. "parententity", None
  1458. )
  1459. elif left_info.is_mapper or left_info.is_aliased_class:
  1460. parententity = left_info
  1461. else:
  1462. parententity = None
  1463. if parententity is not None:
  1464. self._annotations = self._annotations.union(
  1465. {"parententity": parententity}
  1466. )
  1467. augment_onclause = bool(_extra_criteria) and not prop
  1468. expression.Join.__init__(self, left, right, onclause, isouter, full)
  1469. if augment_onclause:
  1470. self.onclause &= sql.and_(*_extra_criteria)
  1471. if (
  1472. not prop
  1473. and getattr(right_info, "mapper", None)
  1474. and right_info.mapper.single
  1475. ):
  1476. # if single inheritance target and we are using a manual
  1477. # or implicit ON clause, augment it the same way we'd augment the
  1478. # WHERE.
  1479. single_crit = right_info.mapper._single_table_criterion
  1480. if single_crit is not None:
  1481. if right_info.is_aliased_class:
  1482. single_crit = right_info._adapter.traverse(single_crit)
  1483. self.onclause = self.onclause & single_crit
  1484. def _splice_into_center(self, other):
  1485. """Splice a join into the center.
  1486. Given join(a, b) and join(b, c), return join(a, b).join(c)
  1487. """
  1488. leftmost = other
  1489. while isinstance(leftmost, sql.Join):
  1490. leftmost = leftmost.left
  1491. assert self.right is leftmost
  1492. left = _ORMJoin(
  1493. self.left,
  1494. other.left,
  1495. self.onclause,
  1496. isouter=self.isouter,
  1497. _left_memo=self._left_memo,
  1498. _right_memo=other._left_memo,
  1499. )
  1500. return _ORMJoin(
  1501. left,
  1502. other.right,
  1503. other.onclause,
  1504. isouter=other.isouter,
  1505. _right_memo=other._right_memo,
  1506. )
  1507. def join(
  1508. self,
  1509. right,
  1510. onclause=None,
  1511. isouter=False,
  1512. full=False,
  1513. join_to_left=None,
  1514. ):
  1515. return _ORMJoin(self, right, onclause, full=full, isouter=isouter)
  1516. def outerjoin(self, right, onclause=None, full=False, join_to_left=None):
  1517. return _ORMJoin(self, right, onclause, isouter=True, full=full)
  1518. def join(
  1519. left, right, onclause=None, isouter=False, full=False, join_to_left=None
  1520. ):
  1521. r"""Produce an inner join between left and right clauses.
  1522. :func:`_orm.join` is an extension to the core join interface
  1523. provided by :func:`_expression.join()`, where the
  1524. left and right selectables may be not only core selectable
  1525. objects such as :class:`_schema.Table`, but also mapped classes or
  1526. :class:`.AliasedClass` instances. The "on" clause can
  1527. be a SQL expression or an ORM mapped attribute
  1528. referencing a configured :func:`_orm.relationship`.
  1529. .. deprecated:: 1.4 using a string relationship name for the "onclause"
  1530. is deprecated and will be removed in 2.0; the onclause may be only
  1531. an ORM-mapped relationship attribute or a SQL expression construct.
  1532. :func:`_orm.join` is not commonly needed in modern usage,
  1533. as its functionality is encapsulated within that of the
  1534. :meth:`_sql.Select.join` and :meth:`_query.Query.join`
  1535. methods. which feature a
  1536. significant amount of automation beyond :func:`_orm.join`
  1537. by itself. Explicit use of :func:`_orm.join`
  1538. with ORM-enabled SELECT statements involves use of the
  1539. :meth:`_sql.Select.select_from` method, as in::
  1540. from sqlalchemy.orm import join
  1541. stmt = select(User).\
  1542. select_from(join(User, Address, User.addresses)).\
  1543. filter(Address.email_address=='foo@bar.com')
  1544. In modern SQLAlchemy the above join can be written more
  1545. succinctly as::
  1546. stmt = select(User).\
  1547. join(User.addresses).\
  1548. filter(Address.email_address=='foo@bar.com')
  1549. .. warning:: using :func:`_orm.join` directly may not work properly
  1550. with modern ORM options such as :func:`_orm.with_loader_criteria`.
  1551. It is strongly recommended to use the idiomatic join patterns
  1552. provided by methods such as :meth:`.Select.join` and
  1553. :meth:`.Select.join_from` when creating ORM joins.
  1554. .. seealso::
  1555. :ref:`orm_queryguide_joins` - in the :ref:`queryguide_toplevel` for
  1556. background on idiomatic ORM join patterns
  1557. """
  1558. return _ORMJoin(left, right, onclause, isouter, full)
  1559. def outerjoin(left, right, onclause=None, full=False, join_to_left=None):
  1560. """Produce a left outer join between left and right clauses.
  1561. This is the "outer join" version of the :func:`_orm.join` function,
  1562. featuring the same behavior except that an OUTER JOIN is generated.
  1563. See that function's documentation for other usage details.
  1564. """
  1565. return _ORMJoin(left, right, onclause, True, full)
  1566. def with_parent(instance, prop, from_entity=None):
  1567. """Create filtering criterion that relates this query's primary entity
  1568. to the given related instance, using established
  1569. :func:`_orm.relationship()`
  1570. configuration.
  1571. E.g.::
  1572. stmt = select(Address).where(with_parent(some_user, User.addresses))
  1573. The SQL rendered is the same as that rendered when a lazy loader
  1574. would fire off from the given parent on that attribute, meaning
  1575. that the appropriate state is taken from the parent object in
  1576. Python without the need to render joins to the parent table
  1577. in the rendered statement.
  1578. The given property may also make use of :meth:`_orm.PropComparator.of_type`
  1579. to indicate the left side of the criteria::
  1580. a1 = aliased(Address)
  1581. a2 = aliased(Address)
  1582. stmt = select(a1, a2).where(
  1583. with_parent(u1, User.addresses.of_type(a2))
  1584. )
  1585. The above use is equivalent to using the
  1586. :func:`_orm.with_parent.from_entity` argument::
  1587. a1 = aliased(Address)
  1588. a2 = aliased(Address)
  1589. stmt = select(a1, a2).where(
  1590. with_parent(u1, User.addresses, from_entity=a2)
  1591. )
  1592. :param instance:
  1593. An instance which has some :func:`_orm.relationship`.
  1594. :param property:
  1595. String property name, or class-bound attribute, which indicates
  1596. what relationship from the instance should be used to reconcile the
  1597. parent/child relationship.
  1598. .. deprecated:: 1.4 Using strings is deprecated and will be removed
  1599. in SQLAlchemy 2.0. Please use the class-bound attribute directly.
  1600. :param from_entity:
  1601. Entity in which to consider as the left side. This defaults to the
  1602. "zero" entity of the :class:`_query.Query` itself.
  1603. .. versionadded:: 1.2
  1604. """
  1605. if isinstance(prop, util.string_types):
  1606. util.warn_deprecated_20(
  1607. "Using strings to indicate relationship names in the ORM "
  1608. "with_parent() function is deprecated and will be removed "
  1609. "SQLAlchemy 2.0. Please use the class-bound attribute directly."
  1610. )
  1611. mapper = object_mapper(instance)
  1612. prop = getattr(mapper.class_, prop).property
  1613. elif isinstance(prop, attributes.QueryableAttribute):
  1614. if prop._of_type:
  1615. from_entity = prop._of_type
  1616. prop = prop.property
  1617. return prop._with_parent(instance, from_entity=from_entity)
  1618. def has_identity(object_):
  1619. """Return True if the given object has a database
  1620. identity.
  1621. This typically corresponds to the object being
  1622. in either the persistent or detached state.
  1623. .. seealso::
  1624. :func:`.was_deleted`
  1625. """
  1626. state = attributes.instance_state(object_)
  1627. return state.has_identity
  1628. def was_deleted(object_):
  1629. """Return True if the given object was deleted
  1630. within a session flush.
  1631. This is regardless of whether or not the object is
  1632. persistent or detached.
  1633. .. seealso::
  1634. :attr:`.InstanceState.was_deleted`
  1635. """
  1636. state = attributes.instance_state(object_)
  1637. return state.was_deleted
  1638. def _entity_corresponds_to(given, entity):
  1639. """determine if 'given' corresponds to 'entity', in terms
  1640. of an entity passed to Query that would match the same entity
  1641. being referred to elsewhere in the query.
  1642. """
  1643. if entity.is_aliased_class:
  1644. if given.is_aliased_class:
  1645. if entity._base_alias() is given._base_alias():
  1646. return True
  1647. return False
  1648. elif given.is_aliased_class:
  1649. if given._use_mapper_path:
  1650. return entity in given.with_polymorphic_mappers
  1651. else:
  1652. return entity is given
  1653. return entity.common_parent(given)
  1654. def _entity_corresponds_to_use_path_impl(given, entity):
  1655. """determine if 'given' corresponds to 'entity', in terms
  1656. of a path of loader options where a mapped attribute is taken to
  1657. be a member of a parent entity.
  1658. e.g.::
  1659. someoption(A).someoption(A.b) # -> fn(A, A) -> True
  1660. someoption(A).someoption(C.d) # -> fn(A, C) -> False
  1661. a1 = aliased(A)
  1662. someoption(a1).someoption(A.b) # -> fn(a1, A) -> False
  1663. someoption(a1).someoption(a1.b) # -> fn(a1, a1) -> True
  1664. wp = with_polymorphic(A, [A1, A2])
  1665. someoption(wp).someoption(A1.foo) # -> fn(wp, A1) -> False
  1666. someoption(wp).someoption(wp.A1.foo) # -> fn(wp, wp.A1) -> True
  1667. """
  1668. if given.is_aliased_class:
  1669. return (
  1670. entity.is_aliased_class
  1671. and not entity._use_mapper_path
  1672. and (given is entity or given in entity._with_polymorphic_entities)
  1673. )
  1674. elif not entity.is_aliased_class:
  1675. return given.common_parent(entity.mapper)
  1676. else:
  1677. return (
  1678. entity._use_mapper_path
  1679. and given in entity.with_polymorphic_mappers
  1680. )
  1681. def _entity_isa(given, mapper):
  1682. """determine if 'given' "is a" mapper, in terms of the given
  1683. would load rows of type 'mapper'.
  1684. """
  1685. if given.is_aliased_class:
  1686. return mapper in given.with_polymorphic_mappers or given.mapper.isa(
  1687. mapper
  1688. )
  1689. elif given.with_polymorphic_mappers:
  1690. return mapper in given.with_polymorphic_mappers
  1691. else:
  1692. return given.isa(mapper)
  1693. def randomize_unitofwork():
  1694. """Use random-ordering sets within the unit of work in order
  1695. to detect unit of work sorting issues.
  1696. This is a utility function that can be used to help reproduce
  1697. inconsistent unit of work sorting issues. For example,
  1698. if two kinds of objects A and B are being inserted, and
  1699. B has a foreign key reference to A - the A must be inserted first.
  1700. However, if there is no relationship between A and B, the unit of work
  1701. won't know to perform this sorting, and an operation may or may not
  1702. fail, depending on how the ordering works out. Since Python sets
  1703. and dictionaries have non-deterministic ordering, such an issue may
  1704. occur on some runs and not on others, and in practice it tends to
  1705. have a great dependence on the state of the interpreter. This leads
  1706. to so-called "heisenbugs" where changing entirely irrelevant aspects
  1707. of the test program still cause the failure behavior to change.
  1708. By calling ``randomize_unitofwork()`` when a script first runs, the
  1709. ordering of a key series of sets within the unit of work implementation
  1710. are randomized, so that the script can be minimized down to the
  1711. fundamental mapping and operation that's failing, while still reproducing
  1712. the issue on at least some runs.
  1713. This utility is also available when running the test suite via the
  1714. ``--reversetop`` flag.
  1715. """
  1716. from sqlalchemy.orm import unitofwork, session, mapper, dependency
  1717. from sqlalchemy.util import topological
  1718. from sqlalchemy.testing.util import RandomSet
  1719. topological.set = (
  1720. unitofwork.set
  1721. ) = session.set = mapper.set = dependency.set = RandomSet
  1722. def _getitem(iterable_query, item, allow_negative):
  1723. """calculate __getitem__ in terms of an iterable query object
  1724. that also has a slice() method.
  1725. """
  1726. def _no_negative_indexes():
  1727. if not allow_negative:
  1728. raise IndexError(
  1729. "negative indexes are not accepted by SQL "
  1730. "index / slice operators"
  1731. )
  1732. else:
  1733. util.warn_deprecated_20(
  1734. "Support for negative indexes for SQL index / slice operators "
  1735. "will be "
  1736. "removed in 2.0; these operators fetch the complete result "
  1737. "and do not work efficiently."
  1738. )
  1739. if isinstance(item, slice):
  1740. start, stop, step = util.decode_slice(item)
  1741. if (
  1742. isinstance(stop, int)
  1743. and isinstance(start, int)
  1744. and stop - start <= 0
  1745. ):
  1746. return []
  1747. elif (isinstance(start, int) and start < 0) or (
  1748. isinstance(stop, int) and stop < 0
  1749. ):
  1750. _no_negative_indexes()
  1751. return list(iterable_query)[item]
  1752. res = iterable_query.slice(start, stop)
  1753. if step is not None:
  1754. return list(res)[None : None : item.step]
  1755. else:
  1756. return list(res)
  1757. else:
  1758. if item == -1:
  1759. _no_negative_indexes()
  1760. return list(iterable_query)[-1]
  1761. else:
  1762. return list(iterable_query[item : item + 1])[0]