relationships.py 126 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508
  1. # orm/relationships.py
  2. # Copyright (C) 2005-2025 the SQLAlchemy authors and contributors
  3. # <see AUTHORS file>
  4. #
  5. # This module is part of SQLAlchemy and is released under
  6. # the MIT License: https://www.opensource.org/licenses/mit-license.php
  7. """Heuristics related to join conditions as used in
  8. :func:`_orm.relationship`.
  9. Provides the :class:`.JoinCondition` object, which encapsulates
  10. SQL annotation and aliasing behavior focused on the `primaryjoin`
  11. and `secondaryjoin` aspects of :func:`_orm.relationship`.
  12. """
  13. from __future__ import annotations
  14. import collections
  15. from collections import abc
  16. import dataclasses
  17. import inspect as _py_inspect
  18. import itertools
  19. import re
  20. import typing
  21. from typing import Any
  22. from typing import Callable
  23. from typing import cast
  24. from typing import Collection
  25. from typing import Dict
  26. from typing import FrozenSet
  27. from typing import Generic
  28. from typing import Iterable
  29. from typing import Iterator
  30. from typing import List
  31. from typing import NamedTuple
  32. from typing import NoReturn
  33. from typing import Optional
  34. from typing import Sequence
  35. from typing import Set
  36. from typing import Tuple
  37. from typing import Type
  38. from typing import TypeVar
  39. from typing import Union
  40. import weakref
  41. from . import attributes
  42. from . import strategy_options
  43. from ._typing import insp_is_aliased_class
  44. from ._typing import is_has_collection_adapter
  45. from .base import _DeclarativeMapped
  46. from .base import _is_mapped_class
  47. from .base import class_mapper
  48. from .base import DynamicMapped
  49. from .base import LoaderCallableStatus
  50. from .base import PassiveFlag
  51. from .base import state_str
  52. from .base import WriteOnlyMapped
  53. from .interfaces import _AttributeOptions
  54. from .interfaces import _IntrospectsAnnotations
  55. from .interfaces import MANYTOMANY
  56. from .interfaces import MANYTOONE
  57. from .interfaces import ONETOMANY
  58. from .interfaces import PropComparator
  59. from .interfaces import RelationshipDirection
  60. from .interfaces import StrategizedProperty
  61. from .util import _orm_annotate
  62. from .util import _orm_deannotate
  63. from .util import CascadeOptions
  64. from .. import exc as sa_exc
  65. from .. import Exists
  66. from .. import log
  67. from .. import schema
  68. from .. import sql
  69. from .. import util
  70. from ..inspection import inspect
  71. from ..sql import coercions
  72. from ..sql import expression
  73. from ..sql import operators
  74. from ..sql import roles
  75. from ..sql import visitors
  76. from ..sql._typing import _ColumnExpressionArgument
  77. from ..sql._typing import _HasClauseElement
  78. from ..sql.annotation import _safe_annotate
  79. from ..sql.elements import ColumnClause
  80. from ..sql.elements import ColumnElement
  81. from ..sql.util import _deep_annotate
  82. from ..sql.util import _deep_deannotate
  83. from ..sql.util import _shallow_annotate
  84. from ..sql.util import adapt_criterion_to_null
  85. from ..sql.util import ClauseAdapter
  86. from ..sql.util import join_condition
  87. from ..sql.util import selectables_overlap
  88. from ..sql.util import visit_binary_product
  89. from ..util.typing import de_optionalize_union_types
  90. from ..util.typing import Literal
  91. from ..util.typing import resolve_name_to_real_class_name
  92. if typing.TYPE_CHECKING:
  93. from ._typing import _EntityType
  94. from ._typing import _ExternalEntityType
  95. from ._typing import _IdentityKeyType
  96. from ._typing import _InstanceDict
  97. from ._typing import _InternalEntityType
  98. from ._typing import _O
  99. from ._typing import _RegistryType
  100. from .base import Mapped
  101. from .clsregistry import _class_resolver
  102. from .clsregistry import _ModNS
  103. from .decl_base import _ClassScanMapperConfig
  104. from .dependency import DependencyProcessor
  105. from .mapper import Mapper
  106. from .query import Query
  107. from .session import Session
  108. from .state import InstanceState
  109. from .strategies import LazyLoader
  110. from .util import AliasedClass
  111. from .util import AliasedInsp
  112. from ..sql._typing import _CoreAdapterProto
  113. from ..sql._typing import _EquivalentColumnMap
  114. from ..sql._typing import _InfoType
  115. from ..sql.annotation import _AnnotationDict
  116. from ..sql.annotation import SupportsAnnotations
  117. from ..sql.elements import BinaryExpression
  118. from ..sql.elements import BindParameter
  119. from ..sql.elements import ClauseElement
  120. from ..sql.schema import Table
  121. from ..sql.selectable import FromClause
  122. from ..util.typing import _AnnotationScanType
  123. from ..util.typing import RODescriptorReference
  124. _T = TypeVar("_T", bound=Any)
  125. _T1 = TypeVar("_T1", bound=Any)
  126. _T2 = TypeVar("_T2", bound=Any)
  127. _PT = TypeVar("_PT", bound=Any)
  128. _PT2 = TypeVar("_PT2", bound=Any)
  129. _RelationshipArgumentType = Union[
  130. str,
  131. Type[_T],
  132. Callable[[], Type[_T]],
  133. "Mapper[_T]",
  134. "AliasedClass[_T]",
  135. Callable[[], "Mapper[_T]"],
  136. Callable[[], "AliasedClass[_T]"],
  137. ]
  138. _LazyLoadArgumentType = Literal[
  139. "select",
  140. "joined",
  141. "selectin",
  142. "subquery",
  143. "raise",
  144. "raise_on_sql",
  145. "noload",
  146. "immediate",
  147. "write_only",
  148. "dynamic",
  149. True,
  150. False,
  151. None,
  152. ]
  153. _RelationshipJoinConditionArgument = Union[
  154. str, _ColumnExpressionArgument[bool]
  155. ]
  156. _RelationshipSecondaryArgument = Union[
  157. "FromClause", str, Callable[[], "FromClause"]
  158. ]
  159. _ORMOrderByArgument = Union[
  160. Literal[False],
  161. str,
  162. _ColumnExpressionArgument[Any],
  163. Callable[[], _ColumnExpressionArgument[Any]],
  164. Callable[[], Iterable[_ColumnExpressionArgument[Any]]],
  165. Iterable[Union[str, _ColumnExpressionArgument[Any]]],
  166. ]
  167. ORMBackrefArgument = Union[str, Tuple[str, Dict[str, Any]]]
  168. _ORMColCollectionElement = Union[
  169. ColumnClause[Any],
  170. _HasClauseElement[Any],
  171. roles.DMLColumnRole,
  172. "Mapped[Any]",
  173. ]
  174. _ORMColCollectionArgument = Union[
  175. str,
  176. Sequence[_ORMColCollectionElement],
  177. Callable[[], Sequence[_ORMColCollectionElement]],
  178. Callable[[], _ORMColCollectionElement],
  179. _ORMColCollectionElement,
  180. ]
  181. _CEA = TypeVar("_CEA", bound=_ColumnExpressionArgument[Any])
  182. _CE = TypeVar("_CE", bound="ColumnElement[Any]")
  183. _ColumnPairIterable = Iterable[Tuple[ColumnElement[Any], ColumnElement[Any]]]
  184. _ColumnPairs = Sequence[Tuple[ColumnElement[Any], ColumnElement[Any]]]
  185. _MutableColumnPairs = List[Tuple[ColumnElement[Any], ColumnElement[Any]]]
  186. def remote(expr: _CEA) -> _CEA:
  187. """Annotate a portion of a primaryjoin expression
  188. with a 'remote' annotation.
  189. See the section :ref:`relationship_custom_foreign` for a
  190. description of use.
  191. .. seealso::
  192. :ref:`relationship_custom_foreign`
  193. :func:`.foreign`
  194. """
  195. return _annotate_columns( # type: ignore
  196. coercions.expect(roles.ColumnArgumentRole, expr), {"remote": True}
  197. )
  198. def foreign(expr: _CEA) -> _CEA:
  199. """Annotate a portion of a primaryjoin expression
  200. with a 'foreign' annotation.
  201. See the section :ref:`relationship_custom_foreign` for a
  202. description of use.
  203. .. seealso::
  204. :ref:`relationship_custom_foreign`
  205. :func:`.remote`
  206. """
  207. return _annotate_columns( # type: ignore
  208. coercions.expect(roles.ColumnArgumentRole, expr), {"foreign": True}
  209. )
  210. @dataclasses.dataclass
  211. class _RelationshipArg(Generic[_T1, _T2]):
  212. """stores a user-defined parameter value that must be resolved and
  213. parsed later at mapper configuration time.
  214. """
  215. __slots__ = "name", "argument", "resolved"
  216. name: str
  217. argument: _T1
  218. resolved: Optional[_T2]
  219. def _is_populated(self) -> bool:
  220. return self.argument is not None
  221. def _resolve_against_registry(
  222. self, clsregistry_resolver: Callable[[str, bool], _class_resolver]
  223. ) -> None:
  224. attr_value = self.argument
  225. if isinstance(attr_value, str):
  226. self.resolved = clsregistry_resolver(
  227. attr_value, self.name == "secondary"
  228. )()
  229. elif callable(attr_value) and not _is_mapped_class(attr_value):
  230. self.resolved = attr_value()
  231. else:
  232. self.resolved = attr_value
  233. _RelationshipOrderByArg = Union[Literal[False], Tuple[ColumnElement[Any], ...]]
  234. class _RelationshipArgs(NamedTuple):
  235. """stores user-passed parameters that are resolved at mapper configuration
  236. time.
  237. """
  238. secondary: _RelationshipArg[
  239. Optional[_RelationshipSecondaryArgument],
  240. Optional[FromClause],
  241. ]
  242. primaryjoin: _RelationshipArg[
  243. Optional[_RelationshipJoinConditionArgument],
  244. Optional[ColumnElement[Any]],
  245. ]
  246. secondaryjoin: _RelationshipArg[
  247. Optional[_RelationshipJoinConditionArgument],
  248. Optional[ColumnElement[Any]],
  249. ]
  250. order_by: _RelationshipArg[_ORMOrderByArgument, _RelationshipOrderByArg]
  251. foreign_keys: _RelationshipArg[
  252. Optional[_ORMColCollectionArgument], Set[ColumnElement[Any]]
  253. ]
  254. remote_side: _RelationshipArg[
  255. Optional[_ORMColCollectionArgument], Set[ColumnElement[Any]]
  256. ]
  257. @log.class_logger
  258. class RelationshipProperty(
  259. _IntrospectsAnnotations, StrategizedProperty[_T], log.Identified
  260. ):
  261. """Describes an object property that holds a single item or list
  262. of items that correspond to a related database table.
  263. Public constructor is the :func:`_orm.relationship` function.
  264. .. seealso::
  265. :ref:`relationship_config_toplevel`
  266. """
  267. strategy_wildcard_key = strategy_options._RELATIONSHIP_TOKEN
  268. inherit_cache = True
  269. """:meta private:"""
  270. _links_to_entity = True
  271. _is_relationship = True
  272. _overlaps: Sequence[str]
  273. _lazy_strategy: LazyLoader
  274. _persistence_only = dict(
  275. passive_deletes=False,
  276. passive_updates=True,
  277. enable_typechecks=True,
  278. active_history=False,
  279. cascade_backrefs=False,
  280. )
  281. _dependency_processor: Optional[DependencyProcessor] = None
  282. primaryjoin: ColumnElement[bool]
  283. secondaryjoin: Optional[ColumnElement[bool]]
  284. secondary: Optional[FromClause]
  285. _join_condition: JoinCondition
  286. order_by: _RelationshipOrderByArg
  287. _user_defined_foreign_keys: Set[ColumnElement[Any]]
  288. _calculated_foreign_keys: Set[ColumnElement[Any]]
  289. remote_side: Set[ColumnElement[Any]]
  290. local_columns: Set[ColumnElement[Any]]
  291. synchronize_pairs: _ColumnPairs
  292. secondary_synchronize_pairs: Optional[_ColumnPairs]
  293. local_remote_pairs: Optional[_ColumnPairs]
  294. direction: RelationshipDirection
  295. _init_args: _RelationshipArgs
  296. def __init__(
  297. self,
  298. argument: Optional[_RelationshipArgumentType[_T]] = None,
  299. secondary: Optional[_RelationshipSecondaryArgument] = None,
  300. *,
  301. uselist: Optional[bool] = None,
  302. collection_class: Optional[
  303. Union[Type[Collection[Any]], Callable[[], Collection[Any]]]
  304. ] = None,
  305. primaryjoin: Optional[_RelationshipJoinConditionArgument] = None,
  306. secondaryjoin: Optional[_RelationshipJoinConditionArgument] = None,
  307. back_populates: Optional[str] = None,
  308. order_by: _ORMOrderByArgument = False,
  309. backref: Optional[ORMBackrefArgument] = None,
  310. overlaps: Optional[str] = None,
  311. post_update: bool = False,
  312. cascade: str = "save-update, merge",
  313. viewonly: bool = False,
  314. attribute_options: Optional[_AttributeOptions] = None,
  315. lazy: _LazyLoadArgumentType = "select",
  316. passive_deletes: Union[Literal["all"], bool] = False,
  317. passive_updates: bool = True,
  318. active_history: bool = False,
  319. enable_typechecks: bool = True,
  320. foreign_keys: Optional[_ORMColCollectionArgument] = None,
  321. remote_side: Optional[_ORMColCollectionArgument] = None,
  322. join_depth: Optional[int] = None,
  323. comparator_factory: Optional[
  324. Type[RelationshipProperty.Comparator[Any]]
  325. ] = None,
  326. single_parent: bool = False,
  327. innerjoin: bool = False,
  328. distinct_target_key: Optional[bool] = None,
  329. load_on_pending: bool = False,
  330. query_class: Optional[Type[Query[Any]]] = None,
  331. info: Optional[_InfoType] = None,
  332. omit_join: Literal[None, False] = None,
  333. sync_backref: Optional[bool] = None,
  334. doc: Optional[str] = None,
  335. bake_queries: Literal[True] = True,
  336. cascade_backrefs: Literal[False] = False,
  337. _local_remote_pairs: Optional[_ColumnPairs] = None,
  338. _legacy_inactive_history_style: bool = False,
  339. ):
  340. super().__init__(attribute_options=attribute_options)
  341. self.uselist = uselist
  342. self.argument = argument
  343. self._init_args = _RelationshipArgs(
  344. _RelationshipArg("secondary", secondary, None),
  345. _RelationshipArg("primaryjoin", primaryjoin, None),
  346. _RelationshipArg("secondaryjoin", secondaryjoin, None),
  347. _RelationshipArg("order_by", order_by, None),
  348. _RelationshipArg("foreign_keys", foreign_keys, None),
  349. _RelationshipArg("remote_side", remote_side, None),
  350. )
  351. self.post_update = post_update
  352. self.viewonly = viewonly
  353. if viewonly:
  354. self._warn_for_persistence_only_flags(
  355. passive_deletes=passive_deletes,
  356. passive_updates=passive_updates,
  357. enable_typechecks=enable_typechecks,
  358. active_history=active_history,
  359. cascade_backrefs=cascade_backrefs,
  360. )
  361. if viewonly and sync_backref:
  362. raise sa_exc.ArgumentError(
  363. "sync_backref and viewonly cannot both be True"
  364. )
  365. self.sync_backref = sync_backref
  366. self.lazy = lazy
  367. self.single_parent = single_parent
  368. self.collection_class = collection_class
  369. self.passive_deletes = passive_deletes
  370. if cascade_backrefs:
  371. raise sa_exc.ArgumentError(
  372. "The 'cascade_backrefs' parameter passed to "
  373. "relationship() may only be set to False."
  374. )
  375. self.passive_updates = passive_updates
  376. self.enable_typechecks = enable_typechecks
  377. self.query_class = query_class
  378. self.innerjoin = innerjoin
  379. self.distinct_target_key = distinct_target_key
  380. self.doc = doc
  381. self.active_history = active_history
  382. self._legacy_inactive_history_style = _legacy_inactive_history_style
  383. self.join_depth = join_depth
  384. if omit_join:
  385. util.warn(
  386. "setting omit_join to True is not supported; selectin "
  387. "loading of this relationship may not work correctly if this "
  388. "flag is set explicitly. omit_join optimization is "
  389. "automatically detected for conditions under which it is "
  390. "supported."
  391. )
  392. self.omit_join = omit_join
  393. self.local_remote_pairs = _local_remote_pairs
  394. self.load_on_pending = load_on_pending
  395. self.comparator_factory = (
  396. comparator_factory or RelationshipProperty.Comparator
  397. )
  398. util.set_creation_order(self)
  399. if info is not None:
  400. self.info.update(info)
  401. self.strategy_key = (("lazy", self.lazy),)
  402. self._reverse_property: Set[RelationshipProperty[Any]] = set()
  403. if overlaps:
  404. self._overlaps = set(re.split(r"\s*,\s*", overlaps)) # type: ignore # noqa: E501
  405. else:
  406. self._overlaps = ()
  407. self.cascade = cascade
  408. self.back_populates = back_populates
  409. if self.back_populates:
  410. if backref:
  411. raise sa_exc.ArgumentError(
  412. "backref and back_populates keyword arguments "
  413. "are mutually exclusive"
  414. )
  415. self.backref = None
  416. else:
  417. self.backref = backref
  418. def _warn_for_persistence_only_flags(self, **kw: Any) -> None:
  419. for k, v in kw.items():
  420. if v != self._persistence_only[k]:
  421. # we are warning here rather than warn deprecated as this is a
  422. # configuration mistake, and Python shows regular warnings more
  423. # aggressively than deprecation warnings by default. Unlike the
  424. # case of setting viewonly with cascade, the settings being
  425. # warned about here are not actively doing the wrong thing
  426. # against viewonly=True, so it is not as urgent to have these
  427. # raise an error.
  428. util.warn(
  429. "Setting %s on relationship() while also "
  430. "setting viewonly=True does not make sense, as a "
  431. "viewonly=True relationship does not perform persistence "
  432. "operations. This configuration may raise an error "
  433. "in a future release." % (k,)
  434. )
  435. def instrument_class(self, mapper: Mapper[Any]) -> None:
  436. attributes.register_descriptor(
  437. mapper.class_,
  438. self.key,
  439. comparator=self.comparator_factory(self, mapper),
  440. parententity=mapper,
  441. doc=self.doc,
  442. )
  443. class Comparator(util.MemoizedSlots, PropComparator[_PT]):
  444. """Produce boolean, comparison, and other operators for
  445. :class:`.RelationshipProperty` attributes.
  446. See the documentation for :class:`.PropComparator` for a brief
  447. overview of ORM level operator definition.
  448. .. seealso::
  449. :class:`.PropComparator`
  450. :class:`.ColumnProperty.Comparator`
  451. :class:`.ColumnOperators`
  452. :ref:`types_operators`
  453. :attr:`.TypeEngine.comparator_factory`
  454. """
  455. __slots__ = (
  456. "entity",
  457. "mapper",
  458. "property",
  459. "_of_type",
  460. "_extra_criteria",
  461. )
  462. prop: RODescriptorReference[RelationshipProperty[_PT]]
  463. _of_type: Optional[_EntityType[_PT]]
  464. def __init__(
  465. self,
  466. prop: RelationshipProperty[_PT],
  467. parentmapper: _InternalEntityType[Any],
  468. adapt_to_entity: Optional[AliasedInsp[Any]] = None,
  469. of_type: Optional[_EntityType[_PT]] = None,
  470. extra_criteria: Tuple[ColumnElement[bool], ...] = (),
  471. ):
  472. """Construction of :class:`.RelationshipProperty.Comparator`
  473. is internal to the ORM's attribute mechanics.
  474. """
  475. self.prop = prop
  476. self._parententity = parentmapper
  477. self._adapt_to_entity = adapt_to_entity
  478. if of_type:
  479. self._of_type = of_type
  480. else:
  481. self._of_type = None
  482. self._extra_criteria = extra_criteria
  483. def adapt_to_entity(
  484. self, adapt_to_entity: AliasedInsp[Any]
  485. ) -> RelationshipProperty.Comparator[Any]:
  486. return self.__class__(
  487. self.prop,
  488. self._parententity,
  489. adapt_to_entity=adapt_to_entity,
  490. of_type=self._of_type,
  491. )
  492. entity: _InternalEntityType[_PT]
  493. """The target entity referred to by this
  494. :class:`.RelationshipProperty.Comparator`.
  495. This is either a :class:`_orm.Mapper` or :class:`.AliasedInsp`
  496. object.
  497. This is the "target" or "remote" side of the
  498. :func:`_orm.relationship`.
  499. """
  500. mapper: Mapper[_PT]
  501. """The target :class:`_orm.Mapper` referred to by this
  502. :class:`.RelationshipProperty.Comparator`.
  503. This is the "target" or "remote" side of the
  504. :func:`_orm.relationship`.
  505. """
  506. def _memoized_attr_entity(self) -> _InternalEntityType[_PT]:
  507. if self._of_type:
  508. return inspect(self._of_type) # type: ignore
  509. else:
  510. return self.prop.entity
  511. def _memoized_attr_mapper(self) -> Mapper[_PT]:
  512. return self.entity.mapper
  513. def _source_selectable(self) -> FromClause:
  514. if self._adapt_to_entity:
  515. return self._adapt_to_entity.selectable
  516. else:
  517. return self.property.parent._with_polymorphic_selectable
  518. def __clause_element__(self) -> ColumnElement[bool]:
  519. adapt_from = self._source_selectable()
  520. if self._of_type:
  521. of_type_entity = inspect(self._of_type)
  522. else:
  523. of_type_entity = None
  524. (
  525. pj,
  526. sj,
  527. source,
  528. dest,
  529. secondary,
  530. target_adapter,
  531. ) = self.prop._create_joins(
  532. source_selectable=adapt_from,
  533. source_polymorphic=True,
  534. of_type_entity=of_type_entity,
  535. alias_secondary=True,
  536. extra_criteria=self._extra_criteria,
  537. )
  538. if sj is not None:
  539. return pj & sj
  540. else:
  541. return pj
  542. def of_type(self, class_: _EntityType[Any]) -> PropComparator[_PT]:
  543. r"""Redefine this object in terms of a polymorphic subclass.
  544. See :meth:`.PropComparator.of_type` for an example.
  545. """
  546. return RelationshipProperty.Comparator(
  547. self.prop,
  548. self._parententity,
  549. adapt_to_entity=self._adapt_to_entity,
  550. of_type=class_,
  551. extra_criteria=self._extra_criteria,
  552. )
  553. def and_(
  554. self, *criteria: _ColumnExpressionArgument[bool]
  555. ) -> PropComparator[Any]:
  556. """Add AND criteria.
  557. See :meth:`.PropComparator.and_` for an example.
  558. .. versionadded:: 1.4
  559. """
  560. exprs = tuple(
  561. coercions.expect(roles.WhereHavingRole, clause)
  562. for clause in util.coerce_generator_arg(criteria)
  563. )
  564. return RelationshipProperty.Comparator(
  565. self.prop,
  566. self._parententity,
  567. adapt_to_entity=self._adapt_to_entity,
  568. of_type=self._of_type,
  569. extra_criteria=self._extra_criteria + exprs,
  570. )
  571. def in_(self, other: Any) -> NoReturn:
  572. """Produce an IN clause - this is not implemented
  573. for :func:`_orm.relationship`-based attributes at this time.
  574. """
  575. raise NotImplementedError(
  576. "in_() not yet supported for "
  577. "relationships. For a simple "
  578. "many-to-one, use in_() against "
  579. "the set of foreign key values."
  580. )
  581. # https://github.com/python/mypy/issues/4266
  582. __hash__ = None # type: ignore
  583. def __eq__(self, other: Any) -> ColumnElement[bool]: # type: ignore[override] # noqa: E501
  584. """Implement the ``==`` operator.
  585. In a many-to-one context, such as:
  586. .. sourcecode:: text
  587. MyClass.some_prop == <some object>
  588. this will typically produce a
  589. clause such as:
  590. .. sourcecode:: text
  591. mytable.related_id == <some id>
  592. Where ``<some id>`` is the primary key of the given
  593. object.
  594. The ``==`` operator provides partial functionality for non-
  595. many-to-one comparisons:
  596. * Comparisons against collections are not supported.
  597. Use :meth:`~.Relationship.Comparator.contains`.
  598. * Compared to a scalar one-to-many, will produce a
  599. clause that compares the target columns in the parent to
  600. the given target.
  601. * Compared to a scalar many-to-many, an alias
  602. of the association table will be rendered as
  603. well, forming a natural join that is part of the
  604. main body of the query. This will not work for
  605. queries that go beyond simple AND conjunctions of
  606. comparisons, such as those which use OR. Use
  607. explicit joins, outerjoins, or
  608. :meth:`~.Relationship.Comparator.has` for
  609. more comprehensive non-many-to-one scalar
  610. membership tests.
  611. * Comparisons against ``None`` given in a one-to-many
  612. or many-to-many context produce a NOT EXISTS clause.
  613. """
  614. if other is None or isinstance(other, expression.Null):
  615. if self.property.direction in [ONETOMANY, MANYTOMANY]:
  616. return ~self._criterion_exists()
  617. else:
  618. return _orm_annotate(
  619. self.property._optimized_compare(
  620. None, adapt_source=self.adapter
  621. )
  622. )
  623. elif self.property.uselist:
  624. raise sa_exc.InvalidRequestError(
  625. "Can't compare a collection to an object or collection; "
  626. "use contains() to test for membership."
  627. )
  628. else:
  629. return _orm_annotate(
  630. self.property._optimized_compare(
  631. other, adapt_source=self.adapter
  632. )
  633. )
  634. def _criterion_exists(
  635. self,
  636. criterion: Optional[_ColumnExpressionArgument[bool]] = None,
  637. **kwargs: Any,
  638. ) -> Exists:
  639. where_criteria = (
  640. coercions.expect(roles.WhereHavingRole, criterion)
  641. if criterion is not None
  642. else None
  643. )
  644. if getattr(self, "_of_type", None):
  645. info: Optional[_InternalEntityType[Any]] = inspect(
  646. self._of_type
  647. )
  648. assert info is not None
  649. target_mapper, to_selectable, is_aliased_class = (
  650. info.mapper,
  651. info.selectable,
  652. info.is_aliased_class,
  653. )
  654. if self.property._is_self_referential and not is_aliased_class:
  655. to_selectable = to_selectable._anonymous_fromclause()
  656. single_crit = target_mapper._single_table_criterion
  657. if single_crit is not None:
  658. if where_criteria is not None:
  659. where_criteria = single_crit & where_criteria
  660. else:
  661. where_criteria = single_crit
  662. else:
  663. is_aliased_class = False
  664. to_selectable = None
  665. if self.adapter:
  666. source_selectable = self._source_selectable()
  667. else:
  668. source_selectable = None
  669. (
  670. pj,
  671. sj,
  672. source,
  673. dest,
  674. secondary,
  675. target_adapter,
  676. ) = self.property._create_joins(
  677. dest_selectable=to_selectable,
  678. source_selectable=source_selectable,
  679. )
  680. for k in kwargs:
  681. crit = getattr(self.property.mapper.class_, k) == kwargs[k]
  682. if where_criteria is None:
  683. where_criteria = crit
  684. else:
  685. where_criteria = where_criteria & crit
  686. # annotate the *local* side of the join condition, in the case
  687. # of pj + sj this is the full primaryjoin, in the case of just
  688. # pj its the local side of the primaryjoin.
  689. if sj is not None:
  690. j = _orm_annotate(pj) & sj
  691. else:
  692. j = _orm_annotate(pj, exclude=self.property.remote_side)
  693. if (
  694. where_criteria is not None
  695. and target_adapter
  696. and not is_aliased_class
  697. ):
  698. # limit this adapter to annotated only?
  699. where_criteria = target_adapter.traverse(where_criteria)
  700. # only have the "joined left side" of what we
  701. # return be subject to Query adaption. The right
  702. # side of it is used for an exists() subquery and
  703. # should not correlate or otherwise reach out
  704. # to anything in the enclosing query.
  705. if where_criteria is not None:
  706. where_criteria = where_criteria._annotate(
  707. {"no_replacement_traverse": True}
  708. )
  709. crit = j & sql.True_._ifnone(where_criteria)
  710. if secondary is not None:
  711. ex = (
  712. sql.exists(1)
  713. .where(crit)
  714. .select_from(dest, secondary)
  715. .correlate_except(dest, secondary)
  716. )
  717. else:
  718. ex = (
  719. sql.exists(1)
  720. .where(crit)
  721. .select_from(dest)
  722. .correlate_except(dest)
  723. )
  724. return ex
  725. def any(
  726. self,
  727. criterion: Optional[_ColumnExpressionArgument[bool]] = None,
  728. **kwargs: Any,
  729. ) -> ColumnElement[bool]:
  730. """Produce an expression that tests a collection against
  731. particular criterion, using EXISTS.
  732. An expression like::
  733. session.query(MyClass).filter(
  734. MyClass.somereference.any(SomeRelated.x == 2)
  735. )
  736. Will produce a query like:
  737. .. sourcecode:: sql
  738. SELECT * FROM my_table WHERE
  739. EXISTS (SELECT 1 FROM related WHERE related.my_id=my_table.id
  740. AND related.x=2)
  741. Because :meth:`~.Relationship.Comparator.any` uses
  742. a correlated subquery, its performance is not nearly as
  743. good when compared against large target tables as that of
  744. using a join.
  745. :meth:`~.Relationship.Comparator.any` is particularly
  746. useful for testing for empty collections::
  747. session.query(MyClass).filter(~MyClass.somereference.any())
  748. will produce:
  749. .. sourcecode:: sql
  750. SELECT * FROM my_table WHERE
  751. NOT (EXISTS (SELECT 1 FROM related WHERE
  752. related.my_id=my_table.id))
  753. :meth:`~.Relationship.Comparator.any` is only
  754. valid for collections, i.e. a :func:`_orm.relationship`
  755. that has ``uselist=True``. For scalar references,
  756. use :meth:`~.Relationship.Comparator.has`.
  757. """
  758. if not self.property.uselist:
  759. raise sa_exc.InvalidRequestError(
  760. "'any()' not implemented for scalar "
  761. "attributes. Use has()."
  762. )
  763. return self._criterion_exists(criterion, **kwargs)
  764. def has(
  765. self,
  766. criterion: Optional[_ColumnExpressionArgument[bool]] = None,
  767. **kwargs: Any,
  768. ) -> ColumnElement[bool]:
  769. """Produce an expression that tests a scalar reference against
  770. particular criterion, using EXISTS.
  771. An expression like::
  772. session.query(MyClass).filter(
  773. MyClass.somereference.has(SomeRelated.x == 2)
  774. )
  775. Will produce a query like:
  776. .. sourcecode:: sql
  777. SELECT * FROM my_table WHERE
  778. EXISTS (SELECT 1 FROM related WHERE
  779. related.id==my_table.related_id AND related.x=2)
  780. Because :meth:`~.Relationship.Comparator.has` uses
  781. a correlated subquery, its performance is not nearly as
  782. good when compared against large target tables as that of
  783. using a join.
  784. :meth:`~.Relationship.Comparator.has` is only
  785. valid for scalar references, i.e. a :func:`_orm.relationship`
  786. that has ``uselist=False``. For collection references,
  787. use :meth:`~.Relationship.Comparator.any`.
  788. """
  789. if self.property.uselist:
  790. raise sa_exc.InvalidRequestError(
  791. "'has()' not implemented for collections. Use any()."
  792. )
  793. return self._criterion_exists(criterion, **kwargs)
  794. def contains(
  795. self, other: _ColumnExpressionArgument[Any], **kwargs: Any
  796. ) -> ColumnElement[bool]:
  797. """Return a simple expression that tests a collection for
  798. containment of a particular item.
  799. :meth:`~.Relationship.Comparator.contains` is
  800. only valid for a collection, i.e. a
  801. :func:`_orm.relationship` that implements
  802. one-to-many or many-to-many with ``uselist=True``.
  803. When used in a simple one-to-many context, an
  804. expression like::
  805. MyClass.contains(other)
  806. Produces a clause like:
  807. .. sourcecode:: sql
  808. mytable.id == <some id>
  809. Where ``<some id>`` is the value of the foreign key
  810. attribute on ``other`` which refers to the primary
  811. key of its parent object. From this it follows that
  812. :meth:`~.Relationship.Comparator.contains` is
  813. very useful when used with simple one-to-many
  814. operations.
  815. For many-to-many operations, the behavior of
  816. :meth:`~.Relationship.Comparator.contains`
  817. has more caveats. The association table will be
  818. rendered in the statement, producing an "implicit"
  819. join, that is, includes multiple tables in the FROM
  820. clause which are equated in the WHERE clause::
  821. query(MyClass).filter(MyClass.contains(other))
  822. Produces a query like:
  823. .. sourcecode:: sql
  824. SELECT * FROM my_table, my_association_table AS
  825. my_association_table_1 WHERE
  826. my_table.id = my_association_table_1.parent_id
  827. AND my_association_table_1.child_id = <some id>
  828. Where ``<some id>`` would be the primary key of
  829. ``other``. From the above, it is clear that
  830. :meth:`~.Relationship.Comparator.contains`
  831. will **not** work with many-to-many collections when
  832. used in queries that move beyond simple AND
  833. conjunctions, such as multiple
  834. :meth:`~.Relationship.Comparator.contains`
  835. expressions joined by OR. In such cases subqueries or
  836. explicit "outer joins" will need to be used instead.
  837. See :meth:`~.Relationship.Comparator.any` for
  838. a less-performant alternative using EXISTS, or refer
  839. to :meth:`_query.Query.outerjoin`
  840. as well as :ref:`orm_queryguide_joins`
  841. for more details on constructing outer joins.
  842. kwargs may be ignored by this operator but are required for API
  843. conformance.
  844. """
  845. if not self.prop.uselist:
  846. raise sa_exc.InvalidRequestError(
  847. "'contains' not implemented for scalar "
  848. "attributes. Use =="
  849. )
  850. clause = self.prop._optimized_compare(
  851. other, adapt_source=self.adapter
  852. )
  853. if self.prop.secondaryjoin is not None:
  854. clause.negation_clause = self.__negated_contains_or_equals(
  855. other
  856. )
  857. return clause
  858. def __negated_contains_or_equals(
  859. self, other: Any
  860. ) -> ColumnElement[bool]:
  861. if self.prop.direction == MANYTOONE:
  862. state = attributes.instance_state(other)
  863. def state_bindparam(
  864. local_col: ColumnElement[Any],
  865. state: InstanceState[Any],
  866. remote_col: ColumnElement[Any],
  867. ) -> BindParameter[Any]:
  868. dict_ = state.dict
  869. return sql.bindparam(
  870. local_col.key,
  871. type_=local_col.type,
  872. unique=True,
  873. callable_=self.prop._get_attr_w_warn_on_none(
  874. self.prop.mapper, state, dict_, remote_col
  875. ),
  876. )
  877. def adapt(col: _CE) -> _CE:
  878. if self.adapter:
  879. return self.adapter(col)
  880. else:
  881. return col
  882. if self.property._use_get:
  883. return sql.and_(
  884. *[
  885. sql.or_(
  886. adapt(x)
  887. != state_bindparam(adapt(x), state, y),
  888. adapt(x) == None,
  889. )
  890. for (x, y) in self.property.local_remote_pairs
  891. ]
  892. )
  893. criterion = sql.and_(
  894. *[
  895. x == y
  896. for (x, y) in zip(
  897. self.property.mapper.primary_key,
  898. self.property.mapper.primary_key_from_instance(other),
  899. )
  900. ]
  901. )
  902. return ~self._criterion_exists(criterion)
  903. def __ne__(self, other: Any) -> ColumnElement[bool]: # type: ignore[override] # noqa: E501
  904. """Implement the ``!=`` operator.
  905. In a many-to-one context, such as:
  906. .. sourcecode:: text
  907. MyClass.some_prop != <some object>
  908. This will typically produce a clause such as:
  909. .. sourcecode:: sql
  910. mytable.related_id != <some id>
  911. Where ``<some id>`` is the primary key of the
  912. given object.
  913. The ``!=`` operator provides partial functionality for non-
  914. many-to-one comparisons:
  915. * Comparisons against collections are not supported.
  916. Use
  917. :meth:`~.Relationship.Comparator.contains`
  918. in conjunction with :func:`_expression.not_`.
  919. * Compared to a scalar one-to-many, will produce a
  920. clause that compares the target columns in the parent to
  921. the given target.
  922. * Compared to a scalar many-to-many, an alias
  923. of the association table will be rendered as
  924. well, forming a natural join that is part of the
  925. main body of the query. This will not work for
  926. queries that go beyond simple AND conjunctions of
  927. comparisons, such as those which use OR. Use
  928. explicit joins, outerjoins, or
  929. :meth:`~.Relationship.Comparator.has` in
  930. conjunction with :func:`_expression.not_` for
  931. more comprehensive non-many-to-one scalar
  932. membership tests.
  933. * Comparisons against ``None`` given in a one-to-many
  934. or many-to-many context produce an EXISTS clause.
  935. """
  936. if other is None or isinstance(other, expression.Null):
  937. if self.property.direction == MANYTOONE:
  938. return _orm_annotate(
  939. ~self.property._optimized_compare(
  940. None, adapt_source=self.adapter
  941. )
  942. )
  943. else:
  944. return self._criterion_exists()
  945. elif self.property.uselist:
  946. raise sa_exc.InvalidRequestError(
  947. "Can't compare a collection"
  948. " to an object or collection; use "
  949. "contains() to test for membership."
  950. )
  951. else:
  952. return _orm_annotate(self.__negated_contains_or_equals(other))
  953. def _memoized_attr_property(self) -> RelationshipProperty[_PT]:
  954. self.prop.parent._check_configure()
  955. return self.prop
  956. def _with_parent(
  957. self,
  958. instance: object,
  959. alias_secondary: bool = True,
  960. from_entity: Optional[_EntityType[Any]] = None,
  961. ) -> ColumnElement[bool]:
  962. assert instance is not None
  963. adapt_source: Optional[_CoreAdapterProto] = None
  964. if from_entity is not None:
  965. insp: Optional[_InternalEntityType[Any]] = inspect(from_entity)
  966. assert insp is not None
  967. if insp_is_aliased_class(insp):
  968. adapt_source = insp._adapter.adapt_clause
  969. return self._optimized_compare(
  970. instance,
  971. value_is_parent=True,
  972. adapt_source=adapt_source,
  973. alias_secondary=alias_secondary,
  974. )
  975. def _optimized_compare(
  976. self,
  977. state: Any,
  978. value_is_parent: bool = False,
  979. adapt_source: Optional[_CoreAdapterProto] = None,
  980. alias_secondary: bool = True,
  981. ) -> ColumnElement[bool]:
  982. if state is not None:
  983. try:
  984. state = inspect(state)
  985. except sa_exc.NoInspectionAvailable:
  986. state = None
  987. if state is None or not getattr(state, "is_instance", False):
  988. raise sa_exc.ArgumentError(
  989. "Mapped instance expected for relationship "
  990. "comparison to object. Classes, queries and other "
  991. "SQL elements are not accepted in this context; for "
  992. "comparison with a subquery, "
  993. "use %s.has(**criteria)." % self
  994. )
  995. reverse_direction = not value_is_parent
  996. if state is None:
  997. return self._lazy_none_clause(
  998. reverse_direction, adapt_source=adapt_source
  999. )
  1000. if not reverse_direction:
  1001. criterion, bind_to_col = (
  1002. self._lazy_strategy._lazywhere,
  1003. self._lazy_strategy._bind_to_col,
  1004. )
  1005. else:
  1006. criterion, bind_to_col = (
  1007. self._lazy_strategy._rev_lazywhere,
  1008. self._lazy_strategy._rev_bind_to_col,
  1009. )
  1010. if reverse_direction:
  1011. mapper = self.mapper
  1012. else:
  1013. mapper = self.parent
  1014. dict_ = attributes.instance_dict(state.obj())
  1015. def visit_bindparam(bindparam: BindParameter[Any]) -> None:
  1016. if bindparam._identifying_key in bind_to_col:
  1017. bindparam.callable = self._get_attr_w_warn_on_none(
  1018. mapper,
  1019. state,
  1020. dict_,
  1021. bind_to_col[bindparam._identifying_key],
  1022. )
  1023. if self.secondary is not None and alias_secondary:
  1024. criterion = ClauseAdapter(
  1025. self.secondary._anonymous_fromclause()
  1026. ).traverse(criterion)
  1027. criterion = visitors.cloned_traverse(
  1028. criterion, {}, {"bindparam": visit_bindparam}
  1029. )
  1030. if adapt_source:
  1031. criterion = adapt_source(criterion)
  1032. return criterion
  1033. def _get_attr_w_warn_on_none(
  1034. self,
  1035. mapper: Mapper[Any],
  1036. state: InstanceState[Any],
  1037. dict_: _InstanceDict,
  1038. column: ColumnElement[Any],
  1039. ) -> Callable[[], Any]:
  1040. """Create the callable that is used in a many-to-one expression.
  1041. E.g.::
  1042. u1 = s.query(User).get(5)
  1043. expr = Address.user == u1
  1044. Above, the SQL should be "address.user_id = 5". The callable
  1045. returned by this method produces the value "5" based on the identity
  1046. of ``u1``.
  1047. """
  1048. # in this callable, we're trying to thread the needle through
  1049. # a wide variety of scenarios, including:
  1050. #
  1051. # * the object hasn't been flushed yet and there's no value for
  1052. # the attribute as of yet
  1053. #
  1054. # * the object hasn't been flushed yet but it has a user-defined
  1055. # value
  1056. #
  1057. # * the object has a value but it's expired and not locally present
  1058. #
  1059. # * the object has a value but it's expired and not locally present,
  1060. # and the object is also detached
  1061. #
  1062. # * The object hadn't been flushed yet, there was no value, but
  1063. # later, the object has been expired and detached, and *now*
  1064. # they're trying to evaluate it
  1065. #
  1066. # * the object had a value, but it was changed to a new value, and
  1067. # then expired
  1068. #
  1069. # * the object had a value, but it was changed to a new value, and
  1070. # then expired, then the object was detached
  1071. #
  1072. # * the object has a user-set value, but it's None and we don't do
  1073. # the comparison correctly for that so warn
  1074. #
  1075. prop = mapper.get_property_by_column(column)
  1076. # by invoking this method, InstanceState will track the last known
  1077. # value for this key each time the attribute is to be expired.
  1078. # this feature was added explicitly for use in this method.
  1079. state._track_last_known_value(prop.key)
  1080. lkv_fixed = state._last_known_values
  1081. def _go() -> Any:
  1082. assert lkv_fixed is not None
  1083. last_known = to_return = lkv_fixed[prop.key]
  1084. existing_is_available = (
  1085. last_known is not LoaderCallableStatus.NO_VALUE
  1086. )
  1087. # we support that the value may have changed. so here we
  1088. # try to get the most recent value including re-fetching.
  1089. # only if we can't get a value now due to detachment do we return
  1090. # the last known value
  1091. current_value = mapper._get_state_attr_by_column(
  1092. state,
  1093. dict_,
  1094. column,
  1095. passive=(
  1096. PassiveFlag.PASSIVE_OFF
  1097. if state.persistent
  1098. else PassiveFlag.PASSIVE_NO_FETCH ^ PassiveFlag.INIT_OK
  1099. ),
  1100. )
  1101. if current_value is LoaderCallableStatus.NEVER_SET:
  1102. if not existing_is_available:
  1103. raise sa_exc.InvalidRequestError(
  1104. "Can't resolve value for column %s on object "
  1105. "%s; no value has been set for this column"
  1106. % (column, state_str(state))
  1107. )
  1108. elif current_value is LoaderCallableStatus.PASSIVE_NO_RESULT:
  1109. if not existing_is_available:
  1110. raise sa_exc.InvalidRequestError(
  1111. "Can't resolve value for column %s on object "
  1112. "%s; the object is detached and the value was "
  1113. "expired" % (column, state_str(state))
  1114. )
  1115. else:
  1116. to_return = current_value
  1117. if to_return is None:
  1118. util.warn(
  1119. "Got None for value of column %s; this is unsupported "
  1120. "for a relationship comparison and will not "
  1121. "currently produce an IS comparison "
  1122. "(but may in a future release)" % column
  1123. )
  1124. return to_return
  1125. return _go
  1126. def _lazy_none_clause(
  1127. self,
  1128. reverse_direction: bool = False,
  1129. adapt_source: Optional[_CoreAdapterProto] = None,
  1130. ) -> ColumnElement[bool]:
  1131. if not reverse_direction:
  1132. criterion, bind_to_col = (
  1133. self._lazy_strategy._lazywhere,
  1134. self._lazy_strategy._bind_to_col,
  1135. )
  1136. else:
  1137. criterion, bind_to_col = (
  1138. self._lazy_strategy._rev_lazywhere,
  1139. self._lazy_strategy._rev_bind_to_col,
  1140. )
  1141. criterion = adapt_criterion_to_null(criterion, bind_to_col)
  1142. if adapt_source:
  1143. criterion = adapt_source(criterion)
  1144. return criterion
  1145. def __str__(self) -> str:
  1146. return str(self.parent.class_.__name__) + "." + self.key
  1147. def merge(
  1148. self,
  1149. session: Session,
  1150. source_state: InstanceState[Any],
  1151. source_dict: _InstanceDict,
  1152. dest_state: InstanceState[Any],
  1153. dest_dict: _InstanceDict,
  1154. load: bool,
  1155. _recursive: Dict[Any, object],
  1156. _resolve_conflict_map: Dict[_IdentityKeyType[Any], object],
  1157. ) -> None:
  1158. if load:
  1159. for r in self._reverse_property:
  1160. if (source_state, r) in _recursive:
  1161. return
  1162. if "merge" not in self._cascade:
  1163. return
  1164. if self.key not in source_dict:
  1165. return
  1166. if self.uselist:
  1167. impl = source_state.get_impl(self.key)
  1168. assert is_has_collection_adapter(impl)
  1169. instances_iterable = impl.get_collection(source_state, source_dict)
  1170. # if this is a CollectionAttributeImpl, then empty should
  1171. # be False, otherwise "self.key in source_dict" should not be
  1172. # True
  1173. assert not instances_iterable.empty if impl.collection else True
  1174. if load:
  1175. # for a full merge, pre-load the destination collection,
  1176. # so that individual _merge of each item pulls from identity
  1177. # map for those already present.
  1178. # also assumes CollectionAttributeImpl behavior of loading
  1179. # "old" list in any case
  1180. dest_state.get_impl(self.key).get(
  1181. dest_state, dest_dict, passive=PassiveFlag.PASSIVE_MERGE
  1182. )
  1183. dest_list = []
  1184. for current in instances_iterable:
  1185. current_state = attributes.instance_state(current)
  1186. current_dict = attributes.instance_dict(current)
  1187. _recursive[(current_state, self)] = True
  1188. obj = session._merge(
  1189. current_state,
  1190. current_dict,
  1191. load=load,
  1192. _recursive=_recursive,
  1193. _resolve_conflict_map=_resolve_conflict_map,
  1194. )
  1195. if obj is not None:
  1196. dest_list.append(obj)
  1197. if not load:
  1198. coll = attributes.init_state_collection(
  1199. dest_state, dest_dict, self.key
  1200. )
  1201. for c in dest_list:
  1202. coll.append_without_event(c)
  1203. else:
  1204. dest_impl = dest_state.get_impl(self.key)
  1205. assert is_has_collection_adapter(dest_impl)
  1206. dest_impl.set(
  1207. dest_state,
  1208. dest_dict,
  1209. dest_list,
  1210. _adapt=False,
  1211. passive=PassiveFlag.PASSIVE_MERGE,
  1212. )
  1213. else:
  1214. current = source_dict[self.key]
  1215. if current is not None:
  1216. current_state = attributes.instance_state(current)
  1217. current_dict = attributes.instance_dict(current)
  1218. _recursive[(current_state, self)] = True
  1219. obj = session._merge(
  1220. current_state,
  1221. current_dict,
  1222. load=load,
  1223. _recursive=_recursive,
  1224. _resolve_conflict_map=_resolve_conflict_map,
  1225. )
  1226. else:
  1227. obj = None
  1228. if not load:
  1229. dest_dict[self.key] = obj
  1230. else:
  1231. dest_state.get_impl(self.key).set(
  1232. dest_state, dest_dict, obj, None
  1233. )
  1234. def _value_as_iterable(
  1235. self,
  1236. state: InstanceState[_O],
  1237. dict_: _InstanceDict,
  1238. key: str,
  1239. passive: PassiveFlag = PassiveFlag.PASSIVE_OFF,
  1240. ) -> Sequence[Tuple[InstanceState[_O], _O]]:
  1241. """Return a list of tuples (state, obj) for the given
  1242. key.
  1243. returns an empty list if the value is None/empty/PASSIVE_NO_RESULT
  1244. """
  1245. impl = state.manager[key].impl
  1246. x = impl.get(state, dict_, passive=passive)
  1247. if x is LoaderCallableStatus.PASSIVE_NO_RESULT or x is None:
  1248. return []
  1249. elif is_has_collection_adapter(impl):
  1250. return [
  1251. (attributes.instance_state(o), o)
  1252. for o in impl.get_collection(state, dict_, x, passive=passive)
  1253. ]
  1254. else:
  1255. return [(attributes.instance_state(x), x)]
  1256. def cascade_iterator(
  1257. self,
  1258. type_: str,
  1259. state: InstanceState[Any],
  1260. dict_: _InstanceDict,
  1261. visited_states: Set[InstanceState[Any]],
  1262. halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None,
  1263. ) -> Iterator[Tuple[Any, Mapper[Any], InstanceState[Any], _InstanceDict]]:
  1264. # assert type_ in self._cascade
  1265. # only actively lazy load on the 'delete' cascade
  1266. if type_ != "delete" or self.passive_deletes:
  1267. passive = PassiveFlag.PASSIVE_NO_INITIALIZE
  1268. else:
  1269. passive = PassiveFlag.PASSIVE_OFF | PassiveFlag.NO_RAISE
  1270. if type_ == "save-update":
  1271. tuples = state.manager[self.key].impl.get_all_pending(state, dict_)
  1272. else:
  1273. tuples = self._value_as_iterable(
  1274. state, dict_, self.key, passive=passive
  1275. )
  1276. skip_pending = (
  1277. type_ == "refresh-expire" and "delete-orphan" not in self._cascade
  1278. )
  1279. for instance_state, c in tuples:
  1280. if instance_state in visited_states:
  1281. continue
  1282. if c is None:
  1283. # would like to emit a warning here, but
  1284. # would not be consistent with collection.append(None)
  1285. # current behavior of silently skipping.
  1286. # see [ticket:2229]
  1287. continue
  1288. assert instance_state is not None
  1289. instance_dict = attributes.instance_dict(c)
  1290. if halt_on and halt_on(instance_state):
  1291. continue
  1292. if skip_pending and not instance_state.key:
  1293. continue
  1294. instance_mapper = instance_state.manager.mapper
  1295. if not instance_mapper.isa(self.mapper.class_manager.mapper):
  1296. raise AssertionError(
  1297. "Attribute '%s' on class '%s' "
  1298. "doesn't handle objects "
  1299. "of type '%s'"
  1300. % (self.key, self.parent.class_, c.__class__)
  1301. )
  1302. visited_states.add(instance_state)
  1303. yield c, instance_mapper, instance_state, instance_dict
  1304. @property
  1305. def _effective_sync_backref(self) -> bool:
  1306. if self.viewonly:
  1307. return False
  1308. else:
  1309. return self.sync_backref is not False
  1310. @staticmethod
  1311. def _check_sync_backref(
  1312. rel_a: RelationshipProperty[Any], rel_b: RelationshipProperty[Any]
  1313. ) -> None:
  1314. if rel_a.viewonly and rel_b.sync_backref:
  1315. raise sa_exc.InvalidRequestError(
  1316. "Relationship %s cannot specify sync_backref=True since %s "
  1317. "includes viewonly=True." % (rel_b, rel_a)
  1318. )
  1319. if (
  1320. rel_a.viewonly
  1321. and not rel_b.viewonly
  1322. and rel_b.sync_backref is not False
  1323. ):
  1324. rel_b.sync_backref = False
  1325. def _add_reverse_property(self, key: str) -> None:
  1326. other = self.mapper.get_property(key, _configure_mappers=False)
  1327. if not isinstance(other, RelationshipProperty):
  1328. raise sa_exc.InvalidRequestError(
  1329. "back_populates on relationship '%s' refers to attribute '%s' "
  1330. "that is not a relationship. The back_populates parameter "
  1331. "should refer to the name of a relationship on the target "
  1332. "class." % (self, other)
  1333. )
  1334. # viewonly and sync_backref cases
  1335. # 1. self.viewonly==True and other.sync_backref==True -> error
  1336. # 2. self.viewonly==True and other.viewonly==False and
  1337. # other.sync_backref==None -> warn sync_backref=False, set to False
  1338. self._check_sync_backref(self, other)
  1339. # 3. other.viewonly==True and self.sync_backref==True -> error
  1340. # 4. other.viewonly==True and self.viewonly==False and
  1341. # self.sync_backref==None -> warn sync_backref=False, set to False
  1342. self._check_sync_backref(other, self)
  1343. self._reverse_property.add(other)
  1344. other._reverse_property.add(self)
  1345. other._setup_entity()
  1346. if not other.mapper.common_parent(self.parent):
  1347. raise sa_exc.ArgumentError(
  1348. "reverse_property %r on "
  1349. "relationship %s references relationship %s, which "
  1350. "does not reference mapper %s"
  1351. % (key, self, other, self.parent)
  1352. )
  1353. if (
  1354. other._configure_started
  1355. and self.direction in (ONETOMANY, MANYTOONE)
  1356. and self.direction == other.direction
  1357. ):
  1358. raise sa_exc.ArgumentError(
  1359. "%s and back-reference %s are "
  1360. "both of the same direction %r. Did you mean to "
  1361. "set remote_side on the many-to-one side ?"
  1362. % (other, self, self.direction)
  1363. )
  1364. @util.memoized_property
  1365. def entity(self) -> _InternalEntityType[_T]:
  1366. """Return the target mapped entity, which is an inspect() of the
  1367. class or aliased class that is referenced by this
  1368. :class:`.RelationshipProperty`.
  1369. """
  1370. self.parent._check_configure()
  1371. return self.entity
  1372. @util.memoized_property
  1373. def mapper(self) -> Mapper[_T]:
  1374. """Return the targeted :class:`_orm.Mapper` for this
  1375. :class:`.RelationshipProperty`.
  1376. """
  1377. return self.entity.mapper
  1378. def do_init(self) -> None:
  1379. self._check_conflicts()
  1380. self._process_dependent_arguments()
  1381. self._setup_entity()
  1382. self._setup_registry_dependencies()
  1383. self._setup_join_conditions()
  1384. self._check_cascade_settings(self._cascade)
  1385. self._post_init()
  1386. self._generate_backref()
  1387. self._join_condition._warn_for_conflicting_sync_targets()
  1388. super().do_init()
  1389. self._lazy_strategy = cast(
  1390. "LazyLoader", self._get_strategy((("lazy", "select"),))
  1391. )
  1392. def _setup_registry_dependencies(self) -> None:
  1393. self.parent.mapper.registry._set_depends_on(
  1394. self.entity.mapper.registry
  1395. )
  1396. def _process_dependent_arguments(self) -> None:
  1397. """Convert incoming configuration arguments to their
  1398. proper form.
  1399. Callables are resolved, ORM annotations removed.
  1400. """
  1401. # accept callables for other attributes which may require
  1402. # deferred initialization. This technique is used
  1403. # by declarative "string configs" and some recipes.
  1404. init_args = self._init_args
  1405. for attr in (
  1406. "order_by",
  1407. "primaryjoin",
  1408. "secondaryjoin",
  1409. "secondary",
  1410. "foreign_keys",
  1411. "remote_side",
  1412. ):
  1413. rel_arg = getattr(init_args, attr)
  1414. rel_arg._resolve_against_registry(self._clsregistry_resolvers[1])
  1415. # remove "annotations" which are present if mapped class
  1416. # descriptors are used to create the join expression.
  1417. for attr in "primaryjoin", "secondaryjoin":
  1418. rel_arg = getattr(init_args, attr)
  1419. val = rel_arg.resolved
  1420. if val is not None:
  1421. rel_arg.resolved = _orm_deannotate(
  1422. coercions.expect(
  1423. roles.ColumnArgumentRole, val, argname=attr
  1424. )
  1425. )
  1426. secondary = init_args.secondary.resolved
  1427. if secondary is not None and _is_mapped_class(secondary):
  1428. raise sa_exc.ArgumentError(
  1429. "secondary argument %s passed to to relationship() %s must "
  1430. "be a Table object or other FROM clause; can't send a mapped "
  1431. "class directly as rows in 'secondary' are persisted "
  1432. "independently of a class that is mapped "
  1433. "to that same table." % (secondary, self)
  1434. )
  1435. # ensure expressions in self.order_by, foreign_keys,
  1436. # remote_side are all columns, not strings.
  1437. if (
  1438. init_args.order_by.resolved is not False
  1439. and init_args.order_by.resolved is not None
  1440. ):
  1441. self.order_by = tuple(
  1442. coercions.expect(
  1443. roles.ColumnArgumentRole, x, argname="order_by"
  1444. )
  1445. for x in util.to_list(init_args.order_by.resolved)
  1446. )
  1447. else:
  1448. self.order_by = False
  1449. self._user_defined_foreign_keys = util.column_set(
  1450. coercions.expect(
  1451. roles.ColumnArgumentRole, x, argname="foreign_keys"
  1452. )
  1453. for x in util.to_column_set(init_args.foreign_keys.resolved)
  1454. )
  1455. self.remote_side = util.column_set(
  1456. coercions.expect(
  1457. roles.ColumnArgumentRole, x, argname="remote_side"
  1458. )
  1459. for x in util.to_column_set(init_args.remote_side.resolved)
  1460. )
  1461. def declarative_scan(
  1462. self,
  1463. decl_scan: _ClassScanMapperConfig,
  1464. registry: _RegistryType,
  1465. cls: Type[Any],
  1466. originating_module: Optional[str],
  1467. key: str,
  1468. mapped_container: Optional[Type[Mapped[Any]]],
  1469. annotation: Optional[_AnnotationScanType],
  1470. extracted_mapped_annotation: Optional[_AnnotationScanType],
  1471. is_dataclass_field: bool,
  1472. ) -> None:
  1473. if extracted_mapped_annotation is None:
  1474. if self.argument is None:
  1475. self._raise_for_required(key, cls)
  1476. else:
  1477. return
  1478. argument = extracted_mapped_annotation
  1479. assert originating_module is not None
  1480. if mapped_container is not None:
  1481. is_write_only = issubclass(mapped_container, WriteOnlyMapped)
  1482. is_dynamic = issubclass(mapped_container, DynamicMapped)
  1483. if is_write_only:
  1484. self.lazy = "write_only"
  1485. self.strategy_key = (("lazy", self.lazy),)
  1486. elif is_dynamic:
  1487. self.lazy = "dynamic"
  1488. self.strategy_key = (("lazy", self.lazy),)
  1489. else:
  1490. is_write_only = is_dynamic = False
  1491. argument = de_optionalize_union_types(argument)
  1492. if hasattr(argument, "__origin__"):
  1493. arg_origin = argument.__origin__
  1494. if isinstance(arg_origin, type) and issubclass(
  1495. arg_origin, abc.Collection
  1496. ):
  1497. if self.collection_class is None:
  1498. if _py_inspect.isabstract(arg_origin):
  1499. raise sa_exc.ArgumentError(
  1500. f"Collection annotation type {arg_origin} cannot "
  1501. "be instantiated; please provide an explicit "
  1502. "'collection_class' parameter "
  1503. "(e.g. list, set, etc.) to the "
  1504. "relationship() function to accompany this "
  1505. "annotation"
  1506. )
  1507. self.collection_class = arg_origin
  1508. elif not is_write_only and not is_dynamic:
  1509. self.uselist = False
  1510. if argument.__args__: # type: ignore
  1511. if isinstance(arg_origin, type) and issubclass(
  1512. arg_origin, typing.Mapping
  1513. ):
  1514. type_arg = argument.__args__[-1] # type: ignore
  1515. else:
  1516. type_arg = argument.__args__[0] # type: ignore
  1517. if hasattr(type_arg, "__forward_arg__"):
  1518. str_argument = type_arg.__forward_arg__
  1519. argument = resolve_name_to_real_class_name(
  1520. str_argument, originating_module
  1521. )
  1522. else:
  1523. argument = type_arg
  1524. else:
  1525. raise sa_exc.ArgumentError(
  1526. f"Generic alias {argument} requires an argument"
  1527. )
  1528. elif hasattr(argument, "__forward_arg__"):
  1529. argument = argument.__forward_arg__
  1530. argument = resolve_name_to_real_class_name(
  1531. argument, originating_module
  1532. )
  1533. if (
  1534. self.collection_class is None
  1535. and not is_write_only
  1536. and not is_dynamic
  1537. ):
  1538. self.uselist = False
  1539. # ticket #8759
  1540. # if a lead argument was given to relationship(), like
  1541. # `relationship("B")`, use that, don't replace it with class we
  1542. # found in the annotation. The declarative_scan() method call here is
  1543. # still useful, as we continue to derive collection type and do
  1544. # checking of the annotation in any case.
  1545. if self.argument is None:
  1546. self.argument = cast("_RelationshipArgumentType[_T]", argument)
  1547. @util.preload_module("sqlalchemy.orm.mapper")
  1548. def _setup_entity(self, __argument: Any = None) -> None:
  1549. if "entity" in self.__dict__:
  1550. return
  1551. mapperlib = util.preloaded.orm_mapper
  1552. if __argument:
  1553. argument = __argument
  1554. else:
  1555. argument = self.argument
  1556. resolved_argument: _ExternalEntityType[Any]
  1557. if isinstance(argument, str):
  1558. # we might want to cleanup clsregistry API to make this
  1559. # more straightforward
  1560. resolved_argument = cast(
  1561. "_ExternalEntityType[Any]",
  1562. self._clsregistry_resolve_name(argument)(),
  1563. )
  1564. elif callable(argument) and not isinstance(
  1565. argument, (type, mapperlib.Mapper)
  1566. ):
  1567. resolved_argument = argument()
  1568. else:
  1569. resolved_argument = argument
  1570. entity: _InternalEntityType[Any]
  1571. if isinstance(resolved_argument, type):
  1572. entity = class_mapper(resolved_argument, configure=False)
  1573. else:
  1574. try:
  1575. entity = inspect(resolved_argument)
  1576. except sa_exc.NoInspectionAvailable:
  1577. entity = None # type: ignore
  1578. if not hasattr(entity, "mapper"):
  1579. raise sa_exc.ArgumentError(
  1580. "relationship '%s' expects "
  1581. "a class or a mapper argument (received: %s)"
  1582. % (self.key, type(resolved_argument))
  1583. )
  1584. self.entity = entity
  1585. self.target = self.entity.persist_selectable
  1586. def _setup_join_conditions(self) -> None:
  1587. self._join_condition = jc = JoinCondition(
  1588. parent_persist_selectable=self.parent.persist_selectable,
  1589. child_persist_selectable=self.entity.persist_selectable,
  1590. parent_local_selectable=self.parent.local_table,
  1591. child_local_selectable=self.entity.local_table,
  1592. primaryjoin=self._init_args.primaryjoin.resolved,
  1593. secondary=self._init_args.secondary.resolved,
  1594. secondaryjoin=self._init_args.secondaryjoin.resolved,
  1595. parent_equivalents=self.parent._equivalent_columns,
  1596. child_equivalents=self.mapper._equivalent_columns,
  1597. consider_as_foreign_keys=self._user_defined_foreign_keys,
  1598. local_remote_pairs=self.local_remote_pairs,
  1599. remote_side=self.remote_side,
  1600. self_referential=self._is_self_referential,
  1601. prop=self,
  1602. support_sync=not self.viewonly,
  1603. can_be_synced_fn=self._columns_are_mapped,
  1604. )
  1605. self.primaryjoin = jc.primaryjoin
  1606. self.secondaryjoin = jc.secondaryjoin
  1607. self.secondary = jc.secondary
  1608. self.direction = jc.direction
  1609. self.local_remote_pairs = jc.local_remote_pairs
  1610. self.remote_side = jc.remote_columns
  1611. self.local_columns = jc.local_columns
  1612. self.synchronize_pairs = jc.synchronize_pairs
  1613. self._calculated_foreign_keys = jc.foreign_key_columns
  1614. self.secondary_synchronize_pairs = jc.secondary_synchronize_pairs
  1615. @property
  1616. def _clsregistry_resolve_arg(
  1617. self,
  1618. ) -> Callable[[str, bool], _class_resolver]:
  1619. return self._clsregistry_resolvers[1]
  1620. @property
  1621. def _clsregistry_resolve_name(
  1622. self,
  1623. ) -> Callable[[str], Callable[[], Union[Type[Any], Table, _ModNS]]]:
  1624. return self._clsregistry_resolvers[0]
  1625. @util.memoized_property
  1626. @util.preload_module("sqlalchemy.orm.clsregistry")
  1627. def _clsregistry_resolvers(
  1628. self,
  1629. ) -> Tuple[
  1630. Callable[[str], Callable[[], Union[Type[Any], Table, _ModNS]]],
  1631. Callable[[str, bool], _class_resolver],
  1632. ]:
  1633. _resolver = util.preloaded.orm_clsregistry._resolver
  1634. return _resolver(self.parent.class_, self)
  1635. def _check_conflicts(self) -> None:
  1636. """Test that this relationship is legal, warn about
  1637. inheritance conflicts."""
  1638. if self.parent.non_primary and not class_mapper(
  1639. self.parent.class_, configure=False
  1640. ).has_property(self.key):
  1641. raise sa_exc.ArgumentError(
  1642. "Attempting to assign a new "
  1643. "relationship '%s' to a non-primary mapper on "
  1644. "class '%s'. New relationships can only be added "
  1645. "to the primary mapper, i.e. the very first mapper "
  1646. "created for class '%s' "
  1647. % (
  1648. self.key,
  1649. self.parent.class_.__name__,
  1650. self.parent.class_.__name__,
  1651. )
  1652. )
  1653. @property
  1654. def cascade(self) -> CascadeOptions:
  1655. """Return the current cascade setting for this
  1656. :class:`.RelationshipProperty`.
  1657. """
  1658. return self._cascade
  1659. @cascade.setter
  1660. def cascade(self, cascade: Union[str, CascadeOptions]) -> None:
  1661. self._set_cascade(cascade)
  1662. def _set_cascade(self, cascade_arg: Union[str, CascadeOptions]) -> None:
  1663. cascade = CascadeOptions(cascade_arg)
  1664. if self.viewonly:
  1665. cascade = CascadeOptions(
  1666. cascade.intersection(CascadeOptions._viewonly_cascades)
  1667. )
  1668. if "mapper" in self.__dict__:
  1669. self._check_cascade_settings(cascade)
  1670. self._cascade = cascade
  1671. if self._dependency_processor:
  1672. self._dependency_processor.cascade = cascade
  1673. def _check_cascade_settings(self, cascade: CascadeOptions) -> None:
  1674. if (
  1675. cascade.delete_orphan
  1676. and not self.single_parent
  1677. and (self.direction is MANYTOMANY or self.direction is MANYTOONE)
  1678. ):
  1679. raise sa_exc.ArgumentError(
  1680. "For %(direction)s relationship %(rel)s, delete-orphan "
  1681. "cascade is normally "
  1682. 'configured only on the "one" side of a one-to-many '
  1683. "relationship, "
  1684. 'and not on the "many" side of a many-to-one or many-to-many '
  1685. "relationship. "
  1686. "To force this relationship to allow a particular "
  1687. '"%(relatedcls)s" object to be referenced by only '
  1688. 'a single "%(clsname)s" object at a time via the '
  1689. "%(rel)s relationship, which "
  1690. "would allow "
  1691. "delete-orphan cascade to take place in this direction, set "
  1692. "the single_parent=True flag."
  1693. % {
  1694. "rel": self,
  1695. "direction": (
  1696. "many-to-one"
  1697. if self.direction is MANYTOONE
  1698. else "many-to-many"
  1699. ),
  1700. "clsname": self.parent.class_.__name__,
  1701. "relatedcls": self.mapper.class_.__name__,
  1702. },
  1703. code="bbf0",
  1704. )
  1705. if self.passive_deletes == "all" and (
  1706. "delete" in cascade or "delete-orphan" in cascade
  1707. ):
  1708. raise sa_exc.ArgumentError(
  1709. "On %s, can't set passive_deletes='all' in conjunction "
  1710. "with 'delete' or 'delete-orphan' cascade" % self
  1711. )
  1712. if cascade.delete_orphan:
  1713. self.mapper.primary_mapper()._delete_orphans.append(
  1714. (self.key, self.parent.class_)
  1715. )
  1716. def _persists_for(self, mapper: Mapper[Any]) -> bool:
  1717. """Return True if this property will persist values on behalf
  1718. of the given mapper.
  1719. """
  1720. return (
  1721. self.key in mapper.relationships
  1722. and mapper.relationships[self.key] is self
  1723. )
  1724. def _columns_are_mapped(self, *cols: ColumnElement[Any]) -> bool:
  1725. """Return True if all columns in the given collection are
  1726. mapped by the tables referenced by this :class:`.RelationshipProperty`.
  1727. """
  1728. secondary = self._init_args.secondary.resolved
  1729. for c in cols:
  1730. if secondary is not None and secondary.c.contains_column(c):
  1731. continue
  1732. if not self.parent.persist_selectable.c.contains_column(
  1733. c
  1734. ) and not self.target.c.contains_column(c):
  1735. return False
  1736. return True
  1737. def _generate_backref(self) -> None:
  1738. """Interpret the 'backref' instruction to create a
  1739. :func:`_orm.relationship` complementary to this one."""
  1740. if self.parent.non_primary:
  1741. return
  1742. if self.backref is not None and not self.back_populates:
  1743. kwargs: Dict[str, Any]
  1744. if isinstance(self.backref, str):
  1745. backref_key, kwargs = self.backref, {}
  1746. else:
  1747. backref_key, kwargs = self.backref
  1748. mapper = self.mapper.primary_mapper()
  1749. if not mapper.concrete:
  1750. check = set(mapper.iterate_to_root()).union(
  1751. mapper.self_and_descendants
  1752. )
  1753. for m in check:
  1754. if m.has_property(backref_key) and not m.concrete:
  1755. raise sa_exc.ArgumentError(
  1756. "Error creating backref "
  1757. "'%s' on relationship '%s': property of that "
  1758. "name exists on mapper '%s'"
  1759. % (backref_key, self, m)
  1760. )
  1761. # determine primaryjoin/secondaryjoin for the
  1762. # backref. Use the one we had, so that
  1763. # a custom join doesn't have to be specified in
  1764. # both directions.
  1765. if self.secondary is not None:
  1766. # for many to many, just switch primaryjoin/
  1767. # secondaryjoin. use the annotated
  1768. # pj/sj on the _join_condition.
  1769. pj = kwargs.pop(
  1770. "primaryjoin",
  1771. self._join_condition.secondaryjoin_minus_local,
  1772. )
  1773. sj = kwargs.pop(
  1774. "secondaryjoin",
  1775. self._join_condition.primaryjoin_minus_local,
  1776. )
  1777. else:
  1778. pj = kwargs.pop(
  1779. "primaryjoin",
  1780. self._join_condition.primaryjoin_reverse_remote,
  1781. )
  1782. sj = kwargs.pop("secondaryjoin", None)
  1783. if sj:
  1784. raise sa_exc.InvalidRequestError(
  1785. "Can't assign 'secondaryjoin' on a backref "
  1786. "against a non-secondary relationship."
  1787. )
  1788. foreign_keys = kwargs.pop(
  1789. "foreign_keys", self._user_defined_foreign_keys
  1790. )
  1791. parent = self.parent.primary_mapper()
  1792. kwargs.setdefault("viewonly", self.viewonly)
  1793. kwargs.setdefault("post_update", self.post_update)
  1794. kwargs.setdefault("passive_updates", self.passive_updates)
  1795. kwargs.setdefault("sync_backref", self.sync_backref)
  1796. self.back_populates = backref_key
  1797. relationship = RelationshipProperty(
  1798. parent,
  1799. self.secondary,
  1800. primaryjoin=pj,
  1801. secondaryjoin=sj,
  1802. foreign_keys=foreign_keys,
  1803. back_populates=self.key,
  1804. **kwargs,
  1805. )
  1806. mapper._configure_property(
  1807. backref_key, relationship, warn_for_existing=True
  1808. )
  1809. if self.back_populates:
  1810. self._add_reverse_property(self.back_populates)
  1811. @util.preload_module("sqlalchemy.orm.dependency")
  1812. def _post_init(self) -> None:
  1813. dependency = util.preloaded.orm_dependency
  1814. if self.uselist is None:
  1815. self.uselist = self.direction is not MANYTOONE
  1816. if not self.viewonly:
  1817. self._dependency_processor = ( # type: ignore
  1818. dependency.DependencyProcessor.from_relationship
  1819. )(self)
  1820. @util.memoized_property
  1821. def _use_get(self) -> bool:
  1822. """memoize the 'use_get' attribute of this RelationshipLoader's
  1823. lazyloader."""
  1824. strategy = self._lazy_strategy
  1825. return strategy.use_get
  1826. @util.memoized_property
  1827. def _is_self_referential(self) -> bool:
  1828. return self.mapper.common_parent(self.parent)
  1829. def _create_joins(
  1830. self,
  1831. source_polymorphic: bool = False,
  1832. source_selectable: Optional[FromClause] = None,
  1833. dest_selectable: Optional[FromClause] = None,
  1834. of_type_entity: Optional[_InternalEntityType[Any]] = None,
  1835. alias_secondary: bool = False,
  1836. extra_criteria: Tuple[ColumnElement[bool], ...] = (),
  1837. ) -> Tuple[
  1838. ColumnElement[bool],
  1839. Optional[ColumnElement[bool]],
  1840. FromClause,
  1841. FromClause,
  1842. Optional[FromClause],
  1843. Optional[ClauseAdapter],
  1844. ]:
  1845. aliased = False
  1846. if alias_secondary and self.secondary is not None:
  1847. aliased = True
  1848. if source_selectable is None:
  1849. if source_polymorphic and self.parent.with_polymorphic:
  1850. source_selectable = self.parent._with_polymorphic_selectable
  1851. if of_type_entity:
  1852. dest_mapper = of_type_entity.mapper
  1853. if dest_selectable is None:
  1854. dest_selectable = of_type_entity.selectable
  1855. aliased = True
  1856. else:
  1857. dest_mapper = self.mapper
  1858. if dest_selectable is None:
  1859. dest_selectable = self.entity.selectable
  1860. if self.mapper.with_polymorphic:
  1861. aliased = True
  1862. if self._is_self_referential and source_selectable is None:
  1863. dest_selectable = dest_selectable._anonymous_fromclause()
  1864. aliased = True
  1865. elif (
  1866. dest_selectable is not self.mapper._with_polymorphic_selectable
  1867. or self.mapper.with_polymorphic
  1868. ):
  1869. aliased = True
  1870. single_crit = dest_mapper._single_table_criterion
  1871. aliased = aliased or (
  1872. source_selectable is not None
  1873. and (
  1874. source_selectable
  1875. is not self.parent._with_polymorphic_selectable
  1876. or source_selectable._is_subquery
  1877. )
  1878. )
  1879. (
  1880. primaryjoin,
  1881. secondaryjoin,
  1882. secondary,
  1883. target_adapter,
  1884. dest_selectable,
  1885. ) = self._join_condition.join_targets(
  1886. source_selectable,
  1887. dest_selectable,
  1888. aliased,
  1889. single_crit,
  1890. extra_criteria,
  1891. )
  1892. if source_selectable is None:
  1893. source_selectable = self.parent.local_table
  1894. if dest_selectable is None:
  1895. dest_selectable = self.entity.local_table
  1896. return (
  1897. primaryjoin,
  1898. secondaryjoin,
  1899. source_selectable,
  1900. dest_selectable,
  1901. secondary,
  1902. target_adapter,
  1903. )
  1904. def _annotate_columns(element: _CE, annotations: _AnnotationDict) -> _CE:
  1905. def clone(elem: _CE) -> _CE:
  1906. if isinstance(elem, expression.ColumnClause):
  1907. elem = elem._annotate(annotations.copy()) # type: ignore
  1908. elem._copy_internals(clone=clone)
  1909. return elem
  1910. if element is not None:
  1911. element = clone(element)
  1912. clone = None # type: ignore # remove gc cycles
  1913. return element
  1914. class JoinCondition:
  1915. primaryjoin_initial: Optional[ColumnElement[bool]]
  1916. primaryjoin: ColumnElement[bool]
  1917. secondaryjoin: Optional[ColumnElement[bool]]
  1918. secondary: Optional[FromClause]
  1919. prop: RelationshipProperty[Any]
  1920. synchronize_pairs: _ColumnPairs
  1921. secondary_synchronize_pairs: _ColumnPairs
  1922. direction: RelationshipDirection
  1923. parent_persist_selectable: FromClause
  1924. child_persist_selectable: FromClause
  1925. parent_local_selectable: FromClause
  1926. child_local_selectable: FromClause
  1927. _local_remote_pairs: Optional[_ColumnPairs]
  1928. def __init__(
  1929. self,
  1930. parent_persist_selectable: FromClause,
  1931. child_persist_selectable: FromClause,
  1932. parent_local_selectable: FromClause,
  1933. child_local_selectable: FromClause,
  1934. *,
  1935. primaryjoin: Optional[ColumnElement[bool]] = None,
  1936. secondary: Optional[FromClause] = None,
  1937. secondaryjoin: Optional[ColumnElement[bool]] = None,
  1938. parent_equivalents: Optional[_EquivalentColumnMap] = None,
  1939. child_equivalents: Optional[_EquivalentColumnMap] = None,
  1940. consider_as_foreign_keys: Any = None,
  1941. local_remote_pairs: Optional[_ColumnPairs] = None,
  1942. remote_side: Any = None,
  1943. self_referential: Any = False,
  1944. prop: RelationshipProperty[Any],
  1945. support_sync: bool = True,
  1946. can_be_synced_fn: Callable[..., bool] = lambda *c: True,
  1947. ):
  1948. self.parent_persist_selectable = parent_persist_selectable
  1949. self.parent_local_selectable = parent_local_selectable
  1950. self.child_persist_selectable = child_persist_selectable
  1951. self.child_local_selectable = child_local_selectable
  1952. self.parent_equivalents = parent_equivalents
  1953. self.child_equivalents = child_equivalents
  1954. self.primaryjoin_initial = primaryjoin
  1955. self.secondaryjoin = secondaryjoin
  1956. self.secondary = secondary
  1957. self.consider_as_foreign_keys = consider_as_foreign_keys
  1958. self._local_remote_pairs = local_remote_pairs
  1959. self._remote_side = remote_side
  1960. self.prop = prop
  1961. self.self_referential = self_referential
  1962. self.support_sync = support_sync
  1963. self.can_be_synced_fn = can_be_synced_fn
  1964. self._determine_joins()
  1965. assert self.primaryjoin is not None
  1966. self._sanitize_joins()
  1967. self._annotate_fks()
  1968. self._annotate_remote()
  1969. self._annotate_local()
  1970. self._annotate_parentmapper()
  1971. self._setup_pairs()
  1972. self._check_foreign_cols(self.primaryjoin, True)
  1973. if self.secondaryjoin is not None:
  1974. self._check_foreign_cols(self.secondaryjoin, False)
  1975. self._determine_direction()
  1976. self._check_remote_side()
  1977. self._log_joins()
  1978. def _log_joins(self) -> None:
  1979. log = self.prop.logger
  1980. log.info("%s setup primary join %s", self.prop, self.primaryjoin)
  1981. log.info("%s setup secondary join %s", self.prop, self.secondaryjoin)
  1982. log.info(
  1983. "%s synchronize pairs [%s]",
  1984. self.prop,
  1985. ",".join(
  1986. "(%s => %s)" % (l, r) for (l, r) in self.synchronize_pairs
  1987. ),
  1988. )
  1989. log.info(
  1990. "%s secondary synchronize pairs [%s]",
  1991. self.prop,
  1992. ",".join(
  1993. "(%s => %s)" % (l, r)
  1994. for (l, r) in self.secondary_synchronize_pairs or []
  1995. ),
  1996. )
  1997. log.info(
  1998. "%s local/remote pairs [%s]",
  1999. self.prop,
  2000. ",".join(
  2001. "(%s / %s)" % (l, r) for (l, r) in self.local_remote_pairs
  2002. ),
  2003. )
  2004. log.info(
  2005. "%s remote columns [%s]",
  2006. self.prop,
  2007. ",".join("%s" % col for col in self.remote_columns),
  2008. )
  2009. log.info(
  2010. "%s local columns [%s]",
  2011. self.prop,
  2012. ",".join("%s" % col for col in self.local_columns),
  2013. )
  2014. log.info("%s relationship direction %s", self.prop, self.direction)
  2015. def _sanitize_joins(self) -> None:
  2016. """remove the parententity annotation from our join conditions which
  2017. can leak in here based on some declarative patterns and maybe others.
  2018. "parentmapper" is relied upon both by the ORM evaluator as well as
  2019. the use case in _join_fixture_inh_selfref_w_entity
  2020. that relies upon it being present, see :ticket:`3364`.
  2021. """
  2022. self.primaryjoin = _deep_deannotate(
  2023. self.primaryjoin, values=("parententity", "proxy_key")
  2024. )
  2025. if self.secondaryjoin is not None:
  2026. self.secondaryjoin = _deep_deannotate(
  2027. self.secondaryjoin, values=("parententity", "proxy_key")
  2028. )
  2029. def _determine_joins(self) -> None:
  2030. """Determine the 'primaryjoin' and 'secondaryjoin' attributes,
  2031. if not passed to the constructor already.
  2032. This is based on analysis of the foreign key relationships
  2033. between the parent and target mapped selectables.
  2034. """
  2035. if self.secondaryjoin is not None and self.secondary is None:
  2036. raise sa_exc.ArgumentError(
  2037. "Property %s specified with secondary "
  2038. "join condition but "
  2039. "no secondary argument" % self.prop
  2040. )
  2041. # find a join between the given mapper's mapped table and
  2042. # the given table. will try the mapper's local table first
  2043. # for more specificity, then if not found will try the more
  2044. # general mapped table, which in the case of inheritance is
  2045. # a join.
  2046. try:
  2047. consider_as_foreign_keys = self.consider_as_foreign_keys or None
  2048. if self.secondary is not None:
  2049. if self.secondaryjoin is None:
  2050. self.secondaryjoin = join_condition(
  2051. self.child_persist_selectable,
  2052. self.secondary,
  2053. a_subset=self.child_local_selectable,
  2054. consider_as_foreign_keys=consider_as_foreign_keys,
  2055. )
  2056. if self.primaryjoin_initial is None:
  2057. self.primaryjoin = join_condition(
  2058. self.parent_persist_selectable,
  2059. self.secondary,
  2060. a_subset=self.parent_local_selectable,
  2061. consider_as_foreign_keys=consider_as_foreign_keys,
  2062. )
  2063. else:
  2064. self.primaryjoin = self.primaryjoin_initial
  2065. else:
  2066. if self.primaryjoin_initial is None:
  2067. self.primaryjoin = join_condition(
  2068. self.parent_persist_selectable,
  2069. self.child_persist_selectable,
  2070. a_subset=self.parent_local_selectable,
  2071. consider_as_foreign_keys=consider_as_foreign_keys,
  2072. )
  2073. else:
  2074. self.primaryjoin = self.primaryjoin_initial
  2075. except sa_exc.NoForeignKeysError as nfe:
  2076. if self.secondary is not None:
  2077. raise sa_exc.NoForeignKeysError(
  2078. "Could not determine join "
  2079. "condition between parent/child tables on "
  2080. "relationship %s - there are no foreign keys "
  2081. "linking these tables via secondary table '%s'. "
  2082. "Ensure that referencing columns are associated "
  2083. "with a ForeignKey or ForeignKeyConstraint, or "
  2084. "specify 'primaryjoin' and 'secondaryjoin' "
  2085. "expressions." % (self.prop, self.secondary)
  2086. ) from nfe
  2087. else:
  2088. raise sa_exc.NoForeignKeysError(
  2089. "Could not determine join "
  2090. "condition between parent/child tables on "
  2091. "relationship %s - there are no foreign keys "
  2092. "linking these tables. "
  2093. "Ensure that referencing columns are associated "
  2094. "with a ForeignKey or ForeignKeyConstraint, or "
  2095. "specify a 'primaryjoin' expression." % self.prop
  2096. ) from nfe
  2097. except sa_exc.AmbiguousForeignKeysError as afe:
  2098. if self.secondary is not None:
  2099. raise sa_exc.AmbiguousForeignKeysError(
  2100. "Could not determine join "
  2101. "condition between parent/child tables on "
  2102. "relationship %s - there are multiple foreign key "
  2103. "paths linking the tables via secondary table '%s'. "
  2104. "Specify the 'foreign_keys' "
  2105. "argument, providing a list of those columns which "
  2106. "should be counted as containing a foreign key "
  2107. "reference from the secondary table to each of the "
  2108. "parent and child tables." % (self.prop, self.secondary)
  2109. ) from afe
  2110. else:
  2111. raise sa_exc.AmbiguousForeignKeysError(
  2112. "Could not determine join "
  2113. "condition between parent/child tables on "
  2114. "relationship %s - there are multiple foreign key "
  2115. "paths linking the tables. Specify the "
  2116. "'foreign_keys' argument, providing a list of those "
  2117. "columns which should be counted as containing a "
  2118. "foreign key reference to the parent table." % self.prop
  2119. ) from afe
  2120. @property
  2121. def primaryjoin_minus_local(self) -> ColumnElement[bool]:
  2122. return _deep_deannotate(self.primaryjoin, values=("local", "remote"))
  2123. @property
  2124. def secondaryjoin_minus_local(self) -> ColumnElement[bool]:
  2125. assert self.secondaryjoin is not None
  2126. return _deep_deannotate(self.secondaryjoin, values=("local", "remote"))
  2127. @util.memoized_property
  2128. def primaryjoin_reverse_remote(self) -> ColumnElement[bool]:
  2129. """Return the primaryjoin condition suitable for the
  2130. "reverse" direction.
  2131. If the primaryjoin was delivered here with pre-existing
  2132. "remote" annotations, the local/remote annotations
  2133. are reversed. Otherwise, the local/remote annotations
  2134. are removed.
  2135. """
  2136. if self._has_remote_annotations:
  2137. def replace(element: _CE, **kw: Any) -> Optional[_CE]:
  2138. if "remote" in element._annotations:
  2139. v = dict(element._annotations)
  2140. del v["remote"]
  2141. v["local"] = True
  2142. return element._with_annotations(v)
  2143. elif "local" in element._annotations:
  2144. v = dict(element._annotations)
  2145. del v["local"]
  2146. v["remote"] = True
  2147. return element._with_annotations(v)
  2148. return None
  2149. return visitors.replacement_traverse(self.primaryjoin, {}, replace)
  2150. else:
  2151. if self._has_foreign_annotations:
  2152. # TODO: coverage
  2153. return _deep_deannotate(
  2154. self.primaryjoin, values=("local", "remote")
  2155. )
  2156. else:
  2157. return _deep_deannotate(self.primaryjoin)
  2158. def _has_annotation(self, clause: ClauseElement, annotation: str) -> bool:
  2159. for col in visitors.iterate(clause, {}):
  2160. if annotation in col._annotations:
  2161. return True
  2162. else:
  2163. return False
  2164. @util.memoized_property
  2165. def _has_foreign_annotations(self) -> bool:
  2166. return self._has_annotation(self.primaryjoin, "foreign")
  2167. @util.memoized_property
  2168. def _has_remote_annotations(self) -> bool:
  2169. return self._has_annotation(self.primaryjoin, "remote")
  2170. def _annotate_fks(self) -> None:
  2171. """Annotate the primaryjoin and secondaryjoin
  2172. structures with 'foreign' annotations marking columns
  2173. considered as foreign.
  2174. """
  2175. if self._has_foreign_annotations:
  2176. return
  2177. if self.consider_as_foreign_keys:
  2178. self._annotate_from_fk_list()
  2179. else:
  2180. self._annotate_present_fks()
  2181. def _annotate_from_fk_list(self) -> None:
  2182. def check_fk(element: _CE, **kw: Any) -> Optional[_CE]:
  2183. if element in self.consider_as_foreign_keys:
  2184. return element._annotate({"foreign": True})
  2185. return None
  2186. self.primaryjoin = visitors.replacement_traverse(
  2187. self.primaryjoin, {}, check_fk
  2188. )
  2189. if self.secondaryjoin is not None:
  2190. self.secondaryjoin = visitors.replacement_traverse(
  2191. self.secondaryjoin, {}, check_fk
  2192. )
  2193. def _annotate_present_fks(self) -> None:
  2194. if self.secondary is not None:
  2195. secondarycols = util.column_set(self.secondary.c)
  2196. else:
  2197. secondarycols = set()
  2198. def is_foreign(
  2199. a: ColumnElement[Any], b: ColumnElement[Any]
  2200. ) -> Optional[ColumnElement[Any]]:
  2201. if isinstance(a, schema.Column) and isinstance(b, schema.Column):
  2202. if a.references(b):
  2203. return a
  2204. elif b.references(a):
  2205. return b
  2206. if secondarycols:
  2207. if a in secondarycols and b not in secondarycols:
  2208. return a
  2209. elif b in secondarycols and a not in secondarycols:
  2210. return b
  2211. return None
  2212. def visit_binary(binary: BinaryExpression[Any]) -> None:
  2213. if not isinstance(
  2214. binary.left, sql.ColumnElement
  2215. ) or not isinstance(binary.right, sql.ColumnElement):
  2216. return
  2217. if (
  2218. "foreign" not in binary.left._annotations
  2219. and "foreign" not in binary.right._annotations
  2220. ):
  2221. col = is_foreign(binary.left, binary.right)
  2222. if col is not None:
  2223. if col.compare(binary.left):
  2224. binary.left = binary.left._annotate({"foreign": True})
  2225. elif col.compare(binary.right):
  2226. binary.right = binary.right._annotate(
  2227. {"foreign": True}
  2228. )
  2229. self.primaryjoin = visitors.cloned_traverse(
  2230. self.primaryjoin, {}, {"binary": visit_binary}
  2231. )
  2232. if self.secondaryjoin is not None:
  2233. self.secondaryjoin = visitors.cloned_traverse(
  2234. self.secondaryjoin, {}, {"binary": visit_binary}
  2235. )
  2236. def _refers_to_parent_table(self) -> bool:
  2237. """Return True if the join condition contains column
  2238. comparisons where both columns are in both tables.
  2239. """
  2240. pt = self.parent_persist_selectable
  2241. mt = self.child_persist_selectable
  2242. result = False
  2243. def visit_binary(binary: BinaryExpression[Any]) -> None:
  2244. nonlocal result
  2245. c, f = binary.left, binary.right
  2246. if (
  2247. isinstance(c, expression.ColumnClause)
  2248. and isinstance(f, expression.ColumnClause)
  2249. and pt.is_derived_from(c.table)
  2250. and pt.is_derived_from(f.table)
  2251. and mt.is_derived_from(c.table)
  2252. and mt.is_derived_from(f.table)
  2253. ):
  2254. result = True
  2255. visitors.traverse(self.primaryjoin, {}, {"binary": visit_binary})
  2256. return result
  2257. def _tables_overlap(self) -> bool:
  2258. """Return True if parent/child tables have some overlap."""
  2259. return selectables_overlap(
  2260. self.parent_persist_selectable, self.child_persist_selectable
  2261. )
  2262. def _annotate_remote(self) -> None:
  2263. """Annotate the primaryjoin and secondaryjoin
  2264. structures with 'remote' annotations marking columns
  2265. considered as part of the 'remote' side.
  2266. """
  2267. if self._has_remote_annotations:
  2268. return
  2269. if self.secondary is not None:
  2270. self._annotate_remote_secondary()
  2271. elif self._local_remote_pairs or self._remote_side:
  2272. self._annotate_remote_from_args()
  2273. elif self._refers_to_parent_table():
  2274. self._annotate_selfref(
  2275. lambda col: "foreign" in col._annotations, False
  2276. )
  2277. elif self._tables_overlap():
  2278. self._annotate_remote_with_overlap()
  2279. else:
  2280. self._annotate_remote_distinct_selectables()
  2281. def _annotate_remote_secondary(self) -> None:
  2282. """annotate 'remote' in primaryjoin, secondaryjoin
  2283. when 'secondary' is present.
  2284. """
  2285. assert self.secondary is not None
  2286. fixed_secondary = self.secondary
  2287. def repl(element: _CE, **kw: Any) -> Optional[_CE]:
  2288. if fixed_secondary.c.contains_column(element):
  2289. return element._annotate({"remote": True})
  2290. return None
  2291. self.primaryjoin = visitors.replacement_traverse(
  2292. self.primaryjoin, {}, repl
  2293. )
  2294. assert self.secondaryjoin is not None
  2295. self.secondaryjoin = visitors.replacement_traverse(
  2296. self.secondaryjoin, {}, repl
  2297. )
  2298. def _annotate_selfref(
  2299. self, fn: Callable[[ColumnElement[Any]], bool], remote_side_given: bool
  2300. ) -> None:
  2301. """annotate 'remote' in primaryjoin, secondaryjoin
  2302. when the relationship is detected as self-referential.
  2303. """
  2304. def visit_binary(binary: BinaryExpression[Any]) -> None:
  2305. equated = binary.left.compare(binary.right)
  2306. if isinstance(binary.left, expression.ColumnClause) and isinstance(
  2307. binary.right, expression.ColumnClause
  2308. ):
  2309. # assume one to many - FKs are "remote"
  2310. if fn(binary.left):
  2311. binary.left = binary.left._annotate({"remote": True})
  2312. if fn(binary.right) and not equated:
  2313. binary.right = binary.right._annotate({"remote": True})
  2314. elif not remote_side_given:
  2315. self._warn_non_column_elements()
  2316. self.primaryjoin = visitors.cloned_traverse(
  2317. self.primaryjoin, {}, {"binary": visit_binary}
  2318. )
  2319. def _annotate_remote_from_args(self) -> None:
  2320. """annotate 'remote' in primaryjoin, secondaryjoin
  2321. when the 'remote_side' or '_local_remote_pairs'
  2322. arguments are used.
  2323. """
  2324. if self._local_remote_pairs:
  2325. if self._remote_side:
  2326. raise sa_exc.ArgumentError(
  2327. "remote_side argument is redundant "
  2328. "against more detailed _local_remote_side "
  2329. "argument."
  2330. )
  2331. remote_side = [r for (l, r) in self._local_remote_pairs]
  2332. else:
  2333. remote_side = self._remote_side
  2334. if self._refers_to_parent_table():
  2335. self._annotate_selfref(lambda col: col in remote_side, True)
  2336. else:
  2337. def repl(element: _CE, **kw: Any) -> Optional[_CE]:
  2338. # use set() to avoid generating ``__eq__()`` expressions
  2339. # against each element
  2340. if element in set(remote_side):
  2341. return element._annotate({"remote": True})
  2342. return None
  2343. self.primaryjoin = visitors.replacement_traverse(
  2344. self.primaryjoin, {}, repl
  2345. )
  2346. def _annotate_remote_with_overlap(self) -> None:
  2347. """annotate 'remote' in primaryjoin, secondaryjoin
  2348. when the parent/child tables have some set of
  2349. tables in common, though is not a fully self-referential
  2350. relationship.
  2351. """
  2352. def visit_binary(binary: BinaryExpression[Any]) -> None:
  2353. binary.left, binary.right = proc_left_right(
  2354. binary.left, binary.right
  2355. )
  2356. binary.right, binary.left = proc_left_right(
  2357. binary.right, binary.left
  2358. )
  2359. check_entities = (
  2360. self.prop is not None and self.prop.mapper is not self.prop.parent
  2361. )
  2362. def proc_left_right(
  2363. left: ColumnElement[Any], right: ColumnElement[Any]
  2364. ) -> Tuple[ColumnElement[Any], ColumnElement[Any]]:
  2365. if isinstance(left, expression.ColumnClause) and isinstance(
  2366. right, expression.ColumnClause
  2367. ):
  2368. if self.child_persist_selectable.c.contains_column(
  2369. right
  2370. ) and self.parent_persist_selectable.c.contains_column(left):
  2371. right = right._annotate({"remote": True})
  2372. elif (
  2373. check_entities
  2374. and right._annotations.get("parentmapper") is self.prop.mapper
  2375. ):
  2376. right = right._annotate({"remote": True})
  2377. elif (
  2378. check_entities
  2379. and left._annotations.get("parentmapper") is self.prop.mapper
  2380. ):
  2381. left = left._annotate({"remote": True})
  2382. else:
  2383. self._warn_non_column_elements()
  2384. return left, right
  2385. self.primaryjoin = visitors.cloned_traverse(
  2386. self.primaryjoin, {}, {"binary": visit_binary}
  2387. )
  2388. def _annotate_remote_distinct_selectables(self) -> None:
  2389. """annotate 'remote' in primaryjoin, secondaryjoin
  2390. when the parent/child tables are entirely
  2391. separate.
  2392. """
  2393. def repl(element: _CE, **kw: Any) -> Optional[_CE]:
  2394. if self.child_persist_selectable.c.contains_column(element) and (
  2395. not self.parent_local_selectable.c.contains_column(element)
  2396. or self.child_local_selectable.c.contains_column(element)
  2397. ):
  2398. return element._annotate({"remote": True})
  2399. return None
  2400. self.primaryjoin = visitors.replacement_traverse(
  2401. self.primaryjoin, {}, repl
  2402. )
  2403. def _warn_non_column_elements(self) -> None:
  2404. util.warn(
  2405. "Non-simple column elements in primary "
  2406. "join condition for property %s - consider using "
  2407. "remote() annotations to mark the remote side." % self.prop
  2408. )
  2409. def _annotate_local(self) -> None:
  2410. """Annotate the primaryjoin and secondaryjoin
  2411. structures with 'local' annotations.
  2412. This annotates all column elements found
  2413. simultaneously in the parent table
  2414. and the join condition that don't have a
  2415. 'remote' annotation set up from
  2416. _annotate_remote() or user-defined.
  2417. """
  2418. if self._has_annotation(self.primaryjoin, "local"):
  2419. return
  2420. if self._local_remote_pairs:
  2421. local_side = util.column_set(
  2422. [l for (l, r) in self._local_remote_pairs]
  2423. )
  2424. else:
  2425. local_side = util.column_set(self.parent_persist_selectable.c)
  2426. def locals_(element: _CE, **kw: Any) -> Optional[_CE]:
  2427. if "remote" not in element._annotations and element in local_side:
  2428. return element._annotate({"local": True})
  2429. return None
  2430. self.primaryjoin = visitors.replacement_traverse(
  2431. self.primaryjoin, {}, locals_
  2432. )
  2433. def _annotate_parentmapper(self) -> None:
  2434. def parentmappers_(element: _CE, **kw: Any) -> Optional[_CE]:
  2435. if "remote" in element._annotations:
  2436. return element._annotate({"parentmapper": self.prop.mapper})
  2437. elif "local" in element._annotations:
  2438. return element._annotate({"parentmapper": self.prop.parent})
  2439. return None
  2440. self.primaryjoin = visitors.replacement_traverse(
  2441. self.primaryjoin, {}, parentmappers_
  2442. )
  2443. def _check_remote_side(self) -> None:
  2444. if not self.local_remote_pairs:
  2445. raise sa_exc.ArgumentError(
  2446. "Relationship %s could "
  2447. "not determine any unambiguous local/remote column "
  2448. "pairs based on join condition and remote_side "
  2449. "arguments. "
  2450. "Consider using the remote() annotation to "
  2451. "accurately mark those elements of the join "
  2452. "condition that are on the remote side of "
  2453. "the relationship." % (self.prop,)
  2454. )
  2455. else:
  2456. not_target = util.column_set(
  2457. self.parent_persist_selectable.c
  2458. ).difference(self.child_persist_selectable.c)
  2459. for _, rmt in self.local_remote_pairs:
  2460. if rmt in not_target:
  2461. util.warn(
  2462. "Expression %s is marked as 'remote', but these "
  2463. "column(s) are local to the local side. The "
  2464. "remote() annotation is needed only for a "
  2465. "self-referential relationship where both sides "
  2466. "of the relationship refer to the same tables."
  2467. % (rmt,)
  2468. )
  2469. def _check_foreign_cols(
  2470. self, join_condition: ColumnElement[bool], primary: bool
  2471. ) -> None:
  2472. """Check the foreign key columns collected and emit error
  2473. messages."""
  2474. foreign_cols = self._gather_columns_with_annotation(
  2475. join_condition, "foreign"
  2476. )
  2477. has_foreign = bool(foreign_cols)
  2478. if primary:
  2479. can_sync = bool(self.synchronize_pairs)
  2480. else:
  2481. can_sync = bool(self.secondary_synchronize_pairs)
  2482. if (
  2483. self.support_sync
  2484. and can_sync
  2485. or (not self.support_sync and has_foreign)
  2486. ):
  2487. return
  2488. # from here below is just determining the best error message
  2489. # to report. Check for a join condition using any operator
  2490. # (not just ==), perhaps they need to turn on "viewonly=True".
  2491. if self.support_sync and has_foreign and not can_sync:
  2492. err = (
  2493. "Could not locate any simple equality expressions "
  2494. "involving locally mapped foreign key columns for "
  2495. "%s join condition "
  2496. "'%s' on relationship %s."
  2497. % (
  2498. primary and "primary" or "secondary",
  2499. join_condition,
  2500. self.prop,
  2501. )
  2502. )
  2503. err += (
  2504. " Ensure that referencing columns are associated "
  2505. "with a ForeignKey or ForeignKeyConstraint, or are "
  2506. "annotated in the join condition with the foreign() "
  2507. "annotation. To allow comparison operators other than "
  2508. "'==', the relationship can be marked as viewonly=True."
  2509. )
  2510. raise sa_exc.ArgumentError(err)
  2511. else:
  2512. err = (
  2513. "Could not locate any relevant foreign key columns "
  2514. "for %s join condition '%s' on relationship %s."
  2515. % (
  2516. primary and "primary" or "secondary",
  2517. join_condition,
  2518. self.prop,
  2519. )
  2520. )
  2521. err += (
  2522. " Ensure that referencing columns are associated "
  2523. "with a ForeignKey or ForeignKeyConstraint, or are "
  2524. "annotated in the join condition with the foreign() "
  2525. "annotation."
  2526. )
  2527. raise sa_exc.ArgumentError(err)
  2528. def _determine_direction(self) -> None:
  2529. """Determine if this relationship is one to many, many to one,
  2530. many to many.
  2531. """
  2532. if self.secondaryjoin is not None:
  2533. self.direction = MANYTOMANY
  2534. else:
  2535. parentcols = util.column_set(self.parent_persist_selectable.c)
  2536. targetcols = util.column_set(self.child_persist_selectable.c)
  2537. # fk collection which suggests ONETOMANY.
  2538. onetomany_fk = targetcols.intersection(self.foreign_key_columns)
  2539. # fk collection which suggests MANYTOONE.
  2540. manytoone_fk = parentcols.intersection(self.foreign_key_columns)
  2541. if onetomany_fk and manytoone_fk:
  2542. # fks on both sides. test for overlap of local/remote
  2543. # with foreign key.
  2544. # we will gather columns directly from their annotations
  2545. # without deannotating, so that we can distinguish on a column
  2546. # that refers to itself.
  2547. # 1. columns that are both remote and FK suggest
  2548. # onetomany.
  2549. onetomany_local = self._gather_columns_with_annotation(
  2550. self.primaryjoin, "remote", "foreign"
  2551. )
  2552. # 2. columns that are FK but are not remote (e.g. local)
  2553. # suggest manytoone.
  2554. manytoone_local = {
  2555. c
  2556. for c in self._gather_columns_with_annotation(
  2557. self.primaryjoin, "foreign"
  2558. )
  2559. if "remote" not in c._annotations
  2560. }
  2561. # 3. if both collections are present, remove columns that
  2562. # refer to themselves. This is for the case of
  2563. # and_(Me.id == Me.remote_id, Me.version == Me.version)
  2564. if onetomany_local and manytoone_local:
  2565. self_equated = self.remote_columns.intersection(
  2566. self.local_columns
  2567. )
  2568. onetomany_local = onetomany_local.difference(self_equated)
  2569. manytoone_local = manytoone_local.difference(self_equated)
  2570. # at this point, if only one or the other collection is
  2571. # present, we know the direction, otherwise it's still
  2572. # ambiguous.
  2573. if onetomany_local and not manytoone_local:
  2574. self.direction = ONETOMANY
  2575. elif manytoone_local and not onetomany_local:
  2576. self.direction = MANYTOONE
  2577. else:
  2578. raise sa_exc.ArgumentError(
  2579. "Can't determine relationship"
  2580. " direction for relationship '%s' - foreign "
  2581. "key columns within the join condition are present "
  2582. "in both the parent and the child's mapped tables. "
  2583. "Ensure that only those columns referring "
  2584. "to a parent column are marked as foreign, "
  2585. "either via the foreign() annotation or "
  2586. "via the foreign_keys argument." % self.prop
  2587. )
  2588. elif onetomany_fk:
  2589. self.direction = ONETOMANY
  2590. elif manytoone_fk:
  2591. self.direction = MANYTOONE
  2592. else:
  2593. raise sa_exc.ArgumentError(
  2594. "Can't determine relationship "
  2595. "direction for relationship '%s' - foreign "
  2596. "key columns are present in neither the parent "
  2597. "nor the child's mapped tables" % self.prop
  2598. )
  2599. def _deannotate_pairs(
  2600. self, collection: _ColumnPairIterable
  2601. ) -> _MutableColumnPairs:
  2602. """provide deannotation for the various lists of
  2603. pairs, so that using them in hashes doesn't incur
  2604. high-overhead __eq__() comparisons against
  2605. original columns mapped.
  2606. """
  2607. return [(x._deannotate(), y._deannotate()) for x, y in collection]
  2608. def _setup_pairs(self) -> None:
  2609. sync_pairs: _MutableColumnPairs = []
  2610. lrp: util.OrderedSet[Tuple[ColumnElement[Any], ColumnElement[Any]]] = (
  2611. util.OrderedSet([])
  2612. )
  2613. secondary_sync_pairs: _MutableColumnPairs = []
  2614. def go(
  2615. joincond: ColumnElement[bool],
  2616. collection: _MutableColumnPairs,
  2617. ) -> None:
  2618. def visit_binary(
  2619. binary: BinaryExpression[Any],
  2620. left: ColumnElement[Any],
  2621. right: ColumnElement[Any],
  2622. ) -> None:
  2623. if (
  2624. "remote" in right._annotations
  2625. and "remote" not in left._annotations
  2626. and self.can_be_synced_fn(left)
  2627. ):
  2628. lrp.add((left, right))
  2629. elif (
  2630. "remote" in left._annotations
  2631. and "remote" not in right._annotations
  2632. and self.can_be_synced_fn(right)
  2633. ):
  2634. lrp.add((right, left))
  2635. if binary.operator is operators.eq and self.can_be_synced_fn(
  2636. left, right
  2637. ):
  2638. if "foreign" in right._annotations:
  2639. collection.append((left, right))
  2640. elif "foreign" in left._annotations:
  2641. collection.append((right, left))
  2642. visit_binary_product(visit_binary, joincond)
  2643. for joincond, collection in [
  2644. (self.primaryjoin, sync_pairs),
  2645. (self.secondaryjoin, secondary_sync_pairs),
  2646. ]:
  2647. if joincond is None:
  2648. continue
  2649. go(joincond, collection)
  2650. self.local_remote_pairs = self._deannotate_pairs(lrp)
  2651. self.synchronize_pairs = self._deannotate_pairs(sync_pairs)
  2652. self.secondary_synchronize_pairs = self._deannotate_pairs(
  2653. secondary_sync_pairs
  2654. )
  2655. _track_overlapping_sync_targets: weakref.WeakKeyDictionary[
  2656. ColumnElement[Any],
  2657. weakref.WeakKeyDictionary[
  2658. RelationshipProperty[Any], ColumnElement[Any]
  2659. ],
  2660. ] = weakref.WeakKeyDictionary()
  2661. def _warn_for_conflicting_sync_targets(self) -> None:
  2662. if not self.support_sync:
  2663. return
  2664. # we would like to detect if we are synchronizing any column
  2665. # pairs in conflict with another relationship that wishes to sync
  2666. # an entirely different column to the same target. This is a
  2667. # very rare edge case so we will try to minimize the memory/overhead
  2668. # impact of this check
  2669. for from_, to_ in [
  2670. (from_, to_) for (from_, to_) in self.synchronize_pairs
  2671. ] + [
  2672. (from_, to_) for (from_, to_) in self.secondary_synchronize_pairs
  2673. ]:
  2674. # save ourselves a ton of memory and overhead by only
  2675. # considering columns that are subject to a overlapping
  2676. # FK constraints at the core level. This condition can arise
  2677. # if multiple relationships overlap foreign() directly, but
  2678. # we're going to assume it's typically a ForeignKeyConstraint-
  2679. # level configuration that benefits from this warning.
  2680. if to_ not in self._track_overlapping_sync_targets:
  2681. self._track_overlapping_sync_targets[to_] = (
  2682. weakref.WeakKeyDictionary({self.prop: from_})
  2683. )
  2684. else:
  2685. other_props = []
  2686. prop_to_from = self._track_overlapping_sync_targets[to_]
  2687. for pr, fr_ in prop_to_from.items():
  2688. if (
  2689. not pr.mapper._dispose_called
  2690. and pr not in self.prop._reverse_property
  2691. and pr.key not in self.prop._overlaps
  2692. and self.prop.key not in pr._overlaps
  2693. # note: the "__*" symbol is used internally by
  2694. # SQLAlchemy as a general means of suppressing the
  2695. # overlaps warning for some extension cases, however
  2696. # this is not currently
  2697. # a publicly supported symbol and may change at
  2698. # any time.
  2699. and "__*" not in self.prop._overlaps
  2700. and "__*" not in pr._overlaps
  2701. and not self.prop.parent.is_sibling(pr.parent)
  2702. and not self.prop.mapper.is_sibling(pr.mapper)
  2703. and not self.prop.parent.is_sibling(pr.mapper)
  2704. and not self.prop.mapper.is_sibling(pr.parent)
  2705. and (
  2706. self.prop.key != pr.key
  2707. or not self.prop.parent.common_parent(pr.parent)
  2708. )
  2709. ):
  2710. other_props.append((pr, fr_))
  2711. if other_props:
  2712. util.warn(
  2713. "relationship '%s' will copy column %s to column %s, "
  2714. "which conflicts with relationship(s): %s. "
  2715. "If this is not the intention, consider if these "
  2716. "relationships should be linked with "
  2717. "back_populates, or if viewonly=True should be "
  2718. "applied to one or more if they are read-only. "
  2719. "For the less common case that foreign key "
  2720. "constraints are partially overlapping, the "
  2721. "orm.foreign() "
  2722. "annotation can be used to isolate the columns that "
  2723. "should be written towards. To silence this "
  2724. "warning, add the parameter 'overlaps=\"%s\"' to the "
  2725. "'%s' relationship."
  2726. % (
  2727. self.prop,
  2728. from_,
  2729. to_,
  2730. ", ".join(
  2731. sorted(
  2732. "'%s' (copies %s to %s)" % (pr, fr_, to_)
  2733. for (pr, fr_) in other_props
  2734. )
  2735. ),
  2736. ",".join(sorted(pr.key for pr, fr in other_props)),
  2737. self.prop,
  2738. ),
  2739. code="qzyx",
  2740. )
  2741. self._track_overlapping_sync_targets[to_][self.prop] = from_
  2742. @util.memoized_property
  2743. def remote_columns(self) -> Set[ColumnElement[Any]]:
  2744. return self._gather_join_annotations("remote")
  2745. @util.memoized_property
  2746. def local_columns(self) -> Set[ColumnElement[Any]]:
  2747. return self._gather_join_annotations("local")
  2748. @util.memoized_property
  2749. def foreign_key_columns(self) -> Set[ColumnElement[Any]]:
  2750. return self._gather_join_annotations("foreign")
  2751. def _gather_join_annotations(
  2752. self, annotation: str
  2753. ) -> Set[ColumnElement[Any]]:
  2754. s = set(
  2755. self._gather_columns_with_annotation(self.primaryjoin, annotation)
  2756. )
  2757. if self.secondaryjoin is not None:
  2758. s.update(
  2759. self._gather_columns_with_annotation(
  2760. self.secondaryjoin, annotation
  2761. )
  2762. )
  2763. return {x._deannotate() for x in s}
  2764. def _gather_columns_with_annotation(
  2765. self, clause: ColumnElement[Any], *annotation: Iterable[str]
  2766. ) -> Set[ColumnElement[Any]]:
  2767. annotation_set = set(annotation)
  2768. return {
  2769. cast(ColumnElement[Any], col)
  2770. for col in visitors.iterate(clause, {})
  2771. if annotation_set.issubset(col._annotations)
  2772. }
  2773. @util.memoized_property
  2774. def _secondary_lineage_set(self) -> FrozenSet[ColumnElement[Any]]:
  2775. if self.secondary is not None:
  2776. return frozenset(
  2777. itertools.chain(*[c.proxy_set for c in self.secondary.c])
  2778. )
  2779. else:
  2780. return util.EMPTY_SET
  2781. def join_targets(
  2782. self,
  2783. source_selectable: Optional[FromClause],
  2784. dest_selectable: FromClause,
  2785. aliased: bool,
  2786. single_crit: Optional[ColumnElement[bool]] = None,
  2787. extra_criteria: Tuple[ColumnElement[bool], ...] = (),
  2788. ) -> Tuple[
  2789. ColumnElement[bool],
  2790. Optional[ColumnElement[bool]],
  2791. Optional[FromClause],
  2792. Optional[ClauseAdapter],
  2793. FromClause,
  2794. ]:
  2795. """Given a source and destination selectable, create a
  2796. join between them.
  2797. This takes into account aliasing the join clause
  2798. to reference the appropriate corresponding columns
  2799. in the target objects, as well as the extra child
  2800. criterion, equivalent column sets, etc.
  2801. """
  2802. # place a barrier on the destination such that
  2803. # replacement traversals won't ever dig into it.
  2804. # its internal structure remains fixed
  2805. # regardless of context.
  2806. dest_selectable = _shallow_annotate(
  2807. dest_selectable, {"no_replacement_traverse": True}
  2808. )
  2809. primaryjoin, secondaryjoin, secondary = (
  2810. self.primaryjoin,
  2811. self.secondaryjoin,
  2812. self.secondary,
  2813. )
  2814. # adjust the join condition for single table inheritance,
  2815. # in the case that the join is to a subclass
  2816. # this is analogous to the
  2817. # "_adjust_for_single_table_inheritance()" method in Query.
  2818. if single_crit is not None:
  2819. if secondaryjoin is not None:
  2820. secondaryjoin = secondaryjoin & single_crit
  2821. else:
  2822. primaryjoin = primaryjoin & single_crit
  2823. if extra_criteria:
  2824. def mark_exclude_cols(
  2825. elem: SupportsAnnotations, annotations: _AnnotationDict
  2826. ) -> SupportsAnnotations:
  2827. """note unrelated columns in the "extra criteria" as either
  2828. should be adapted or not adapted, even though they are not
  2829. part of our "local" or "remote" side.
  2830. see #9779 for this case, as well as #11010 for a follow up
  2831. """
  2832. parentmapper_for_element = elem._annotations.get(
  2833. "parentmapper", None
  2834. )
  2835. if (
  2836. parentmapper_for_element is not self.prop.parent
  2837. and parentmapper_for_element is not self.prop.mapper
  2838. and elem not in self._secondary_lineage_set
  2839. ):
  2840. return _safe_annotate(elem, annotations)
  2841. else:
  2842. return elem
  2843. extra_criteria = tuple(
  2844. _deep_annotate(
  2845. elem,
  2846. {"should_not_adapt": True},
  2847. annotate_callable=mark_exclude_cols,
  2848. )
  2849. for elem in extra_criteria
  2850. )
  2851. if secondaryjoin is not None:
  2852. secondaryjoin = secondaryjoin & sql.and_(*extra_criteria)
  2853. else:
  2854. primaryjoin = primaryjoin & sql.and_(*extra_criteria)
  2855. if aliased:
  2856. if secondary is not None:
  2857. secondary = secondary._anonymous_fromclause(flat=True)
  2858. primary_aliasizer = ClauseAdapter(
  2859. secondary,
  2860. exclude_fn=_local_col_exclude,
  2861. )
  2862. secondary_aliasizer = ClauseAdapter(
  2863. dest_selectable, equivalents=self.child_equivalents
  2864. ).chain(primary_aliasizer)
  2865. if source_selectable is not None:
  2866. primary_aliasizer = ClauseAdapter(
  2867. secondary,
  2868. exclude_fn=_local_col_exclude,
  2869. ).chain(
  2870. ClauseAdapter(
  2871. source_selectable,
  2872. equivalents=self.parent_equivalents,
  2873. )
  2874. )
  2875. secondaryjoin = secondary_aliasizer.traverse(secondaryjoin)
  2876. else:
  2877. primary_aliasizer = ClauseAdapter(
  2878. dest_selectable,
  2879. exclude_fn=_local_col_exclude,
  2880. equivalents=self.child_equivalents,
  2881. )
  2882. if source_selectable is not None:
  2883. primary_aliasizer.chain(
  2884. ClauseAdapter(
  2885. source_selectable,
  2886. exclude_fn=_remote_col_exclude,
  2887. equivalents=self.parent_equivalents,
  2888. )
  2889. )
  2890. secondary_aliasizer = None
  2891. primaryjoin = primary_aliasizer.traverse(primaryjoin)
  2892. target_adapter = secondary_aliasizer or primary_aliasizer
  2893. target_adapter.exclude_fn = None
  2894. else:
  2895. target_adapter = None
  2896. return (
  2897. primaryjoin,
  2898. secondaryjoin,
  2899. secondary,
  2900. target_adapter,
  2901. dest_selectable,
  2902. )
  2903. def create_lazy_clause(self, reverse_direction: bool = False) -> Tuple[
  2904. ColumnElement[bool],
  2905. Dict[str, ColumnElement[Any]],
  2906. Dict[ColumnElement[Any], ColumnElement[Any]],
  2907. ]:
  2908. binds: Dict[ColumnElement[Any], BindParameter[Any]] = {}
  2909. equated_columns: Dict[ColumnElement[Any], ColumnElement[Any]] = {}
  2910. has_secondary = self.secondaryjoin is not None
  2911. if has_secondary:
  2912. lookup = collections.defaultdict(list)
  2913. for l, r in self.local_remote_pairs:
  2914. lookup[l].append((l, r))
  2915. equated_columns[r] = l
  2916. elif not reverse_direction:
  2917. for l, r in self.local_remote_pairs:
  2918. equated_columns[r] = l
  2919. else:
  2920. for l, r in self.local_remote_pairs:
  2921. equated_columns[l] = r
  2922. def col_to_bind(
  2923. element: ColumnElement[Any], **kw: Any
  2924. ) -> Optional[BindParameter[Any]]:
  2925. if (
  2926. (not reverse_direction and "local" in element._annotations)
  2927. or reverse_direction
  2928. and (
  2929. (has_secondary and element in lookup)
  2930. or (not has_secondary and "remote" in element._annotations)
  2931. )
  2932. ):
  2933. if element not in binds:
  2934. binds[element] = sql.bindparam(
  2935. None, None, type_=element.type, unique=True
  2936. )
  2937. return binds[element]
  2938. return None
  2939. lazywhere = self.primaryjoin
  2940. if self.secondaryjoin is None or not reverse_direction:
  2941. lazywhere = visitors.replacement_traverse(
  2942. lazywhere, {}, col_to_bind
  2943. )
  2944. if self.secondaryjoin is not None:
  2945. secondaryjoin = self.secondaryjoin
  2946. if reverse_direction:
  2947. secondaryjoin = visitors.replacement_traverse(
  2948. secondaryjoin, {}, col_to_bind
  2949. )
  2950. lazywhere = sql.and_(lazywhere, secondaryjoin)
  2951. bind_to_col = {binds[col].key: col for col in binds}
  2952. return lazywhere, bind_to_col, equated_columns
  2953. class _ColInAnnotations:
  2954. """Serializable object that tests for names in c._annotations.
  2955. TODO: does this need to be serializable anymore? can we find what the
  2956. use case was for that?
  2957. """
  2958. __slots__ = ("names",)
  2959. def __init__(self, *names: str):
  2960. self.names = frozenset(names)
  2961. def __call__(self, c: ClauseElement) -> bool:
  2962. return bool(self.names.intersection(c._annotations))
  2963. _local_col_exclude = _ColInAnnotations("local", "should_not_adapt")
  2964. _remote_col_exclude = _ColInAnnotations("remote", "should_not_adapt")
  2965. class Relationship(
  2966. RelationshipProperty[_T],
  2967. _DeclarativeMapped[_T],
  2968. ):
  2969. """Describes an object property that holds a single item or list
  2970. of items that correspond to a related database table.
  2971. Public constructor is the :func:`_orm.relationship` function.
  2972. .. seealso::
  2973. :ref:`relationship_config_toplevel`
  2974. .. versionchanged:: 2.0 Added :class:`_orm.Relationship` as a Declarative
  2975. compatible subclass for :class:`_orm.RelationshipProperty`.
  2976. """
  2977. inherit_cache = True
  2978. """:meta private:"""
  2979. class _RelationshipDeclared( # type: ignore[misc]
  2980. Relationship[_T],
  2981. WriteOnlyMapped[_T], # not compatible with Mapped[_T]
  2982. DynamicMapped[_T], # not compatible with Mapped[_T]
  2983. ):
  2984. """Relationship subclass used implicitly for declarative mapping."""
  2985. inherit_cache = True
  2986. """:meta private:"""
  2987. @classmethod
  2988. def _mapper_property_name(cls) -> str:
  2989. return "Relationship"