relationships.py 126 KB

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