Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/mapper.py: 25%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1425 statements  

1# orm/mapper.py 

2# Copyright (C) 2005-2026 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# 

5# This module is part of SQLAlchemy and is released under 

6# the MIT License: https://www.opensource.org/licenses/mit-license.php 

7# mypy: allow-untyped-defs, allow-untyped-calls 

8 

9"""Logic to map Python classes to and from selectables. 

10 

11Defines the :class:`~sqlalchemy.orm.mapper.Mapper` class, the central 

12configurational unit which associates a class with a database table. 

13 

14This is a semi-private module; the main configurational API of the ORM is 

15available in :class:`~sqlalchemy.orm.`. 

16 

17""" 

18from __future__ import annotations 

19 

20from collections import deque 

21from functools import reduce 

22from itertools import chain 

23import sys 

24import threading 

25from typing import Any 

26from typing import Callable 

27from typing import cast 

28from typing import Collection 

29from typing import Deque 

30from typing import Dict 

31from typing import FrozenSet 

32from typing import Generic 

33from typing import Iterable 

34from typing import Iterator 

35from typing import List 

36from typing import Literal 

37from typing import Mapping 

38from typing import Optional 

39from typing import Sequence 

40from typing import Set 

41from typing import Tuple 

42from typing import Type 

43from typing import TYPE_CHECKING 

44from typing import TypeVar 

45from typing import Union 

46import weakref 

47 

48from . import attributes 

49from . import exc as orm_exc 

50from . import instrumentation 

51from . import loading 

52from . import properties 

53from . import util as orm_util 

54from ._typing import _O 

55from .base import _class_to_mapper 

56from .base import _parse_mapper_argument 

57from .base import _state_mapper 

58from .base import PassiveFlag 

59from .base import state_str 

60from .interfaces import _MappedAttribute 

61from .interfaces import EXT_SKIP 

62from .interfaces import InspectionAttr 

63from .interfaces import MapperProperty 

64from .interfaces import ORMEntityColumnsClauseRole 

65from .interfaces import ORMFromClauseRole 

66from .interfaces import StrategizedProperty 

67from .path_registry import PathRegistry 

68from .. import event 

69from .. import exc as sa_exc 

70from .. import inspection 

71from .. import log 

72from .. import schema 

73from .. import sql 

74from .. import util 

75from ..event import dispatcher 

76from ..event import EventTarget 

77from ..sql import base as sql_base 

78from ..sql import coercions 

79from ..sql import expression 

80from ..sql import operators 

81from ..sql import roles 

82from ..sql import TableClause 

83from ..sql import util as sql_util 

84from ..sql import visitors 

85from ..sql.cache_key import MemoizedHasCacheKey 

86from ..sql.elements import KeyedColumnElement 

87from ..sql.schema import Column 

88from ..sql.schema import Table 

89from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

90from ..util import HasMemoized 

91from ..util import HasMemoized_ro_memoized_attribute 

92from ..util.typing import TupleAny 

93from ..util.typing import Unpack 

94 

95if TYPE_CHECKING: 

96 from ._typing import _IdentityKeyType 

97 from ._typing import _InstanceDict 

98 from ._typing import _ORMColumnExprArgument 

99 from ._typing import _RegistryType 

100 from .decl_api import registry 

101 from .dependency import _DependencyProcessor 

102 from .descriptor_props import CompositeProperty 

103 from .descriptor_props import SynonymProperty 

104 from .events import MapperEvents 

105 from .instrumentation import ClassManager 

106 from .path_registry import _CachingEntityRegistry 

107 from .properties import ColumnProperty 

108 from .relationships import RelationshipProperty 

109 from .state import InstanceState 

110 from .util import ORMAdapter 

111 from ..engine import Row 

112 from ..engine import RowMapping 

113 from ..sql._typing import _ColumnExpressionArgument 

114 from ..sql._typing import _EquivalentColumnMap 

115 from ..sql.base import _EntityNamespace 

116 from ..sql.base import ReadOnlyColumnCollection 

117 from ..sql.elements import ColumnClause 

118 from ..sql.elements import ColumnElement 

119 from ..sql.selectable import FromClause 

120 from ..util import OrderedSet 

121 

122 

123_T = TypeVar("_T", bound=Any) 

124_MP = TypeVar("_MP", bound="MapperProperty[Any]") 

125_Fn = TypeVar("_Fn", bound="Callable[..., Any]") 

126 

127 

128_WithPolymorphicArg = Union[ 

129 Literal["*"], 

130 Tuple[ 

131 Union[Literal["*"], Sequence[Union["Mapper[Any]", Type[Any]]]], 

132 Optional["FromClause"], 

133 ], 

134 Sequence[Union["Mapper[Any]", Type[Any]]], 

135] 

136 

137 

138_mapper_registries: weakref.WeakKeyDictionary[_RegistryType, bool] = ( 

139 weakref.WeakKeyDictionary() 

140) 

141 

142 

143def _all_registries() -> Set[registry]: 

144 with _CONFIGURE_MUTEX: 

145 return set(_mapper_registries) 

146 

147 

148def _unconfigured_mappers() -> Iterator[Mapper[Any]]: 

149 for reg in _all_registries(): 

150 yield from reg._mappers_to_configure() 

151 

152 

153_already_compiling = False 

154 

155 

156# a constant returned by _get_attr_by_column to indicate 

157# this mapper is not handling an attribute for a particular 

158# column 

159NO_ATTRIBUTE = util.symbol("NO_ATTRIBUTE") 

160 

161# lock used to synchronize the "mapper configure" step 

162_CONFIGURE_MUTEX = threading.RLock() 

163 

164 

165@inspection._self_inspects 

166@log.class_logger 

167class Mapper( 

168 ORMFromClauseRole, 

169 ORMEntityColumnsClauseRole[_O], 

170 MemoizedHasCacheKey, 

171 InspectionAttr, 

172 log.Identified, 

173 inspection.Inspectable["Mapper[_O]"], 

174 EventTarget, 

175 Generic[_O], 

176): 

177 """Defines an association between a Python class and a database table or 

178 other relational structure, so that ORM operations against the class may 

179 proceed. 

180 

181 The :class:`_orm.Mapper` object is instantiated using mapping methods 

182 present on the :class:`_orm.registry` object. For information 

183 about instantiating new :class:`_orm.Mapper` objects, see 

184 :ref:`orm_mapping_classes_toplevel`. 

185 

186 """ 

187 

188 dispatch: dispatcher[Mapper[_O]] 

189 

190 _dispose_called = False 

191 _configure_failed: Any = False 

192 _ready_for_configure = False 

193 

194 def __init__( 

195 self, 

196 class_: Type[_O], 

197 local_table: Optional[FromClause] = None, 

198 properties: Optional[Mapping[str, MapperProperty[Any]]] = None, 

199 primary_key: Optional[Iterable[_ORMColumnExprArgument[Any]]] = None, 

200 inherits: Optional[Union[Mapper[Any], Type[Any]]] = None, 

201 inherit_condition: Optional[_ColumnExpressionArgument[bool]] = None, 

202 inherit_foreign_keys: Optional[ 

203 Sequence[_ORMColumnExprArgument[Any]] 

204 ] = None, 

205 always_refresh: bool = False, 

206 version_id_col: Optional[_ORMColumnExprArgument[Any]] = None, 

207 version_id_generator: Optional[ 

208 Union[Literal[False], Callable[[Any], Any]] 

209 ] = None, 

210 polymorphic_on: Optional[ 

211 Union[_ORMColumnExprArgument[Any], str, MapperProperty[Any]] 

212 ] = None, 

213 _polymorphic_map: Optional[Dict[Any, Mapper[Any]]] = None, 

214 polymorphic_identity: Optional[Any] = None, 

215 concrete: bool = False, 

216 with_polymorphic: Optional[_WithPolymorphicArg] = None, 

217 polymorphic_abstract: bool = False, 

218 polymorphic_load: Optional[Literal["selectin", "inline"]] = None, 

219 allow_partial_pks: bool = True, 

220 batch: bool = True, 

221 column_prefix: Optional[str] = None, 

222 include_properties: Optional[Sequence[str]] = None, 

223 exclude_properties: Optional[Sequence[str]] = None, 

224 passive_updates: bool = True, 

225 passive_deletes: bool = False, 

226 confirm_deleted_rows: bool = True, 

227 eager_defaults: Literal[True, False, "auto"] = "auto", 

228 legacy_is_orphan: bool = False, 

229 _compiled_cache_size: int = 100, 

230 ): 

231 r"""Direct constructor for a new :class:`_orm.Mapper` object. 

232 

233 The :class:`_orm.Mapper` constructor is not called directly, and 

234 is normally invoked through the 

235 use of the :class:`_orm.registry` object through either the 

236 :ref:`Declarative <orm_declarative_mapping>` or 

237 :ref:`Imperative <orm_imperative_mapping>` mapping styles. 

238 

239 .. versionchanged:: 2.0 The public facing ``mapper()`` function is 

240 removed; for a classical mapping configuration, use the 

241 :meth:`_orm.registry.map_imperatively` method. 

242 

243 Parameters documented below may be passed to either the 

244 :meth:`_orm.registry.map_imperatively` method, or may be passed in the 

245 ``__mapper_args__`` declarative class attribute described at 

246 :ref:`orm_declarative_mapper_options`. 

247 

248 :param class\_: The class to be mapped. When using Declarative, 

249 this argument is automatically passed as the declared class 

250 itself. 

251 

252 :param local_table: The :class:`_schema.Table` or other 

253 :class:`_sql.FromClause` (i.e. selectable) to which the class is 

254 mapped. May be ``None`` if this mapper inherits from another mapper 

255 using single-table inheritance. When using Declarative, this 

256 argument is automatically passed by the extension, based on what is 

257 configured via the :attr:`_orm.DeclarativeBase.__table__` attribute 

258 or via the :class:`_schema.Table` produced as a result of 

259 the :attr:`_orm.DeclarativeBase.__tablename__` attribute being 

260 present. 

261 

262 :param polymorphic_abstract: Indicates this class will be mapped in a 

263 polymorphic hierarchy, but not directly instantiated. The class is 

264 mapped normally, except that it has no requirement for a 

265 :paramref:`_orm.Mapper.polymorphic_identity` within an inheritance 

266 hierarchy. The class however must be part of a polymorphic 

267 inheritance scheme which uses 

268 :paramref:`_orm.Mapper.polymorphic_on` at the base. 

269 

270 .. versionadded:: 2.0 

271 

272 .. seealso:: 

273 

274 :ref:`orm_inheritance_abstract_poly` 

275 

276 :param always_refresh: If True, all query operations for this mapped 

277 class will overwrite all data within object instances that already 

278 exist within the session, erasing any in-memory changes with 

279 whatever information was loaded from the database. Usage of this 

280 flag is highly discouraged; as an alternative, see the method 

281 :meth:`_query.Query.populate_existing`. 

282 

283 :param allow_partial_pks: Defaults to True. Indicates that a 

284 composite primary key with some NULL values should be considered as 

285 possibly existing within the database. This affects whether a 

286 mapper will assign an incoming row to an existing identity, as well 

287 as if :meth:`.Session.merge` will check the database first for a 

288 particular primary key value. A "partial primary key" can occur if 

289 one has mapped to an OUTER JOIN, for example. 

290 

291 The :paramref:`.orm.Mapper.allow_partial_pks` parameter also 

292 indicates to the ORM relationship lazy loader, when loading a 

293 many-to-one related object, if a composite primary key that has 

294 partial NULL values should result in an attempt to load from the 

295 database, or if a load attempt is not necessary. 

296 

297 .. versionadded:: 2.0.36 :paramref:`.orm.Mapper.allow_partial_pks` 

298 is consulted by the relationship lazy loader strategy, such that 

299 when set to False, a SELECT for a composite primary key that 

300 has partial NULL values will not be emitted. 

301 

302 :param batch: Defaults to ``True``, indicating that save operations 

303 of multiple entities can be batched together for efficiency. 

304 Setting to False indicates 

305 that an instance will be fully saved before saving the next 

306 instance. This is used in the extremely rare case that a 

307 :class:`.MapperEvents` listener requires being called 

308 in between individual row persistence operations. 

309 

310 :param column_prefix: A string which will be prepended 

311 to the mapped attribute name when :class:`_schema.Column` 

312 objects are automatically assigned as attributes to the 

313 mapped class. Does not affect :class:`.Column` objects that 

314 are mapped explicitly in the :paramref:`.Mapper.properties` 

315 dictionary. 

316 

317 This parameter is typically useful with imperative mappings 

318 that keep the :class:`.Table` object separate. Below, assuming 

319 the ``user_table`` :class:`.Table` object has columns named 

320 ``user_id``, ``user_name``, and ``password``:: 

321 

322 class User(Base): 

323 __table__ = user_table 

324 __mapper_args__ = {"column_prefix": "_"} 

325 

326 The above mapping will assign the ``user_id``, ``user_name``, and 

327 ``password`` columns to attributes named ``_user_id``, 

328 ``_user_name``, and ``_password`` on the mapped ``User`` class. 

329 

330 The :paramref:`.Mapper.column_prefix` parameter is uncommon in 

331 modern use. For dealing with reflected tables, a more flexible 

332 approach to automating a naming scheme is to intercept the 

333 :class:`.Column` objects as they are reflected; see the section 

334 :ref:`mapper_automated_reflection_schemes` for notes on this usage 

335 pattern. 

336 

337 :param concrete: If True, indicates this mapper should use concrete 

338 table inheritance with its parent mapper. 

339 

340 See the section :ref:`concrete_inheritance` for an example. 

341 

342 :param confirm_deleted_rows: defaults to True; when a DELETE occurs 

343 of one more rows based on specific primary keys, a warning is 

344 emitted when the number of rows matched does not equal the number 

345 of rows expected. This parameter may be set to False to handle the 

346 case where database ON DELETE CASCADE rules may be deleting some of 

347 those rows automatically. The warning may be changed to an 

348 exception in a future release. 

349 

350 :param eager_defaults: if True, the ORM will immediately fetch the 

351 value of server-generated default values after an INSERT or UPDATE, 

352 rather than leaving them as expired to be fetched on next access. 

353 This can be used for event schemes where the server-generated values 

354 are needed immediately before the flush completes. 

355 

356 The fetch of values occurs either by using ``RETURNING`` inline 

357 with the ``INSERT`` or ``UPDATE`` statement, or by adding an 

358 additional ``SELECT`` statement subsequent to the ``INSERT`` or 

359 ``UPDATE``, if the backend does not support ``RETURNING``. 

360 

361 The use of ``RETURNING`` is extremely performant in particular for 

362 ``INSERT`` statements where SQLAlchemy can take advantage of 

363 :ref:`insertmanyvalues <engine_insertmanyvalues>`, whereas the use of 

364 an additional ``SELECT`` is relatively poor performing, adding 

365 additional SQL round trips which would be unnecessary if these new 

366 attributes are not to be accessed in any case. 

367 

368 For this reason, :paramref:`.Mapper.eager_defaults` defaults to the 

369 string value ``"auto"``, which indicates that server defaults for 

370 INSERT should be fetched using ``RETURNING`` if the backing database 

371 supports it and if the dialect in use supports "insertmanyreturning" 

372 for an INSERT statement. If the backing database does not support 

373 ``RETURNING`` or "insertmanyreturning" is not available, server 

374 defaults will not be fetched. 

375 

376 .. versionchanged:: 2.0.0rc1 added the "auto" option for 

377 :paramref:`.Mapper.eager_defaults` 

378 

379 .. seealso:: 

380 

381 :ref:`orm_server_defaults` 

382 

383 .. versionchanged:: 2.0.0 RETURNING now works with multiple rows 

384 INSERTed at once using the 

385 :ref:`insertmanyvalues <engine_insertmanyvalues>` feature, which 

386 among other things allows the :paramref:`.Mapper.eager_defaults` 

387 feature to be very performant on supporting backends. 

388 

389 :param exclude_properties: A list or set of string column names to 

390 be excluded from mapping. 

391 

392 .. seealso:: 

393 

394 :ref:`include_exclude_cols` 

395 

396 :param include_properties: An inclusive list or set of string column 

397 names to map. 

398 

399 .. seealso:: 

400 

401 :ref:`include_exclude_cols` 

402 

403 :param inherits: A mapped class or the corresponding 

404 :class:`_orm.Mapper` 

405 of one indicating a superclass to which this :class:`_orm.Mapper` 

406 should *inherit* from. The mapped class here must be a subclass 

407 of the other mapper's class. When using Declarative, this argument 

408 is passed automatically as a result of the natural class 

409 hierarchy of the declared classes. 

410 

411 .. seealso:: 

412 

413 :ref:`inheritance_toplevel` 

414 

415 :param inherit_condition: For joined table inheritance, a SQL 

416 expression which will 

417 define how the two tables are joined; defaults to a natural join 

418 between the two tables. 

419 

420 :param inherit_foreign_keys: When ``inherit_condition`` is used and 

421 the columns present are missing a :class:`_schema.ForeignKey` 

422 configuration, this parameter can be used to specify which columns 

423 are "foreign". In most cases can be left as ``None``. 

424 

425 :param legacy_is_orphan: Boolean, defaults to ``False``. 

426 When ``True``, specifies that "legacy" orphan consideration 

427 is to be applied to objects mapped by this mapper, which means 

428 that a pending (that is, not persistent) object is auto-expunged 

429 from an owning :class:`.Session` only when it is de-associated 

430 from *all* parents that specify a ``delete-orphan`` cascade towards 

431 this mapper. The new default behavior is that the object is 

432 auto-expunged when it is de-associated with *any* of its parents 

433 that specify ``delete-orphan`` cascade. This behavior is more 

434 consistent with that of a persistent object, and allows behavior to 

435 be consistent in more scenarios independently of whether or not an 

436 orphan object has been flushed yet or not. 

437 

438 See the change note and example at :ref:`legacy_is_orphan_addition` 

439 for more detail on this change. 

440 

441 :param passive_deletes: Indicates DELETE behavior of foreign key 

442 columns when a joined-table inheritance entity is being deleted. 

443 Defaults to ``False`` for a base mapper; for an inheriting mapper, 

444 defaults to ``False`` unless the value is set to ``True`` 

445 on the superclass mapper. 

446 

447 When ``True``, it is assumed that ON DELETE CASCADE is configured 

448 on the foreign key relationships that link this mapper's table 

449 to its superclass table, so that when the unit of work attempts 

450 to delete the entity, it need only emit a DELETE statement for the 

451 superclass table, and not this table. 

452 

453 When ``False``, a DELETE statement is emitted for this mapper's 

454 table individually. If the primary key attributes local to this 

455 table are unloaded, then a SELECT must be emitted in order to 

456 validate these attributes; note that the primary key columns 

457 of a joined-table subclass are not part of the "primary key" of 

458 the object as a whole. 

459 

460 Note that a value of ``True`` is **always** forced onto the 

461 subclass mappers; that is, it's not possible for a superclass 

462 to specify passive_deletes without this taking effect for 

463 all subclass mappers. 

464 

465 .. seealso:: 

466 

467 :ref:`passive_deletes` - description of similar feature as 

468 used with :func:`_orm.relationship` 

469 

470 :paramref:`.mapper.passive_updates` - supporting ON UPDATE 

471 CASCADE for joined-table inheritance mappers 

472 

473 :param passive_updates: Indicates UPDATE behavior of foreign key 

474 columns when a primary key column changes on a joined-table 

475 inheritance mapping. Defaults to ``True``. 

476 

477 When True, it is assumed that ON UPDATE CASCADE is configured on 

478 the foreign key in the database, and that the database will handle 

479 propagation of an UPDATE from a source column to dependent columns 

480 on joined-table rows. 

481 

482 When False, it is assumed that the database does not enforce 

483 referential integrity and will not be issuing its own CASCADE 

484 operation for an update. The unit of work process will 

485 emit an UPDATE statement for the dependent columns during a 

486 primary key change. 

487 

488 .. seealso:: 

489 

490 :ref:`passive_updates` - description of a similar feature as 

491 used with :func:`_orm.relationship` 

492 

493 :paramref:`.mapper.passive_deletes` - supporting ON DELETE 

494 CASCADE for joined-table inheritance mappers 

495 

496 :param polymorphic_load: Specifies "polymorphic loading" behavior 

497 for a subclass in an inheritance hierarchy (joined and single 

498 table inheritance only). Valid values are: 

499 

500 * "'inline'" - specifies this class should be part of 

501 the "with_polymorphic" mappers, e.g. its columns will be included 

502 in a SELECT query against the base. 

503 

504 * "'selectin'" - specifies that when instances of this class 

505 are loaded, an additional SELECT will be emitted to retrieve 

506 the columns specific to this subclass. The SELECT uses 

507 IN to fetch multiple subclasses at once. 

508 

509 .. seealso:: 

510 

511 :ref:`with_polymorphic_mapper_config` 

512 

513 :ref:`polymorphic_selectin` 

514 

515 :param polymorphic_on: Specifies the column, attribute, or 

516 SQL expression used to determine the target class for an 

517 incoming row, when inheriting classes are present. 

518 

519 May be specified as a string attribute name, or as a SQL 

520 expression such as a :class:`_schema.Column` or in a Declarative 

521 mapping a :func:`_orm.mapped_column` object. It is typically 

522 expected that the SQL expression corresponds to a column in the 

523 base-most mapped :class:`.Table`:: 

524 

525 class Employee(Base): 

526 __tablename__ = "employee" 

527 

528 id: Mapped[int] = mapped_column(primary_key=True) 

529 discriminator: Mapped[str] = mapped_column(String(50)) 

530 

531 __mapper_args__ = { 

532 "polymorphic_on": discriminator, 

533 "polymorphic_identity": "employee", 

534 } 

535 

536 It may also be specified 

537 as a SQL expression, as in this example where we 

538 use the :func:`.case` construct to provide a conditional 

539 approach:: 

540 

541 class Employee(Base): 

542 __tablename__ = "employee" 

543 

544 id: Mapped[int] = mapped_column(primary_key=True) 

545 discriminator: Mapped[str] = mapped_column(String(50)) 

546 

547 __mapper_args__ = { 

548 "polymorphic_on": case( 

549 (discriminator == "EN", "engineer"), 

550 (discriminator == "MA", "manager"), 

551 else_="employee", 

552 ), 

553 "polymorphic_identity": "employee", 

554 } 

555 

556 It may also refer to any attribute using its string name, 

557 which is of particular use when using annotated column 

558 configurations:: 

559 

560 class Employee(Base): 

561 __tablename__ = "employee" 

562 

563 id: Mapped[int] = mapped_column(primary_key=True) 

564 discriminator: Mapped[str] 

565 

566 __mapper_args__ = { 

567 "polymorphic_on": "discriminator", 

568 "polymorphic_identity": "employee", 

569 } 

570 

571 When setting ``polymorphic_on`` to reference an 

572 attribute or expression that's not present in the 

573 locally mapped :class:`_schema.Table`, yet the value 

574 of the discriminator should be persisted to the database, 

575 the value of the 

576 discriminator is not automatically set on new 

577 instances; this must be handled by the user, 

578 either through manual means or via event listeners. 

579 A typical approach to establishing such a listener 

580 looks like:: 

581 

582 from sqlalchemy import event 

583 from sqlalchemy.orm import object_mapper 

584 

585 

586 @event.listens_for(Employee, "init", propagate=True) 

587 def set_identity(instance, *arg, **kw): 

588 mapper = object_mapper(instance) 

589 instance.discriminator = mapper.polymorphic_identity 

590 

591 Where above, we assign the value of ``polymorphic_identity`` 

592 for the mapped class to the ``discriminator`` attribute, 

593 thus persisting the value to the ``discriminator`` column 

594 in the database. 

595 

596 .. warning:: 

597 

598 Currently, **only one discriminator column may be set**, typically 

599 on the base-most class in the hierarchy. "Cascading" polymorphic 

600 columns are not yet supported. 

601 

602 .. seealso:: 

603 

604 :ref:`inheritance_toplevel` 

605 

606 :param polymorphic_identity: Specifies the value which 

607 identifies this particular class as returned by the column expression 

608 referred to by the :paramref:`_orm.Mapper.polymorphic_on` setting. As 

609 rows are received, the value corresponding to the 

610 :paramref:`_orm.Mapper.polymorphic_on` column expression is compared 

611 to this value, indicating which subclass should be used for the newly 

612 reconstructed object. 

613 

614 .. seealso:: 

615 

616 :ref:`inheritance_toplevel` 

617 

618 :param properties: A dictionary mapping the string names of object 

619 attributes to :class:`.MapperProperty` instances, which define the 

620 persistence behavior of that attribute. Note that 

621 :class:`_schema.Column` 

622 objects present in 

623 the mapped :class:`_schema.Table` are automatically placed into 

624 ``ColumnProperty`` instances upon mapping, unless overridden. 

625 When using Declarative, this argument is passed automatically, 

626 based on all those :class:`.MapperProperty` instances declared 

627 in the declared class body. 

628 

629 .. seealso:: 

630 

631 :ref:`orm_mapping_properties` - in the 

632 :ref:`orm_mapping_classes_toplevel` 

633 

634 :param primary_key: A list of :class:`_schema.Column` 

635 objects, or alternatively string names of attribute names which 

636 refer to :class:`_schema.Column`, which define 

637 the primary key to be used against this mapper's selectable unit. 

638 This is normally simply the primary key of the ``local_table``, but 

639 can be overridden here. 

640 

641 .. versionchanged:: 2.0.2 :paramref:`_orm.Mapper.primary_key` 

642 arguments may be indicated as string attribute names as well. 

643 

644 .. seealso:: 

645 

646 :ref:`mapper_primary_key` - background and example use 

647 

648 :param version_id_col: A :class:`_schema.Column` 

649 that will be used to keep a running version id of rows 

650 in the table. This is used to detect concurrent updates or 

651 the presence of stale data in a flush. The methodology is to 

652 detect if an UPDATE statement does not match the last known 

653 version id, a 

654 :class:`~sqlalchemy.orm.exc.StaleDataError` exception is 

655 thrown. 

656 By default, the column must be of :class:`.Integer` type, 

657 unless ``version_id_generator`` specifies an alternative version 

658 generator. 

659 

660 .. seealso:: 

661 

662 :ref:`mapper_version_counter` - discussion of version counting 

663 and rationale. 

664 

665 :param version_id_generator: Define how new version ids should 

666 be generated. Defaults to ``None``, which indicates that 

667 a simple integer counting scheme be employed. To provide a custom 

668 versioning scheme, provide a callable function of the form:: 

669 

670 def generate_version(version): 

671 return next_version 

672 

673 Alternatively, server-side versioning functions such as triggers, 

674 or programmatic versioning schemes outside of the version id 

675 generator may be used, by specifying the value ``False``. 

676 Please see :ref:`server_side_version_counter` for a discussion 

677 of important points when using this option. 

678 

679 .. seealso:: 

680 

681 :ref:`custom_version_counter` 

682 

683 :ref:`server_side_version_counter` 

684 

685 

686 :param with_polymorphic: A tuple in the form ``(<classes>, 

687 <selectable>)`` indicating the default style of "polymorphic" 

688 loading, that is, which tables are queried at once. <classes> is 

689 any single or list of mappers and/or classes indicating the 

690 inherited classes that should be loaded at once. The special value 

691 ``'*'`` may be used to indicate all descending classes should be 

692 loaded immediately. The second tuple argument <selectable> 

693 indicates a selectable that will be used to query for multiple 

694 classes. 

695 

696 The :paramref:`_orm.Mapper.polymorphic_load` parameter may be 

697 preferable over the use of :paramref:`_orm.Mapper.with_polymorphic` 

698 in modern mappings to indicate a per-subclass technique of 

699 indicating polymorphic loading styles. 

700 

701 .. seealso:: 

702 

703 :ref:`with_polymorphic_mapper_config` 

704 

705 """ 

706 self.class_ = util.assert_arg_type(class_, type, "class_") 

707 self._sort_key = "%s.%s" % ( 

708 self.class_.__module__, 

709 self.class_.__name__, 

710 ) 

711 

712 self._primary_key_argument = util.to_list(primary_key) 

713 

714 self.always_refresh = always_refresh 

715 

716 if isinstance(version_id_col, MapperProperty): 

717 self.version_id_prop = version_id_col 

718 self.version_id_col = None 

719 else: 

720 self.version_id_col = ( 

721 coercions.expect( 

722 roles.ColumnArgumentOrKeyRole, 

723 version_id_col, 

724 argname="version_id_col", 

725 ) 

726 if version_id_col is not None 

727 else None 

728 ) 

729 

730 if version_id_generator is False: 

731 self.version_id_generator = False 

732 elif version_id_generator is None: 

733 self.version_id_generator = lambda x: (x or 0) + 1 

734 else: 

735 self.version_id_generator = version_id_generator 

736 

737 self.concrete = concrete 

738 self.single = False 

739 

740 if inherits is not None: 

741 self.inherits = _parse_mapper_argument(inherits) 

742 else: 

743 self.inherits = None 

744 

745 if local_table is not None: 

746 self.local_table = coercions.expect( 

747 roles.FromClauseRole, 

748 local_table, 

749 disable_inspection=True, 

750 argname="local_table", 

751 ) 

752 elif self.inherits: 

753 # note this is a new flow as of 2.0 so that 

754 # .local_table need not be Optional 

755 self.local_table = self.inherits.local_table 

756 self.single = True 

757 else: 

758 raise sa_exc.ArgumentError( 

759 f"Mapper[{self.class_.__name__}(None)] has None for a " 

760 "primary table argument and does not specify 'inherits'" 

761 ) 

762 

763 if inherit_condition is not None: 

764 self.inherit_condition = coercions.expect( 

765 roles.OnClauseRole, inherit_condition 

766 ) 

767 else: 

768 self.inherit_condition = None 

769 

770 self.inherit_foreign_keys = inherit_foreign_keys 

771 self._init_properties = dict(properties) if properties else {} 

772 self._delete_orphans = [] 

773 self.batch = batch 

774 self.eager_defaults = eager_defaults 

775 self.column_prefix = column_prefix 

776 

777 # interim - polymorphic_on is further refined in 

778 # _configure_polymorphic_setter 

779 self.polymorphic_on = ( 

780 coercions.expect( # type: ignore 

781 roles.ColumnArgumentOrKeyRole, 

782 polymorphic_on, 

783 argname="polymorphic_on", 

784 ) 

785 if polymorphic_on is not None 

786 else None 

787 ) 

788 self.polymorphic_abstract = polymorphic_abstract 

789 self._dependency_processors = [] 

790 self.validators = util.EMPTY_DICT 

791 self.passive_updates = passive_updates 

792 self.passive_deletes = passive_deletes 

793 self.legacy_is_orphan = legacy_is_orphan 

794 self._clause_adapter = None 

795 self._requires_row_aliasing = False 

796 self._inherits_equated_pairs = None 

797 self._memoized_values = {} 

798 self._compiled_cache_size = _compiled_cache_size 

799 self._reconstructor = None 

800 self.allow_partial_pks = allow_partial_pks 

801 

802 if self.inherits and not self.concrete: 

803 self.confirm_deleted_rows = False 

804 else: 

805 self.confirm_deleted_rows = confirm_deleted_rows 

806 

807 self._set_with_polymorphic(with_polymorphic) 

808 self.polymorphic_load = polymorphic_load 

809 

810 # our 'polymorphic identity', a string name that when located in a 

811 # result set row indicates this Mapper should be used to construct 

812 # the object instance for that row. 

813 self.polymorphic_identity = polymorphic_identity 

814 

815 # a dictionary of 'polymorphic identity' names, associating those 

816 # names with Mappers that will be used to construct object instances 

817 # upon a select operation. 

818 if _polymorphic_map is None: 

819 self.polymorphic_map = {} 

820 else: 

821 self.polymorphic_map = _polymorphic_map 

822 

823 if include_properties is not None: 

824 self.include_properties = util.to_set(include_properties) 

825 else: 

826 self.include_properties = None 

827 if exclude_properties: 

828 self.exclude_properties = util.to_set(exclude_properties) 

829 else: 

830 self.exclude_properties = None 

831 

832 # prevent this mapper from being constructed 

833 # while a configure_mappers() is occurring (and defer a 

834 # configure_mappers() until construction succeeds) 

835 with _CONFIGURE_MUTEX: 

836 cast("MapperEvents", self.dispatch._events)._new_mapper_instance( 

837 class_, self 

838 ) 

839 self._configure_inheritance() 

840 self._configure_class_instrumentation() 

841 self._configure_properties() 

842 self._configure_polymorphic_setter() 

843 self._configure_pks() 

844 self.registry._flag_new_mapper(self) 

845 self._log("constructed") 

846 self._expire_memoizations() 

847 

848 self.dispatch.after_mapper_constructed(self, self.class_) 

849 

850 def _prefer_eager_defaults(self, dialect, table): 

851 if self.eager_defaults == "auto": 

852 if not table.implicit_returning: 

853 return False 

854 

855 return ( 

856 table in self._server_default_col_keys 

857 and dialect.insert_executemany_returning 

858 ) 

859 else: 

860 return self.eager_defaults 

861 

862 def _gen_cache_key(self, anon_map, bindparams): 

863 return (self,) 

864 

865 # ### BEGIN 

866 # ATTRIBUTE DECLARATIONS START HERE 

867 

868 is_mapper = True 

869 """Part of the inspection API.""" 

870 

871 represents_outer_join = False 

872 

873 registry: _RegistryType 

874 

875 @property 

876 def mapper(self) -> Mapper[_O]: 

877 """Part of the inspection API. 

878 

879 Returns self. 

880 

881 """ 

882 return self 

883 

884 @property 

885 def entity(self): 

886 r"""Part of the inspection API. 

887 

888 Returns self.class\_. 

889 

890 """ 

891 return self.class_ 

892 

893 class_: Type[_O] 

894 """The class to which this :class:`_orm.Mapper` is mapped.""" 

895 

896 _identity_class: Type[_O] 

897 

898 _delete_orphans: List[Tuple[str, Type[Any]]] 

899 _dependency_processors: List[_DependencyProcessor] 

900 _memoized_values: Dict[Any, Callable[[], Any]] 

901 _inheriting_mappers: util.WeakSequence[Mapper[Any]] 

902 _all_tables: Set[TableClause] 

903 _polymorphic_attr_key: Optional[str] 

904 

905 _pks_by_table: Dict[FromClause, OrderedSet[ColumnClause[Any]]] 

906 _cols_by_table: Dict[FromClause, OrderedSet[ColumnElement[Any]]] 

907 

908 _props: util.OrderedDict[str, MapperProperty[Any]] 

909 _init_properties: Dict[str, MapperProperty[Any]] 

910 

911 _columntoproperty: _ColumnMapping 

912 

913 _set_polymorphic_identity: Optional[Callable[[InstanceState[_O]], None]] 

914 _validate_polymorphic_identity: Optional[ 

915 Callable[[Mapper[_O], InstanceState[_O], _InstanceDict], None] 

916 ] 

917 

918 tables: Sequence[TableClause] 

919 """A sequence containing the collection of :class:`_schema.Table` 

920 or :class:`_schema.TableClause` objects which this :class:`_orm.Mapper` 

921 is aware of. 

922 

923 If the mapper is mapped to a :class:`_expression.Join`, or an 

924 :class:`_expression.Alias` 

925 representing a :class:`_expression.Select`, the individual 

926 :class:`_schema.Table` 

927 objects that comprise the full construct will be represented here. 

928 

929 This is a *read only* attribute determined during mapper construction. 

930 Behavior is undefined if directly modified. 

931 

932 """ 

933 

934 validators: util.immutabledict[str, Tuple[str, Dict[str, Any]]] 

935 """An immutable dictionary of attributes which have been decorated 

936 using the :func:`_orm.validates` decorator. 

937 

938 The dictionary contains string attribute names as keys 

939 mapped to the actual validation method. 

940 

941 """ 

942 

943 always_refresh: bool 

944 allow_partial_pks: bool 

945 version_id_col: Optional[ColumnElement[Any]] 

946 

947 with_polymorphic: Optional[ 

948 Tuple[ 

949 Union[Literal["*"], Sequence[Union[Mapper[Any], Type[Any]]]], 

950 Optional[FromClause], 

951 ] 

952 ] 

953 

954 version_id_generator: Optional[Union[Literal[False], Callable[[Any], Any]]] 

955 

956 local_table: FromClause 

957 """The immediate :class:`_expression.FromClause` to which this 

958 :class:`_orm.Mapper` refers. 

959 

960 Typically is an instance of :class:`_schema.Table`, may be any 

961 :class:`.FromClause`. 

962 

963 The "local" table is the 

964 selectable that the :class:`_orm.Mapper` is directly responsible for 

965 managing from an attribute access and flush perspective. For 

966 non-inheriting mappers, :attr:`.Mapper.local_table` will be the same 

967 as :attr:`.Mapper.persist_selectable`. For inheriting mappers, 

968 :attr:`.Mapper.local_table` refers to the specific portion of 

969 :attr:`.Mapper.persist_selectable` that includes the columns to which 

970 this :class:`.Mapper` is loading/persisting, such as a particular 

971 :class:`.Table` within a join. 

972 

973 .. seealso:: 

974 

975 :attr:`_orm.Mapper.persist_selectable`. 

976 

977 :attr:`_orm.Mapper.selectable`. 

978 

979 """ 

980 

981 persist_selectable: FromClause 

982 """The :class:`_expression.FromClause` to which this :class:`_orm.Mapper` 

983 is mapped. 

984 

985 Typically is an instance of :class:`_schema.Table`, may be any 

986 :class:`.FromClause`. 

987 

988 The :attr:`_orm.Mapper.persist_selectable` is similar to 

989 :attr:`.Mapper.local_table`, but represents the :class:`.FromClause` that 

990 represents the inheriting class hierarchy overall in an inheritance 

991 scenario. 

992 

993 :attr.`.Mapper.persist_selectable` is also separate from the 

994 :attr:`.Mapper.selectable` attribute, the latter of which may be an 

995 alternate subquery used for selecting columns. 

996 :attr.`.Mapper.persist_selectable` is oriented towards columns that 

997 will be written on a persist operation. 

998 

999 .. seealso:: 

1000 

1001 :attr:`_orm.Mapper.selectable`. 

1002 

1003 :attr:`_orm.Mapper.local_table`. 

1004 

1005 """ 

1006 

1007 inherits: Optional[Mapper[Any]] 

1008 """References the :class:`_orm.Mapper` which this :class:`_orm.Mapper` 

1009 inherits from, if any. 

1010 

1011 """ 

1012 

1013 inherit_condition: Optional[ColumnElement[bool]] 

1014 

1015 configured: bool = False 

1016 """Represent ``True`` if this :class:`_orm.Mapper` has been configured. 

1017 

1018 This is a *read only* attribute determined during mapper construction. 

1019 Behavior is undefined if directly modified. 

1020 

1021 .. seealso:: 

1022 

1023 :func:`.configure_mappers`. 

1024 

1025 """ 

1026 

1027 concrete: bool 

1028 """Represent ``True`` if this :class:`_orm.Mapper` is a concrete 

1029 inheritance mapper. 

1030 

1031 This is a *read only* attribute determined during mapper construction. 

1032 Behavior is undefined if directly modified. 

1033 

1034 """ 

1035 

1036 primary_key: Tuple[ColumnElement[Any], ...] 

1037 """An iterable containing the collection of :class:`_schema.Column` 

1038 objects 

1039 which comprise the 'primary key' of the mapped table, from the 

1040 perspective of this :class:`_orm.Mapper`. 

1041 

1042 This list is against the selectable in 

1043 :attr:`_orm.Mapper.persist_selectable`. 

1044 In the case of inheriting mappers, some columns may be managed by a 

1045 superclass mapper. For example, in the case of a 

1046 :class:`_expression.Join`, the 

1047 primary key is determined by all of the primary key columns across all 

1048 tables referenced by the :class:`_expression.Join`. 

1049 

1050 The list is also not necessarily the same as the primary key column 

1051 collection associated with the underlying tables; the :class:`_orm.Mapper` 

1052 features a ``primary_key`` argument that can override what the 

1053 :class:`_orm.Mapper` considers as primary key columns. 

1054 

1055 This is a *read only* attribute determined during mapper construction. 

1056 Behavior is undefined if directly modified. 

1057 

1058 """ 

1059 

1060 class_manager: ClassManager[_O] 

1061 """The :class:`.ClassManager` which maintains event listeners 

1062 and class-bound descriptors for this :class:`_orm.Mapper`. 

1063 

1064 This is a *read only* attribute determined during mapper construction. 

1065 Behavior is undefined if directly modified. 

1066 

1067 """ 

1068 

1069 single: bool 

1070 """Represent ``True`` if this :class:`_orm.Mapper` is a single table 

1071 inheritance mapper. 

1072 

1073 :attr:`_orm.Mapper.local_table` will be ``None`` if this flag is set. 

1074 

1075 This is a *read only* attribute determined during mapper construction. 

1076 Behavior is undefined if directly modified. 

1077 

1078 """ 

1079 

1080 polymorphic_on: Optional[KeyedColumnElement[Any]] 

1081 """The :class:`_schema.Column` or SQL expression specified as the 

1082 ``polymorphic_on`` argument 

1083 for this :class:`_orm.Mapper`, within an inheritance scenario. 

1084 

1085 This attribute is normally a :class:`_schema.Column` instance but 

1086 may also be an expression, such as one derived from 

1087 :func:`.cast`. 

1088 

1089 This is a *read only* attribute determined during mapper construction. 

1090 Behavior is undefined if directly modified. 

1091 

1092 """ 

1093 

1094 polymorphic_map: Dict[Any, Mapper[Any]] 

1095 """A mapping of "polymorphic identity" identifiers mapped to 

1096 :class:`_orm.Mapper` instances, within an inheritance scenario. 

1097 

1098 The identifiers can be of any type which is comparable to the 

1099 type of column represented by :attr:`_orm.Mapper.polymorphic_on`. 

1100 

1101 An inheritance chain of mappers will all reference the same 

1102 polymorphic map object. The object is used to correlate incoming 

1103 result rows to target mappers. 

1104 

1105 This is a *read only* attribute determined during mapper construction. 

1106 Behavior is undefined if directly modified. 

1107 

1108 """ 

1109 

1110 polymorphic_identity: Optional[Any] 

1111 """Represent an identifier which is matched against the 

1112 :attr:`_orm.Mapper.polymorphic_on` column during result row loading. 

1113 

1114 Used only with inheritance, this object can be of any type which is 

1115 comparable to the type of column represented by 

1116 :attr:`_orm.Mapper.polymorphic_on`. 

1117 

1118 This is a *read only* attribute determined during mapper construction. 

1119 Behavior is undefined if directly modified. 

1120 

1121 """ 

1122 

1123 base_mapper: Mapper[Any] 

1124 """The base-most :class:`_orm.Mapper` in an inheritance chain. 

1125 

1126 In a non-inheriting scenario, this attribute will always be this 

1127 :class:`_orm.Mapper`. In an inheritance scenario, it references 

1128 the :class:`_orm.Mapper` which is parent to all other :class:`_orm.Mapper` 

1129 objects in the inheritance chain. 

1130 

1131 This is a *read only* attribute determined during mapper construction. 

1132 Behavior is undefined if directly modified. 

1133 

1134 """ 

1135 

1136 _columns: sql_base.WriteableColumnCollection[str, Column[Any]] 

1137 

1138 @util.memoized_property 

1139 def _path_registry(self) -> _CachingEntityRegistry: 

1140 return PathRegistry.per_mapper(self) 

1141 

1142 def _configure_inheritance(self): 

1143 """Configure settings related to inheriting and/or inherited mappers 

1144 being present.""" 

1145 

1146 # a set of all mappers which inherit from this one. 

1147 self._inheriting_mappers = util.WeakSequence() 

1148 

1149 if self.inherits: 

1150 if not issubclass(self.class_, self.inherits.class_): 

1151 raise sa_exc.ArgumentError( 

1152 "Class '%s' does not inherit from '%s'" 

1153 % (self.class_.__name__, self.inherits.class_.__name__) 

1154 ) 

1155 

1156 self.dispatch._update(self.inherits.dispatch) 

1157 

1158 if self.single: 

1159 self.persist_selectable = self.inherits.persist_selectable 

1160 elif self.local_table is not self.inherits.local_table: 

1161 if self.concrete: 

1162 self.persist_selectable = self.local_table 

1163 for mapper in self.iterate_to_root(): 

1164 if mapper.polymorphic_on is not None: 

1165 mapper._requires_row_aliasing = True 

1166 else: 

1167 if self.inherit_condition is None: 

1168 # figure out inherit condition from our table to the 

1169 # immediate table of the inherited mapper, not its 

1170 # full table which could pull in other stuff we don't 

1171 # want (allows test/inheritance.InheritTest4 to pass) 

1172 try: 

1173 self.inherit_condition = sql_util.join_condition( 

1174 self.inherits.local_table, self.local_table 

1175 ) 

1176 except sa_exc.NoForeignKeysError as nfe: 

1177 assert self.inherits.local_table is not None 

1178 assert self.local_table is not None 

1179 raise sa_exc.NoForeignKeysError( 

1180 "Can't determine the inherit condition " 

1181 "between inherited table '%s' and " 

1182 "inheriting " 

1183 "table '%s'; tables have no " 

1184 "foreign key relationships established. " 

1185 "Please ensure the inheriting table has " 

1186 "a foreign key relationship to the " 

1187 "inherited " 

1188 "table, or provide an " 

1189 "'on clause' using " 

1190 "the 'inherit_condition' mapper argument." 

1191 % ( 

1192 self.inherits.local_table.description, 

1193 self.local_table.description, 

1194 ) 

1195 ) from nfe 

1196 except sa_exc.AmbiguousForeignKeysError as afe: 

1197 assert self.inherits.local_table is not None 

1198 assert self.local_table is not None 

1199 raise sa_exc.AmbiguousForeignKeysError( 

1200 "Can't determine the inherit condition " 

1201 "between inherited table '%s' and " 

1202 "inheriting " 

1203 "table '%s'; tables have more than one " 

1204 "foreign key relationship established. " 

1205 "Please specify the 'on clause' using " 

1206 "the 'inherit_condition' mapper argument." 

1207 % ( 

1208 self.inherits.local_table.description, 

1209 self.local_table.description, 

1210 ) 

1211 ) from afe 

1212 assert self.inherits.persist_selectable is not None 

1213 self.persist_selectable = sql.join( 

1214 self.inherits.persist_selectable, 

1215 self.local_table, 

1216 self.inherit_condition, 

1217 ) 

1218 

1219 fks = util.to_set(self.inherit_foreign_keys) 

1220 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 

1221 self.persist_selectable.onclause, 

1222 consider_as_foreign_keys=fks, 

1223 ) 

1224 else: 

1225 self.persist_selectable = self.local_table 

1226 

1227 if self.polymorphic_identity is None: 

1228 self._identity_class = self.class_ 

1229 

1230 if ( 

1231 not self.polymorphic_abstract 

1232 and self.inherits.base_mapper.polymorphic_on is not None 

1233 ): 

1234 util.warn( 

1235 f"{self} does not indicate a 'polymorphic_identity', " 

1236 "yet is part of an inheritance hierarchy that has a " 

1237 f"'polymorphic_on' column of " 

1238 f"'{self.inherits.base_mapper.polymorphic_on}'. " 

1239 "If this is an intermediary class that should not be " 

1240 "instantiated, the class may either be left unmapped, " 

1241 "or may include the 'polymorphic_abstract=True' " 

1242 "parameter in its Mapper arguments. To leave the " 

1243 "class unmapped when using Declarative, set the " 

1244 "'__abstract__ = True' attribute on the class." 

1245 ) 

1246 elif self.concrete: 

1247 self._identity_class = self.class_ 

1248 else: 

1249 self._identity_class = self.inherits._identity_class 

1250 

1251 if self.version_id_col is None: 

1252 self.version_id_col = self.inherits.version_id_col 

1253 self.version_id_generator = self.inherits.version_id_generator 

1254 elif ( 

1255 self.inherits.version_id_col is not None 

1256 and self.version_id_col is not self.inherits.version_id_col 

1257 ): 

1258 util.warn( 

1259 "Inheriting version_id_col '%s' does not match inherited " 

1260 "version_id_col '%s' and will not automatically populate " 

1261 "the inherited versioning column. " 

1262 "version_id_col should only be specified on " 

1263 "the base-most mapper that includes versioning." 

1264 % ( 

1265 self.version_id_col.description, 

1266 self.inherits.version_id_col.description, 

1267 ) 

1268 ) 

1269 

1270 self.polymorphic_map = self.inherits.polymorphic_map 

1271 self.batch = self.inherits.batch 

1272 self.inherits._inheriting_mappers.append(self) 

1273 self.base_mapper = self.inherits.base_mapper 

1274 self.passive_updates = self.inherits.passive_updates 

1275 self.passive_deletes = ( 

1276 self.inherits.passive_deletes or self.passive_deletes 

1277 ) 

1278 self._all_tables = self.inherits._all_tables 

1279 

1280 if self.polymorphic_identity is not None: 

1281 if self.polymorphic_identity in self.polymorphic_map: 

1282 util.warn( 

1283 "Reassigning polymorphic association for identity %r " 

1284 "from %r to %r: Check for duplicate use of %r as " 

1285 "value for polymorphic_identity." 

1286 % ( 

1287 self.polymorphic_identity, 

1288 self.polymorphic_map[self.polymorphic_identity], 

1289 self, 

1290 self.polymorphic_identity, 

1291 ) 

1292 ) 

1293 self.polymorphic_map[self.polymorphic_identity] = self 

1294 

1295 if self.polymorphic_load and self.concrete: 

1296 raise sa_exc.ArgumentError( 

1297 "polymorphic_load is not currently supported " 

1298 "with concrete table inheritance" 

1299 ) 

1300 if self.polymorphic_load == "inline": 

1301 self.inherits._add_with_polymorphic_subclass(self) 

1302 elif self.polymorphic_load == "selectin": 

1303 pass 

1304 elif self.polymorphic_load is not None: 

1305 raise sa_exc.ArgumentError( 

1306 "unknown argument for polymorphic_load: %r" 

1307 % self.polymorphic_load 

1308 ) 

1309 

1310 else: 

1311 self._all_tables = set() 

1312 self.base_mapper = self 

1313 assert self.local_table is not None 

1314 self.persist_selectable = self.local_table 

1315 if self.polymorphic_identity is not None: 

1316 self.polymorphic_map[self.polymorphic_identity] = self 

1317 self._identity_class = self.class_ 

1318 

1319 if self.persist_selectable is None: 

1320 raise sa_exc.ArgumentError( 

1321 "Mapper '%s' does not have a persist_selectable specified." 

1322 % self 

1323 ) 

1324 

1325 def _set_with_polymorphic( 

1326 self, with_polymorphic: Optional[_WithPolymorphicArg] 

1327 ) -> None: 

1328 if with_polymorphic == "*": 

1329 self.with_polymorphic = ("*", None) 

1330 elif isinstance(with_polymorphic, (tuple, list)): 

1331 if isinstance(with_polymorphic[0], (str, tuple, list)): 

1332 self.with_polymorphic = cast( 

1333 """Tuple[ 

1334 Union[ 

1335 Literal["*"], 

1336 Sequence[Union["Mapper[Any]", Type[Any]]], 

1337 ], 

1338 Optional["FromClause"], 

1339 ]""", 

1340 with_polymorphic, 

1341 ) 

1342 else: 

1343 self.with_polymorphic = (with_polymorphic, None) 

1344 elif with_polymorphic is not None: 

1345 raise sa_exc.ArgumentError( 

1346 f"Invalid setting for with_polymorphic: {with_polymorphic!r}" 

1347 ) 

1348 else: 

1349 self.with_polymorphic = None 

1350 

1351 if self.with_polymorphic and self.with_polymorphic[1] is not None: 

1352 self.with_polymorphic = ( 

1353 self.with_polymorphic[0], 

1354 coercions.expect( 

1355 roles.FromClauseRole, 

1356 self.with_polymorphic[1], 

1357 ), 

1358 ) 

1359 

1360 if self.configured: 

1361 self._expire_memoizations() 

1362 

1363 def _add_with_polymorphic_subclass(self, mapper): 

1364 subcl = mapper.class_ 

1365 if self.with_polymorphic is None: 

1366 self._set_with_polymorphic((subcl,)) 

1367 elif self.with_polymorphic[0] != "*": 

1368 assert isinstance(self.with_polymorphic[0], tuple) 

1369 self._set_with_polymorphic( 

1370 (self.with_polymorphic[0] + (subcl,), self.with_polymorphic[1]) 

1371 ) 

1372 

1373 def _set_concrete_base(self, mapper): 

1374 """Set the given :class:`_orm.Mapper` as the 'inherits' for this 

1375 :class:`_orm.Mapper`, assuming this :class:`_orm.Mapper` is concrete 

1376 and does not already have an inherits.""" 

1377 

1378 assert self.concrete 

1379 assert not self.inherits 

1380 assert isinstance(mapper, Mapper) 

1381 self.inherits = mapper 

1382 self.inherits.polymorphic_map.update(self.polymorphic_map) 

1383 self.polymorphic_map = self.inherits.polymorphic_map 

1384 for mapper in self.iterate_to_root(): 

1385 if mapper.polymorphic_on is not None: 

1386 mapper._requires_row_aliasing = True 

1387 self.batch = self.inherits.batch 

1388 for mp in self.self_and_descendants: 

1389 mp.base_mapper = self.inherits.base_mapper 

1390 self.inherits._inheriting_mappers.append(self) 

1391 self.passive_updates = self.inherits.passive_updates 

1392 self._all_tables = self.inherits._all_tables 

1393 

1394 for key, prop in mapper._props.items(): 

1395 if key not in self._props and not self._should_exclude( 

1396 key, key, local=False, column=None 

1397 ): 

1398 self._adapt_inherited_property(key, prop, False) 

1399 

1400 def _set_polymorphic_on(self, polymorphic_on): 

1401 self.polymorphic_on = polymorphic_on 

1402 self._configure_polymorphic_setter(True) 

1403 

1404 def _configure_class_instrumentation(self): 

1405 """Associate this Mapper with the 

1406 given class and entity name. 

1407 

1408 Subsequent calls to ``class_mapper()`` for the ``class_`` / ``entity`` 

1409 name combination will return this mapper. Also decorate the 

1410 `__init__` method on the mapped class to include optional 

1411 auto-session attachment logic. 

1412 

1413 """ 

1414 

1415 # we expect that declarative has applied the class manager 

1416 # already and set up a registry. if this is None, 

1417 # this raises as of 2.0. 

1418 manager = attributes.opt_manager_of_class(self.class_) 

1419 

1420 if manager is None or not manager.registry: 

1421 raise sa_exc.InvalidRequestError( 

1422 "The _mapper() function and Mapper() constructor may not be " 

1423 "invoked directly outside of a declarative registry." 

1424 " Please use the sqlalchemy.orm.registry.map_imperatively() " 

1425 "function for a classical mapping." 

1426 ) 

1427 

1428 self.dispatch.instrument_class(self, self.class_) 

1429 

1430 # this invokes the class_instrument event and sets up 

1431 # the __init__ method. documented behavior is that this must 

1432 # occur after the instrument_class event above. 

1433 # yes two events with the same two words reversed and different APIs. 

1434 # :( 

1435 

1436 manager = instrumentation.register_class( 

1437 self.class_, 

1438 mapper=self, 

1439 expired_attribute_loader=util.partial( 

1440 loading._load_scalar_attributes, self 

1441 ), 

1442 # finalize flag means instrument the __init__ method 

1443 # and call the class_instrument event 

1444 finalize=True, 

1445 ) 

1446 

1447 self.class_manager = manager 

1448 

1449 assert manager.registry is not None 

1450 self.registry = manager.registry 

1451 

1452 # The remaining members can be added by any mapper, 

1453 # e_name None or not. 

1454 if manager.mapper is None: 

1455 return 

1456 

1457 event.listen(manager, "init", _event_on_init, raw=True) 

1458 

1459 for key, method in util.iterate_attributes(self.class_): 

1460 if key == "__init__" and hasattr(method, "_sa_original_init"): 

1461 method = method._sa_original_init 

1462 if hasattr(method, "__func__"): 

1463 method = method.__func__ 

1464 if callable(method): 

1465 if hasattr(method, "__sa_reconstructor__"): 

1466 self._reconstructor = method 

1467 event.listen(manager, "load", _event_on_load, raw=True) 

1468 elif hasattr(method, "__sa_validators__"): 

1469 validation_opts = method.__sa_validation_opts__ 

1470 for name in method.__sa_validators__: 

1471 if name in self.validators: 

1472 raise sa_exc.InvalidRequestError( 

1473 "A validation function for mapped " 

1474 "attribute %r on mapper %s already exists." 

1475 % (name, self) 

1476 ) 

1477 self.validators = self.validators.union( 

1478 {name: (method, validation_opts)} 

1479 ) 

1480 

1481 def _set_dispose_flags(self) -> None: 

1482 self.configured = True 

1483 self._ready_for_configure = True 

1484 self._dispose_called = True 

1485 

1486 self.__dict__.pop("_configure_failed", None) 

1487 

1488 def _str_arg_to_mapped_col(self, argname: str, key: str) -> Column[Any]: 

1489 try: 

1490 prop = self._props[key] 

1491 except KeyError as err: 

1492 raise sa_exc.ArgumentError( 

1493 f"Can't determine {argname} column '{key}' - " 

1494 "no attribute is mapped to this name." 

1495 ) from err 

1496 try: 

1497 expr = prop.expression 

1498 except AttributeError as ae: 

1499 raise sa_exc.ArgumentError( 

1500 f"Can't determine {argname} column '{key}'; " 

1501 "property does not refer to a single mapped Column" 

1502 ) from ae 

1503 if not isinstance(expr, Column): 

1504 raise sa_exc.ArgumentError( 

1505 f"Can't determine {argname} column '{key}'; " 

1506 "property does not refer to a single " 

1507 "mapped Column" 

1508 ) 

1509 return expr 

1510 

1511 def _configure_pks(self) -> None: 

1512 self.tables = sql_util.find_tables(self.persist_selectable) 

1513 

1514 self._all_tables.update(t for t in self.tables) 

1515 

1516 self._pks_by_table = {} 

1517 self._cols_by_table = {} 

1518 

1519 all_cols = util.column_set( 

1520 chain(*[col.proxy_set for col in self._columntoproperty]) 

1521 ) 

1522 

1523 pk_cols = util.column_set(c for c in all_cols if c.primary_key) 

1524 

1525 # identify primary key columns which are also mapped by this mapper. 

1526 for fc in set(self.tables).union([self.persist_selectable]): 

1527 if fc.primary_key and pk_cols.issuperset(fc.primary_key): 

1528 # ordering is important since it determines the ordering of 

1529 # mapper.primary_key (and therefore query.get()) 

1530 self._pks_by_table[fc] = util.ordered_column_set( # type: ignore # noqa: E501 

1531 fc.primary_key 

1532 ).intersection( 

1533 pk_cols 

1534 ) 

1535 self._cols_by_table[fc] = util.ordered_column_set(fc.c).intersection( # type: ignore # noqa: E501 

1536 all_cols 

1537 ) 

1538 

1539 if self._primary_key_argument: 

1540 coerced_pk_arg = [ 

1541 ( 

1542 self._str_arg_to_mapped_col("primary_key", c) 

1543 if isinstance(c, str) 

1544 else c 

1545 ) 

1546 for c in ( 

1547 coercions.expect( 

1548 roles.DDLConstraintColumnRole, 

1549 coerce_pk, 

1550 argname="primary_key", 

1551 ) 

1552 for coerce_pk in self._primary_key_argument 

1553 ) 

1554 ] 

1555 else: 

1556 coerced_pk_arg = None 

1557 

1558 # if explicit PK argument sent, add those columns to the 

1559 # primary key mappings 

1560 if coerced_pk_arg: 

1561 for k in coerced_pk_arg: 

1562 if k.table not in self._pks_by_table: 

1563 self._pks_by_table[k.table] = util.OrderedSet() 

1564 self._pks_by_table[k.table].add(k) 

1565 

1566 # otherwise, see that we got a full PK for the mapped table 

1567 elif ( 

1568 self.persist_selectable not in self._pks_by_table 

1569 or len(self._pks_by_table[self.persist_selectable]) == 0 

1570 ): 

1571 raise sa_exc.ArgumentError( 

1572 "Mapper %s could not assemble any primary " 

1573 "key columns for mapped table '%s'" 

1574 % (self, self.persist_selectable.description) 

1575 ) 

1576 elif self.local_table not in self._pks_by_table and isinstance( 

1577 self.local_table, schema.Table 

1578 ): 

1579 util.warn( 

1580 "Could not assemble any primary " 

1581 "keys for locally mapped table '%s' - " 

1582 "no rows will be persisted in this Table." 

1583 % self.local_table.description 

1584 ) 

1585 

1586 if ( 

1587 self.inherits 

1588 and not self.concrete 

1589 and not self._primary_key_argument 

1590 ): 

1591 # if inheriting, the "primary key" for this mapper is 

1592 # that of the inheriting (unless concrete or explicit) 

1593 self.primary_key = self.inherits.primary_key 

1594 else: 

1595 # determine primary key from argument or persist_selectable pks 

1596 primary_key: Collection[ColumnElement[Any]] 

1597 

1598 if coerced_pk_arg: 

1599 primary_key = [ 

1600 cc if cc is not None else c 

1601 for cc, c in ( 

1602 (self.persist_selectable.corresponding_column(c), c) 

1603 for c in coerced_pk_arg 

1604 ) 

1605 ] 

1606 else: 

1607 # if heuristically determined PKs, reduce to the minimal set 

1608 # of columns by eliminating FK->PK pairs for a multi-table 

1609 # expression. May over-reduce for some kinds of UNIONs 

1610 # / CTEs; use explicit PK argument for these special cases 

1611 primary_key = sql_util.reduce_columns( 

1612 self._pks_by_table[self.persist_selectable], 

1613 ignore_nonexistent_tables=True, 

1614 ) 

1615 

1616 if len(primary_key) == 0: 

1617 raise sa_exc.ArgumentError( 

1618 "Mapper %s could not assemble any primary " 

1619 "key columns for mapped table '%s'" 

1620 % (self, self.persist_selectable.description) 

1621 ) 

1622 

1623 self.primary_key = tuple(primary_key) 

1624 self._log("Identified primary key columns: %s", primary_key) 

1625 

1626 # determine cols that aren't expressed within our tables; mark these 

1627 # as "read only" properties which are refreshed upon INSERT/UPDATE 

1628 self._readonly_props = { 

1629 self._columntoproperty[col] 

1630 for col in self._columntoproperty 

1631 if self._columntoproperty[col] not in self._identity_key_props 

1632 and ( 

1633 not hasattr(col, "table") 

1634 or col.table not in self._cols_by_table 

1635 ) 

1636 } 

1637 

1638 def _configure_properties(self) -> None: 

1639 self._columns = sql_base.WriteableColumnCollection() 

1640 

1641 # object attribute names mapped to MapperProperty objects 

1642 self._props = util.OrderedDict() 

1643 

1644 # table columns mapped to MapperProperty 

1645 self._columntoproperty = _ColumnMapping(self) 

1646 

1647 explicit_col_props_by_column: Dict[ 

1648 KeyedColumnElement[Any], Tuple[str, ColumnProperty[Any]] 

1649 ] = {} 

1650 explicit_col_props_by_key: Dict[str, ColumnProperty[Any]] = {} 

1651 

1652 # step 1: go through properties that were explicitly passed 

1653 # in the properties dictionary. For Columns that are local, put them 

1654 # aside in a separate collection we will reconcile with the Table 

1655 # that's given. For other properties, set them up in _props now. 

1656 if self._init_properties: 

1657 for key, prop_arg in self._init_properties.items(): 

1658 if not isinstance(prop_arg, MapperProperty): 

1659 possible_col_prop = self._make_prop_from_column( 

1660 key, prop_arg 

1661 ) 

1662 else: 

1663 possible_col_prop = prop_arg 

1664 

1665 # issue #8705. if the explicit property is actually a 

1666 # Column that is local to the local Table, don't set it up 

1667 # in ._props yet, integrate it into the order given within 

1668 # the Table. 

1669 

1670 _map_as_property_now = True 

1671 if isinstance(possible_col_prop, properties.ColumnProperty): 

1672 for given_col in possible_col_prop.columns: 

1673 if self.local_table.c.contains_column(given_col): 

1674 _map_as_property_now = False 

1675 explicit_col_props_by_key[key] = possible_col_prop 

1676 explicit_col_props_by_column[given_col] = ( 

1677 key, 

1678 possible_col_prop, 

1679 ) 

1680 

1681 if _map_as_property_now: 

1682 self._configure_property( 

1683 key, 

1684 possible_col_prop, 

1685 init=False, 

1686 ) 

1687 

1688 # step 2: pull properties from the inherited mapper. reconcile 

1689 # columns with those which are explicit above. for properties that 

1690 # are only in the inheriting mapper, set them up as local props 

1691 if self.inherits: 

1692 for key, inherited_prop in self.inherits._props.items(): 

1693 if self._should_exclude(key, key, local=False, column=None): 

1694 continue 

1695 

1696 incoming_prop = explicit_col_props_by_key.get(key) 

1697 if incoming_prop: 

1698 new_prop = self._reconcile_prop_with_incoming_columns( 

1699 key, 

1700 inherited_prop, 

1701 warn_only=False, 

1702 incoming_prop=incoming_prop, 

1703 ) 

1704 explicit_col_props_by_key[key] = new_prop 

1705 

1706 for inc_col in incoming_prop.columns: 

1707 explicit_col_props_by_column[inc_col] = ( 

1708 key, 

1709 new_prop, 

1710 ) 

1711 elif key not in self._props: 

1712 self._adapt_inherited_property(key, inherited_prop, False) 

1713 

1714 # step 3. Iterate through all columns in the persist selectable. 

1715 # this includes not only columns in the local table / fromclause, 

1716 # but also those columns in the superclass table if we are joined 

1717 # inh or single inh mapper. map these columns as well. additional 

1718 # reconciliation against inherited columns occurs here also. 

1719 

1720 for column in self.persist_selectable.columns: 

1721 if column in explicit_col_props_by_column: 

1722 # column was explicitly passed to properties; configure 

1723 # it now in the order in which it corresponds to the 

1724 # Table / selectable 

1725 key, prop = explicit_col_props_by_column[column] 

1726 self._configure_property(key, prop, init=False) 

1727 continue 

1728 

1729 elif column in self._columntoproperty: 

1730 continue 

1731 

1732 column_key = (self.column_prefix or "") + column.key 

1733 if self._should_exclude( 

1734 column.key, 

1735 column_key, 

1736 local=self.local_table.c.contains_column(column), 

1737 column=column, 

1738 ): 

1739 continue 

1740 

1741 # adjust the "key" used for this column to that 

1742 # of the inheriting mapper 

1743 for mapper in self.iterate_to_root(): 

1744 if column in mapper._columntoproperty: 

1745 column_key = mapper._columntoproperty[column].key 

1746 

1747 self._configure_property( 

1748 column_key, 

1749 column, 

1750 init=False, 

1751 setparent=True, 

1752 ) 

1753 

1754 def _configure_polymorphic_setter(self, init=False): 

1755 """Configure an attribute on the mapper representing the 

1756 'polymorphic_on' column, if applicable, and not 

1757 already generated by _configure_properties (which is typical). 

1758 

1759 Also create a setter function which will assign this 

1760 attribute to the value of the 'polymorphic_identity' 

1761 upon instance construction, also if applicable. This 

1762 routine will run when an instance is created. 

1763 

1764 """ 

1765 setter = False 

1766 polymorphic_key: Optional[str] = None 

1767 

1768 if self.polymorphic_on is not None: 

1769 setter = True 

1770 

1771 if isinstance(self.polymorphic_on, str): 

1772 # polymorphic_on specified as a string - link 

1773 # it to mapped ColumnProperty 

1774 try: 

1775 self.polymorphic_on = self._props[self.polymorphic_on] 

1776 except KeyError as err: 

1777 raise sa_exc.ArgumentError( 

1778 "Can't determine polymorphic_on " 

1779 "value '%s' - no attribute is " 

1780 "mapped to this name." % self.polymorphic_on 

1781 ) from err 

1782 

1783 if self.polymorphic_on in self._columntoproperty: 

1784 # polymorphic_on is a column that is already mapped 

1785 # to a ColumnProperty 

1786 prop = self._columntoproperty[self.polymorphic_on] 

1787 elif isinstance(self.polymorphic_on, MapperProperty): 

1788 # polymorphic_on is directly a MapperProperty, 

1789 # ensure it's a ColumnProperty 

1790 if not isinstance( 

1791 self.polymorphic_on, properties.ColumnProperty 

1792 ): 

1793 raise sa_exc.ArgumentError( 

1794 "Only direct column-mapped " 

1795 "property or SQL expression " 

1796 "can be passed for polymorphic_on" 

1797 ) 

1798 prop = self.polymorphic_on 

1799 else: 

1800 # polymorphic_on is a Column or SQL expression and 

1801 # doesn't appear to be mapped. this means it can be 1. 

1802 # only present in the with_polymorphic selectable or 

1803 # 2. a totally standalone SQL expression which we'd 

1804 # hope is compatible with this mapper's persist_selectable 

1805 col = self.persist_selectable.corresponding_column( 

1806 self.polymorphic_on 

1807 ) 

1808 if col is None: 

1809 # polymorphic_on doesn't derive from any 

1810 # column/expression isn't present in the mapped 

1811 # table. we will make a "hidden" ColumnProperty 

1812 # for it. Just check that if it's directly a 

1813 # schema.Column and we have with_polymorphic, it's 

1814 # likely a user error if the schema.Column isn't 

1815 # represented somehow in either persist_selectable or 

1816 # with_polymorphic. Otherwise as of 0.7.4 we 

1817 # just go with it and assume the user wants it 

1818 # that way (i.e. a CASE statement) 

1819 setter = False 

1820 instrument = False 

1821 col = self.polymorphic_on 

1822 if isinstance(col, schema.Column) and ( 

1823 self.with_polymorphic is None 

1824 or self.with_polymorphic[1] is None 

1825 or self.with_polymorphic[1].corresponding_column(col) 

1826 is None 

1827 ): 

1828 raise sa_exc.InvalidRequestError( 

1829 "Could not map polymorphic_on column " 

1830 "'%s' to the mapped table - polymorphic " 

1831 "loads will not function properly" 

1832 % col.description 

1833 ) 

1834 else: 

1835 # column/expression that polymorphic_on derives from 

1836 # is present in our mapped table 

1837 # and is probably mapped, but polymorphic_on itself 

1838 # is not. This happens when 

1839 # the polymorphic_on is only directly present in the 

1840 # with_polymorphic selectable, as when use 

1841 # polymorphic_union. 

1842 # we'll make a separate ColumnProperty for it. 

1843 instrument = True 

1844 key = getattr(col, "key", None) 

1845 if key: 

1846 if self._should_exclude(key, key, False, col): 

1847 raise sa_exc.InvalidRequestError( 

1848 "Cannot exclude or override the " 

1849 "discriminator column %r" % key 

1850 ) 

1851 else: 

1852 self.polymorphic_on = col = col.label("_sa_polymorphic_on") 

1853 key = col.key 

1854 

1855 prop = properties.ColumnProperty(col, _instrument=instrument) 

1856 self._configure_property(key, prop, init=init, setparent=True) 

1857 

1858 # the actual polymorphic_on should be the first public-facing 

1859 # column in the property 

1860 self.polymorphic_on = prop.columns[0] 

1861 polymorphic_key = prop.key 

1862 else: 

1863 # no polymorphic_on was set. 

1864 # check inheriting mappers for one. 

1865 for mapper in self.iterate_to_root(): 

1866 # determine if polymorphic_on of the parent 

1867 # should be propagated here. If the col 

1868 # is present in our mapped table, or if our mapped 

1869 # table is the same as the parent (i.e. single table 

1870 # inheritance), we can use it 

1871 if mapper.polymorphic_on is not None: 

1872 if self.persist_selectable is mapper.persist_selectable: 

1873 self.polymorphic_on = mapper.polymorphic_on 

1874 else: 

1875 self.polymorphic_on = ( 

1876 self.persist_selectable 

1877 ).corresponding_column(mapper.polymorphic_on) 

1878 # we can use the parent mapper's _set_polymorphic_identity 

1879 # directly; it ensures the polymorphic_identity of the 

1880 # instance's mapper is used so is portable to subclasses. 

1881 if self.polymorphic_on is not None: 

1882 self._set_polymorphic_identity = ( 

1883 mapper._set_polymorphic_identity 

1884 ) 

1885 self._polymorphic_attr_key = ( 

1886 mapper._polymorphic_attr_key 

1887 ) 

1888 self._validate_polymorphic_identity = ( 

1889 mapper._validate_polymorphic_identity 

1890 ) 

1891 else: 

1892 self._set_polymorphic_identity = None 

1893 self._polymorphic_attr_key = None 

1894 return 

1895 

1896 if self.polymorphic_abstract and self.polymorphic_on is None: 

1897 raise sa_exc.InvalidRequestError( 

1898 "The Mapper.polymorphic_abstract parameter may only be used " 

1899 "on a mapper hierarchy which includes the " 

1900 "Mapper.polymorphic_on parameter at the base of the hierarchy." 

1901 ) 

1902 

1903 if setter: 

1904 

1905 def _set_polymorphic_identity(state): 

1906 dict_ = state.dict 

1907 # TODO: what happens if polymorphic_on column attribute name 

1908 # does not match .key? 

1909 

1910 polymorphic_identity = ( 

1911 state.manager.mapper.polymorphic_identity 

1912 ) 

1913 if ( 

1914 polymorphic_identity is None 

1915 and state.manager.mapper.polymorphic_abstract 

1916 ): 

1917 raise sa_exc.InvalidRequestError( 

1918 f"Can't instantiate class for {state.manager.mapper}; " 

1919 "mapper is marked polymorphic_abstract=True" 

1920 ) 

1921 

1922 state.get_impl(polymorphic_key).set( 

1923 state, 

1924 dict_, 

1925 polymorphic_identity, 

1926 None, 

1927 ) 

1928 

1929 self._polymorphic_attr_key = polymorphic_key 

1930 

1931 def _validate_polymorphic_identity(mapper, state, dict_): 

1932 if ( 

1933 polymorphic_key in dict_ 

1934 and dict_[polymorphic_key] 

1935 not in mapper._acceptable_polymorphic_identities 

1936 ): 

1937 util.warn_limited( 

1938 "Flushing object %s with " 

1939 "incompatible polymorphic identity %r; the " 

1940 "object may not refresh and/or load correctly", 

1941 (state_str(state), dict_[polymorphic_key]), 

1942 ) 

1943 

1944 self._set_polymorphic_identity = _set_polymorphic_identity 

1945 self._validate_polymorphic_identity = ( 

1946 _validate_polymorphic_identity 

1947 ) 

1948 else: 

1949 self._polymorphic_attr_key = None 

1950 self._set_polymorphic_identity = None 

1951 

1952 _validate_polymorphic_identity = None 

1953 

1954 @HasMemoized.memoized_attribute 

1955 def _version_id_prop(self): 

1956 if self.version_id_col is not None: 

1957 return self._columntoproperty[self.version_id_col] 

1958 else: 

1959 return None 

1960 

1961 @HasMemoized.memoized_attribute 

1962 def _acceptable_polymorphic_identities(self): 

1963 identities = set() 

1964 

1965 stack = deque([self]) 

1966 while stack: 

1967 item = stack.popleft() 

1968 if item.persist_selectable is self.persist_selectable: 

1969 identities.add(item.polymorphic_identity) 

1970 stack.extend(item._inheriting_mappers) 

1971 

1972 return identities 

1973 

1974 @HasMemoized.memoized_attribute 

1975 def _prop_set(self): 

1976 return frozenset(self._props.values()) 

1977 

1978 @util.preload_module("sqlalchemy.orm.descriptor_props") 

1979 def _adapt_inherited_property(self, key, prop, init): 

1980 descriptor_props = util.preloaded.orm_descriptor_props 

1981 

1982 if not self.concrete: 

1983 self._configure_property(key, prop, init=False, setparent=False) 

1984 elif key not in self._props: 

1985 # determine if the class implements this attribute; if not, 

1986 # or if it is implemented by the attribute that is handling the 

1987 # given superclass-mapped property, then we need to report that we 

1988 # can't use this at the instance level since we are a concrete 

1989 # mapper and we don't map this. don't trip user-defined 

1990 # descriptors that might have side effects when invoked. 

1991 implementing_attribute = self.class_manager._get_class_attr_mro( 

1992 key, prop 

1993 ) 

1994 if implementing_attribute is prop or ( 

1995 isinstance( 

1996 implementing_attribute, attributes.InstrumentedAttribute 

1997 ) 

1998 and implementing_attribute._parententity is prop.parent 

1999 ): 

2000 self._configure_property( 

2001 key, 

2002 descriptor_props.ConcreteInheritedProperty(), 

2003 init=init, 

2004 setparent=True, 

2005 ) 

2006 

2007 @util.preload_module("sqlalchemy.orm.descriptor_props") 

2008 def _configure_property( 

2009 self, 

2010 key: str, 

2011 prop_arg: Union[KeyedColumnElement[Any], MapperProperty[Any]], 

2012 *, 

2013 init: bool = True, 

2014 setparent: bool = True, 

2015 warn_for_existing: bool = False, 

2016 ) -> MapperProperty[Any]: 

2017 descriptor_props = util.preloaded.orm_descriptor_props 

2018 self._log( 

2019 "_configure_property(%s, %s)", key, prop_arg.__class__.__name__ 

2020 ) 

2021 

2022 # early setup mode - don't assign any props, only 

2023 # ensure a Column is turned into a ColumnProperty. 

2024 # see #12858 

2025 early_setup = not hasattr(self, "_props") 

2026 

2027 if not isinstance(prop_arg, MapperProperty): 

2028 prop: MapperProperty[Any] = self._property_from_column( 

2029 key, prop_arg, early_setup 

2030 ) 

2031 else: 

2032 prop = prop_arg 

2033 

2034 if early_setup: 

2035 return prop 

2036 

2037 if isinstance(prop, properties.ColumnProperty): 

2038 col = self.persist_selectable.corresponding_column(prop.columns[0]) 

2039 

2040 # if the column is not present in the mapped table, 

2041 # test if a column has been added after the fact to the 

2042 # parent table (or their parent, etc.) [ticket:1570] 

2043 if col is None and self.inherits: 

2044 path = [self] 

2045 for m in self.inherits.iterate_to_root(): 

2046 col = m.local_table.corresponding_column(prop.columns[0]) 

2047 if col is not None: 

2048 for m2 in path: 

2049 m2.persist_selectable._refresh_for_new_column(col) 

2050 col = self.persist_selectable.corresponding_column( 

2051 prop.columns[0] 

2052 ) 

2053 break 

2054 path.append(m) 

2055 

2056 # subquery expression, column not present in the mapped 

2057 # selectable. 

2058 if col is None: 

2059 col = prop.columns[0] 

2060 

2061 # column is coming in after _readonly_props was 

2062 # initialized; check for 'readonly' 

2063 if hasattr(self, "_readonly_props") and ( 

2064 not hasattr(col, "table") 

2065 or col.table not in self._cols_by_table 

2066 ): 

2067 self._readonly_props.add(prop) 

2068 

2069 else: 

2070 # if column is coming in after _cols_by_table was 

2071 # initialized, ensure the col is in the right set 

2072 if ( 

2073 hasattr(self, "_cols_by_table") 

2074 and col.table in self._cols_by_table 

2075 and col not in self._cols_by_table[col.table] 

2076 ): 

2077 self._cols_by_table[col.table].add(col) 

2078 

2079 # if this properties.ColumnProperty represents the "polymorphic 

2080 # discriminator" column, mark it. We'll need this when rendering 

2081 # columns in SELECT statements. 

2082 if not hasattr(prop, "_is_polymorphic_discriminator"): 

2083 prop._is_polymorphic_discriminator = ( 

2084 col is self.polymorphic_on 

2085 or prop.columns[0] is self.polymorphic_on 

2086 ) 

2087 

2088 if isinstance(col, expression.Label): 

2089 # new in 1.4, get column property against expressions 

2090 # to be addressable in subqueries 

2091 col.key = col._tq_key_label = key 

2092 

2093 self._columns.add(col, key) 

2094 

2095 for col in prop.columns: 

2096 for proxy_col in col.proxy_set: 

2097 self._columntoproperty[proxy_col] = prop 

2098 

2099 if getattr(prop, "key", key) != key: 

2100 util.warn( 

2101 f"ORM mapped property {self.class_.__name__}.{prop.key} being " 

2102 "assigned to attribute " 

2103 f"{key!r} is already associated with " 

2104 f"attribute {prop.key!r}. The attribute will be de-associated " 

2105 f"from {prop.key!r}." 

2106 ) 

2107 

2108 prop.key = key 

2109 

2110 if setparent: 

2111 prop.set_parent(self, init) 

2112 

2113 if key in self._props and getattr( 

2114 self._props[key], "_mapped_by_synonym", False 

2115 ): 

2116 syn = self._props[key]._mapped_by_synonym 

2117 raise sa_exc.ArgumentError( 

2118 "Can't call map_column=True for synonym %r=%r, " 

2119 "a ColumnProperty already exists keyed to the name " 

2120 "%r for column %r" % (syn, key, key, syn) 

2121 ) 

2122 

2123 # replacement cases 

2124 

2125 # case one: prop is replacing a prop that we have mapped. this is 

2126 # independent of whatever might be in the actual class dictionary 

2127 if ( 

2128 key in self._props 

2129 and not isinstance( 

2130 self._props[key], descriptor_props.ConcreteInheritedProperty 

2131 ) 

2132 and not isinstance(prop, descriptor_props.SynonymProperty) 

2133 ): 

2134 if warn_for_existing: 

2135 util.warn_deprecated( 

2136 f"User-placed attribute {self.class_.__name__}.{key} on " 

2137 f"{self} is replacing an existing ORM-mapped attribute. " 

2138 "Behavior is not fully defined in this case. This " 

2139 "use is deprecated and will raise an error in a future " 

2140 "release", 

2141 "2.0", 

2142 ) 

2143 oldprop = self._props[key] 

2144 self._path_registry.pop(oldprop, None) 

2145 

2146 # case two: prop is replacing an attribute on the class of some kind. 

2147 # we have to be more careful here since it's normal when using 

2148 # Declarative that all the "declared attributes" on the class 

2149 # get replaced. 

2150 elif ( 

2151 warn_for_existing 

2152 and self.class_.__dict__.get(key, None) is not None 

2153 and not isinstance(prop, descriptor_props.SynonymProperty) 

2154 and not isinstance( 

2155 self._props.get(key, None), 

2156 descriptor_props.ConcreteInheritedProperty, 

2157 ) 

2158 ): 

2159 util.warn_deprecated( 

2160 f"User-placed attribute {self.class_.__name__}.{key} on " 

2161 f"{self} is replacing an existing class-bound " 

2162 "attribute of the same name. " 

2163 "Behavior is not fully defined in this case. This " 

2164 "use is deprecated and will raise an error in a future " 

2165 "release", 

2166 "2.0", 

2167 ) 

2168 

2169 self._props[key] = prop 

2170 

2171 prop.instrument_class(self) 

2172 

2173 for mapper in self._inheriting_mappers: 

2174 mapper._adapt_inherited_property(key, prop, init) 

2175 

2176 if init: 

2177 prop.init() 

2178 prop.post_instrument_class(self) 

2179 

2180 if self.configured: 

2181 self._expire_memoizations() 

2182 

2183 return prop 

2184 

2185 def _make_prop_from_column( 

2186 self, 

2187 key: str, 

2188 column: Union[ 

2189 Sequence[KeyedColumnElement[Any]], KeyedColumnElement[Any] 

2190 ], 

2191 ) -> ColumnProperty[Any]: 

2192 columns = util.to_list(column) 

2193 mapped_column = [] 

2194 for c in columns: 

2195 mc = self.persist_selectable.corresponding_column(c) 

2196 if mc is None: 

2197 mc = self.local_table.corresponding_column(c) 

2198 if mc is not None: 

2199 # if the column is in the local table but not the 

2200 # mapped table, this corresponds to adding a 

2201 # column after the fact to the local table. 

2202 # [ticket:1523] 

2203 self.persist_selectable._refresh_for_new_column(mc) 

2204 mc = self.persist_selectable.corresponding_column(c) 

2205 if mc is None: 

2206 raise sa_exc.ArgumentError( 

2207 "When configuring property '%s' on %s, " 

2208 "column '%s' is not represented in the mapper's " 

2209 "table. Use the `column_property()` function to " 

2210 "force this column to be mapped as a read-only " 

2211 "attribute." % (key, self, c) 

2212 ) 

2213 mapped_column.append(mc) 

2214 return properties.ColumnProperty(*mapped_column) 

2215 

2216 def _reconcile_prop_with_incoming_columns( 

2217 self, 

2218 key: str, 

2219 existing_prop: MapperProperty[Any], 

2220 warn_only: bool, 

2221 incoming_prop: Optional[ColumnProperty[Any]] = None, 

2222 single_column: Optional[KeyedColumnElement[Any]] = None, 

2223 ) -> ColumnProperty[Any]: 

2224 if incoming_prop and ( 

2225 self.concrete 

2226 or not isinstance(existing_prop, properties.ColumnProperty) 

2227 ): 

2228 return incoming_prop 

2229 

2230 existing_column = existing_prop.columns[0] 

2231 

2232 if incoming_prop and existing_column in incoming_prop.columns: 

2233 return incoming_prop 

2234 

2235 if incoming_prop is None: 

2236 assert single_column is not None 

2237 incoming_column = single_column 

2238 equated_pair_key = (existing_prop.columns[0], incoming_column) 

2239 else: 

2240 assert single_column is None 

2241 incoming_column = incoming_prop.columns[0] 

2242 equated_pair_key = (incoming_column, existing_prop.columns[0]) 

2243 

2244 if ( 

2245 ( 

2246 not self._inherits_equated_pairs 

2247 or (equated_pair_key not in self._inherits_equated_pairs) 

2248 ) 

2249 and not existing_column.shares_lineage(incoming_column) 

2250 and existing_column is not self.version_id_col 

2251 and incoming_column is not self.version_id_col 

2252 ): 

2253 msg = ( 

2254 "Implicitly combining column %s with column " 

2255 "%s under attribute '%s'. Please configure one " 

2256 "or more attributes for these same-named columns " 

2257 "explicitly." 

2258 % ( 

2259 existing_prop.columns[-1], 

2260 incoming_column, 

2261 key, 

2262 ) 

2263 ) 

2264 if warn_only: 

2265 util.warn(msg) 

2266 else: 

2267 raise sa_exc.InvalidRequestError(msg) 

2268 

2269 # existing properties.ColumnProperty from an inheriting 

2270 # mapper. make a copy and append our column to it 

2271 new_prop = existing_prop.copy() 

2272 

2273 new_prop.columns.insert(0, incoming_column) 

2274 self._log( 

2275 "inserting column to existing list " 

2276 "in properties.ColumnProperty %s", 

2277 key, 

2278 ) 

2279 return new_prop # type: ignore 

2280 

2281 @util.preload_module("sqlalchemy.orm.descriptor_props") 

2282 def _property_from_column( 

2283 self, key: str, column: KeyedColumnElement[Any], early_setup: bool 

2284 ) -> ColumnProperty[Any]: 

2285 """generate/update a :class:`.ColumnProperty` given a 

2286 :class:`_schema.Column` or other SQL expression object.""" 

2287 

2288 descriptor_props = util.preloaded.orm_descriptor_props 

2289 

2290 if early_setup: 

2291 prop = None 

2292 else: 

2293 prop = self._props.get(key) 

2294 

2295 if isinstance(prop, properties.ColumnProperty): 

2296 return self._reconcile_prop_with_incoming_columns( 

2297 key, 

2298 prop, 

2299 single_column=column, 

2300 warn_only=prop.parent is not self, 

2301 ) 

2302 elif prop is None or isinstance( 

2303 prop, descriptor_props.ConcreteInheritedProperty 

2304 ): 

2305 return self._make_prop_from_column(key, column) 

2306 else: 

2307 raise sa_exc.ArgumentError( 

2308 "WARNING: when configuring property '%s' on %s, " 

2309 "column '%s' conflicts with property '%r'. " 

2310 "To resolve this, map the column to the class under a " 

2311 "different name in the 'properties' dictionary. Or, " 

2312 "to remove all awareness of the column entirely " 

2313 "(including its availability as a foreign key), " 

2314 "use the 'include_properties' or 'exclude_properties' " 

2315 "mapper arguments to control specifically which table " 

2316 "columns get mapped." % (key, self, column.key, prop) 

2317 ) 

2318 

2319 @util.langhelpers.tag_method_for_warnings( 

2320 "This warning originated from the `configure_mappers()` process, " 

2321 "which was invoked automatically in response to a user-initiated " 

2322 "operation.", 

2323 sa_exc.SAWarning, 

2324 ) 

2325 def _check_configure(self) -> None: 

2326 if self.registry._new_mappers: 

2327 _configure_registries({self.registry}, cascade=True) 

2328 

2329 def _post_configure_properties(self) -> None: 

2330 """Call the ``init()`` method on all ``MapperProperties`` 

2331 attached to this mapper. 

2332 

2333 This is a deferred configuration step which is intended 

2334 to execute once all mappers have been constructed. 

2335 

2336 """ 

2337 

2338 self._log("_post_configure_properties() started") 

2339 l = [(key, prop) for key, prop in self._props.items()] 

2340 for key, prop in l: 

2341 self._log("initialize prop %s", key) 

2342 

2343 if prop.parent is self and not prop._configure_started: 

2344 prop.init() 

2345 

2346 if prop._configure_finished: 

2347 prop.post_instrument_class(self) 

2348 

2349 self._log("_post_configure_properties() complete") 

2350 self.configured = True 

2351 

2352 def add_properties(self, dict_of_properties): 

2353 """Add the given dictionary of properties to this mapper, 

2354 using `add_property`. 

2355 

2356 """ 

2357 for key, value in dict_of_properties.items(): 

2358 self.add_property(key, value) 

2359 

2360 def add_property( 

2361 self, key: str, prop: Union[Column[Any], MapperProperty[Any]] 

2362 ) -> None: 

2363 """Add an individual MapperProperty to this mapper. 

2364 

2365 If the mapper has not been configured yet, just adds the 

2366 property to the initial properties dictionary sent to the 

2367 constructor. If this Mapper has already been configured, then 

2368 the given MapperProperty is configured immediately. 

2369 

2370 """ 

2371 prop = self._configure_property( 

2372 key, prop, init=self.configured, warn_for_existing=True 

2373 ) 

2374 assert isinstance(prop, MapperProperty) 

2375 self._init_properties[key] = prop 

2376 

2377 def _expire_memoizations(self) -> None: 

2378 for mapper in self.iterate_to_root(): 

2379 mapper._reset_memoizations() 

2380 

2381 @property 

2382 def _log_desc(self) -> str: 

2383 return ( 

2384 "(" 

2385 + self.class_.__name__ 

2386 + "|" 

2387 + ( 

2388 self.local_table is not None 

2389 and self.local_table.description 

2390 or str(self.local_table) 

2391 ) 

2392 + ")" 

2393 ) 

2394 

2395 def _log(self, msg: str, *args: Any) -> None: 

2396 self.logger.info("%s " + msg, *((self._log_desc,) + args)) 

2397 

2398 def _log_debug(self, msg: str, *args: Any) -> None: 

2399 self.logger.debug("%s " + msg, *((self._log_desc,) + args)) 

2400 

2401 def __repr__(self) -> str: 

2402 return "<Mapper at 0x%x; %s>" % (id(self), self.class_.__name__) 

2403 

2404 def __str__(self) -> str: 

2405 return "Mapper[%s(%s)]" % ( 

2406 self.class_.__name__, 

2407 ( 

2408 self.local_table.description 

2409 if self.local_table is not None 

2410 else self.persist_selectable.description 

2411 ), 

2412 ) 

2413 

2414 def _is_orphan(self, state: InstanceState[_O]) -> bool: 

2415 orphan_possible = False 

2416 for mapper in self.iterate_to_root(): 

2417 for key, cls in mapper._delete_orphans: 

2418 orphan_possible = True 

2419 

2420 has_parent = attributes.manager_of_class(cls).has_parent( 

2421 state, key, optimistic=state.has_identity 

2422 ) 

2423 

2424 if self.legacy_is_orphan and has_parent: 

2425 return False 

2426 elif not self.legacy_is_orphan and not has_parent: 

2427 return True 

2428 

2429 if self.legacy_is_orphan: 

2430 return orphan_possible 

2431 else: 

2432 return False 

2433 

2434 def has_property(self, key: str) -> bool: 

2435 return key in self._props 

2436 

2437 def get_property( 

2438 self, key: str, _configure_mappers: bool = False 

2439 ) -> MapperProperty[Any]: 

2440 """return a MapperProperty associated with the given key.""" 

2441 

2442 if _configure_mappers: 

2443 self._check_configure() 

2444 

2445 try: 

2446 return self._props[key] 

2447 except KeyError as err: 

2448 raise sa_exc.InvalidRequestError( 

2449 f"Mapper '{self}' has no property '{key}'. If this property " 

2450 "was indicated from other mappers or configure events, ensure " 

2451 "registry.configure() has been called." 

2452 ) from err 

2453 

2454 def get_property_by_column( 

2455 self, column: ColumnElement[_T] 

2456 ) -> MapperProperty[_T]: 

2457 """Given a :class:`_schema.Column` object, return the 

2458 :class:`.MapperProperty` which maps this column.""" 

2459 

2460 return self._columntoproperty[column] 

2461 

2462 @HasMemoized.memoized_attribute 

2463 def columns(self) -> ReadOnlyColumnCollection[str, Column[Any]]: 

2464 """A collection of :class:`_schema.Column` or other scalar expression 

2465 objects maintained by this :class:`_orm.Mapper`. 

2466 

2467 The collection behaves the same as that of the ``c`` attribute on any 

2468 :class:`_schema.Table` object, except that only those columns included 

2469 in this mapping are present, and are keyed based on the attribute name 

2470 defined in the mapping, not necessarily the ``key`` attribute of the 

2471 :class:`_schema.Column` itself. Additionally, scalar expressions 

2472 mapped by :func:`.column_property` are also present here. 

2473 

2474 This is a *read only* attribute determined during mapper construction. 

2475 Behavior is undefined if directly modified. 

2476 

2477 """ 

2478 return self._columns.as_readonly() 

2479 

2480 @HasMemoized.memoized_attribute 

2481 def c(self) -> ReadOnlyColumnCollection[str, Column[Any]]: 

2482 """A synonym for :attr:`_orm.Mapper.columns`.""" 

2483 return self._columns.as_readonly() 

2484 

2485 @property 

2486 def iterate_properties(self): 

2487 """return an iterator of all MapperProperty objects.""" 

2488 

2489 return iter(self._props.values()) 

2490 

2491 def _mappers_from_spec( 

2492 self, spec: Any, selectable: Optional[FromClause] 

2493 ) -> Sequence[Mapper[Any]]: 

2494 """given a with_polymorphic() argument, return the set of mappers it 

2495 represents. 

2496 

2497 Trims the list of mappers to just those represented within the given 

2498 selectable, if present. This helps some more legacy-ish mappings. 

2499 

2500 """ 

2501 if spec == "*": 

2502 mappers = list(self.self_and_descendants) 

2503 elif spec: 

2504 mapper_set: Set[Mapper[Any]] = set() 

2505 for m in util.to_list(spec): 

2506 m = _class_to_mapper(m) 

2507 if not m.isa(self): 

2508 raise sa_exc.InvalidRequestError( 

2509 "%r does not inherit from %r" % (m, self) 

2510 ) 

2511 

2512 if selectable is None: 

2513 mapper_set.update(m.iterate_to_root()) 

2514 else: 

2515 mapper_set.add(m) 

2516 mappers = [m for m in self.self_and_descendants if m in mapper_set] 

2517 else: 

2518 mappers = [] 

2519 

2520 if selectable is not None: 

2521 tables = set( 

2522 sql_util.find_tables(selectable, include_aliases=True) 

2523 ) 

2524 mappers = [m for m in mappers if m.local_table in tables] 

2525 return mappers 

2526 

2527 def _selectable_from_mappers( 

2528 self, mappers: Iterable[Mapper[Any]], innerjoin: bool 

2529 ) -> FromClause: 

2530 """given a list of mappers (assumed to be within this mapper's 

2531 inheritance hierarchy), construct an outerjoin amongst those mapper's 

2532 mapped tables. 

2533 

2534 """ 

2535 from_obj = self.persist_selectable 

2536 for m in mappers: 

2537 if m is self: 

2538 continue 

2539 if m.concrete: 

2540 raise sa_exc.InvalidRequestError( 

2541 "'with_polymorphic()' requires 'selectable' argument " 

2542 "when concrete-inheriting mappers are used." 

2543 ) 

2544 elif not m.single: 

2545 if innerjoin: 

2546 from_obj = from_obj.join( 

2547 m.local_table, m.inherit_condition 

2548 ) 

2549 else: 

2550 from_obj = from_obj.outerjoin( 

2551 m.local_table, m.inherit_condition 

2552 ) 

2553 

2554 return from_obj 

2555 

2556 @HasMemoized.memoized_attribute 

2557 def _version_id_has_server_side_value(self) -> bool: 

2558 vid_col = self.version_id_col 

2559 

2560 if vid_col is None: 

2561 return False 

2562 

2563 elif not isinstance(vid_col, Column): 

2564 return True 

2565 else: 

2566 return vid_col.server_default is not None or ( 

2567 vid_col.default is not None 

2568 and ( 

2569 not vid_col.default.is_scalar 

2570 and not vid_col.default.is_callable 

2571 ) 

2572 ) 

2573 

2574 @HasMemoized.memoized_attribute 

2575 def _single_table_criteria_component(self): 

2576 if self.single and self.inherits and self.polymorphic_on is not None: 

2577 

2578 hierarchy = tuple( 

2579 m.polymorphic_identity 

2580 for m in self.self_and_descendants 

2581 if not m.polymorphic_abstract 

2582 ) 

2583 

2584 return ( 

2585 self.polymorphic_on._annotate( 

2586 {"parententity": self, "parentmapper": self} 

2587 ), 

2588 hierarchy, 

2589 ) 

2590 else: 

2591 return None 

2592 

2593 @HasMemoized.memoized_attribute 

2594 def _single_table_criterion(self): 

2595 component = self._single_table_criteria_component 

2596 if component is not None: 

2597 return component[0].in_(component[1]) 

2598 else: 

2599 return None 

2600 

2601 @HasMemoized.memoized_attribute 

2602 def _has_aliased_polymorphic_fromclause(self): 

2603 """return True if with_polymorphic[1] is an aliased fromclause, 

2604 like a subquery. 

2605 

2606 As of #8168, polymorphic adaption with ORMAdapter is used only 

2607 if this is present. 

2608 

2609 """ 

2610 return self.with_polymorphic and isinstance( 

2611 self.with_polymorphic[1], 

2612 expression.AliasedReturnsRows, 

2613 ) 

2614 

2615 @HasMemoized.memoized_attribute 

2616 def _should_select_with_poly_adapter(self): 

2617 """determine if _MapperEntity or _ORMColumnEntity will need to use 

2618 polymorphic adaption when setting up a SELECT as well as fetching 

2619 rows for mapped classes and subclasses against this Mapper. 

2620 

2621 moved here from context.py for #8456 to generalize the ruleset 

2622 for this condition. 

2623 

2624 """ 

2625 

2626 # this has been simplified as of #8456. 

2627 # rule is: if we have a with_polymorphic or a concrete-style 

2628 # polymorphic selectable, *or* if the base mapper has either of those, 

2629 # we turn on the adaption thing. if not, we do *no* adaption. 

2630 # 

2631 # (UPDATE for #8168: the above comment was not accurate, as we were 

2632 # still saying "do polymorphic" if we were using an auto-generated 

2633 # flattened JOIN for with_polymorphic.) 

2634 # 

2635 # this splits the behavior among the "regular" joined inheritance 

2636 # and single inheritance mappers, vs. the "weird / difficult" 

2637 # concrete and joined inh mappings that use a with_polymorphic of 

2638 # some kind or polymorphic_union. 

2639 # 

2640 # note we have some tests in test_polymorphic_rel that query against 

2641 # a subclass, then refer to the superclass that has a with_polymorphic 

2642 # on it (such as test_join_from_polymorphic_explicit_aliased_three). 

2643 # these tests actually adapt the polymorphic selectable (like, the 

2644 # UNION or the SELECT subquery with JOIN in it) to be just the simple 

2645 # subclass table. Hence even if we are a "plain" inheriting mapper 

2646 # but our base has a wpoly on it, we turn on adaption. This is a 

2647 # legacy case we should probably disable. 

2648 # 

2649 # 

2650 # UPDATE: simplified way more as of #8168. polymorphic adaption 

2651 # is turned off even if with_polymorphic is set, as long as there 

2652 # is no user-defined aliased selectable / subquery configured. 

2653 # this scales back the use of polymorphic adaption in practice 

2654 # to basically no cases except for concrete inheritance with a 

2655 # polymorphic base class. 

2656 # 

2657 return ( 

2658 self._has_aliased_polymorphic_fromclause 

2659 or self._requires_row_aliasing 

2660 or (self.base_mapper._has_aliased_polymorphic_fromclause) 

2661 or self.base_mapper._requires_row_aliasing 

2662 ) 

2663 

2664 @HasMemoized.memoized_attribute 

2665 def _with_polymorphic_mappers(self) -> Sequence[Mapper[Any]]: 

2666 self._check_configure() 

2667 

2668 if not self.with_polymorphic: 

2669 return [] 

2670 return self._mappers_from_spec(*self.with_polymorphic) 

2671 

2672 @HasMemoized.memoized_attribute 

2673 def _post_inspect(self): 

2674 """This hook is invoked by attribute inspection. 

2675 

2676 E.g. when Query calls: 

2677 

2678 coercions.expect(roles.ColumnsClauseRole, ent, keep_inspect=True) 

2679 

2680 This allows the inspection process run a configure mappers hook. 

2681 

2682 """ 

2683 self._check_configure() 

2684 

2685 @HasMemoized_ro_memoized_attribute 

2686 def _with_polymorphic_selectable(self) -> FromClause: 

2687 if not self.with_polymorphic: 

2688 return self.persist_selectable 

2689 

2690 spec, selectable = self.with_polymorphic 

2691 if selectable is not None: 

2692 return selectable 

2693 else: 

2694 return self._selectable_from_mappers( 

2695 self._mappers_from_spec(spec, selectable), False 

2696 ) 

2697 

2698 with_polymorphic_mappers = _with_polymorphic_mappers 

2699 """The list of :class:`_orm.Mapper` objects included in the 

2700 default "polymorphic" query. 

2701 

2702 """ 

2703 

2704 @HasMemoized_ro_memoized_attribute 

2705 def _insert_cols_evaluating_none(self): 

2706 return { 

2707 table: frozenset( 

2708 col for col in columns if col.type.should_evaluate_none 

2709 ) 

2710 for table, columns in self._cols_by_table.items() 

2711 } 

2712 

2713 @HasMemoized.memoized_attribute 

2714 def _insert_cols_as_none(self): 

2715 return { 

2716 table: frozenset( 

2717 col.key 

2718 for col in columns 

2719 if not col.primary_key 

2720 and not col.server_default 

2721 and not col.default 

2722 and not col.type.should_evaluate_none 

2723 ) 

2724 for table, columns in self._cols_by_table.items() 

2725 } 

2726 

2727 @HasMemoized.memoized_attribute 

2728 def _propkey_to_col(self): 

2729 return { 

2730 table: {self._columntoproperty[col].key: col for col in columns} 

2731 for table, columns in self._cols_by_table.items() 

2732 } 

2733 

2734 @HasMemoized.memoized_attribute 

2735 def _pk_keys_by_table(self): 

2736 return { 

2737 table: frozenset([col.key for col in pks]) 

2738 for table, pks in self._pks_by_table.items() 

2739 } 

2740 

2741 @HasMemoized.memoized_attribute 

2742 def _pk_attr_keys_by_table(self): 

2743 return { 

2744 table: frozenset([self._columntoproperty[col].key for col in pks]) 

2745 for table, pks in self._pks_by_table.items() 

2746 } 

2747 

2748 @HasMemoized.memoized_attribute 

2749 def _server_default_cols( 

2750 self, 

2751 ) -> Mapping[FromClause, FrozenSet[Column[Any]]]: 

2752 return { 

2753 table: frozenset( 

2754 [ 

2755 col 

2756 for col in cast("Iterable[Column[Any]]", columns) 

2757 if col.server_default is not None 

2758 or ( 

2759 col.default is not None 

2760 and col.default.is_clause_element 

2761 ) 

2762 ] 

2763 ) 

2764 for table, columns in self._cols_by_table.items() 

2765 } 

2766 

2767 @HasMemoized.memoized_attribute 

2768 def _server_onupdate_default_cols( 

2769 self, 

2770 ) -> Mapping[FromClause, FrozenSet[Column[Any]]]: 

2771 return { 

2772 table: frozenset( 

2773 [ 

2774 col 

2775 for col in cast("Iterable[Column[Any]]", columns) 

2776 if col.server_onupdate is not None 

2777 or ( 

2778 col.onupdate is not None 

2779 and col.onupdate.is_clause_element 

2780 ) 

2781 ] 

2782 ) 

2783 for table, columns in self._cols_by_table.items() 

2784 } 

2785 

2786 @HasMemoized.memoized_attribute 

2787 def _server_default_col_keys(self) -> Mapping[FromClause, FrozenSet[str]]: 

2788 return { 

2789 table: frozenset(col.key for col in cols if col.key is not None) 

2790 for table, cols in self._server_default_cols.items() 

2791 } 

2792 

2793 @HasMemoized.memoized_attribute 

2794 def _server_onupdate_default_col_keys( 

2795 self, 

2796 ) -> Mapping[FromClause, FrozenSet[str]]: 

2797 return { 

2798 table: frozenset(col.key for col in cols if col.key is not None) 

2799 for table, cols in self._server_onupdate_default_cols.items() 

2800 } 

2801 

2802 @HasMemoized.memoized_attribute 

2803 def _server_default_plus_onupdate_propkeys(self) -> Set[str]: 

2804 result: Set[str] = set() 

2805 

2806 col_to_property = self._columntoproperty 

2807 for table, columns in self._server_default_cols.items(): 

2808 result.update( 

2809 col_to_property[col].key 

2810 for col in columns.intersection(col_to_property) 

2811 ) 

2812 for table, columns in self._server_onupdate_default_cols.items(): 

2813 result.update( 

2814 col_to_property[col].key 

2815 for col in columns.intersection(col_to_property) 

2816 ) 

2817 return result 

2818 

2819 @HasMemoized.memoized_instancemethod 

2820 def __clause_element__(self): 

2821 annotations: Dict[str, Any] = { 

2822 "entity_namespace": self, 

2823 "parententity": self, 

2824 "parentmapper": self, 

2825 } 

2826 if self.persist_selectable is not self.local_table: 

2827 # joined table inheritance, with polymorphic selectable, 

2828 # etc. 

2829 annotations["dml_table"] = self.local_table._annotate( 

2830 { 

2831 "entity_namespace": self, 

2832 "parententity": self, 

2833 "parentmapper": self, 

2834 } 

2835 )._set_propagate_attrs( 

2836 {"compile_state_plugin": "orm", "plugin_subject": self} 

2837 ) 

2838 

2839 return self.selectable._annotate(annotations)._set_propagate_attrs( 

2840 {"compile_state_plugin": "orm", "plugin_subject": self} 

2841 ) 

2842 

2843 @util.memoized_property 

2844 def select_identity_token(self): 

2845 return ( 

2846 expression.null() 

2847 ._annotate( 

2848 { 

2849 "entity_namespace": self, 

2850 "parententity": self, 

2851 "parentmapper": self, 

2852 "identity_token": True, 

2853 } 

2854 ) 

2855 ._set_propagate_attrs( 

2856 {"compile_state_plugin": "orm", "plugin_subject": self} 

2857 ) 

2858 ) 

2859 

2860 @property 

2861 def selectable(self) -> FromClause: 

2862 """The :class:`_schema.FromClause` construct this 

2863 :class:`_orm.Mapper` selects from by default. 

2864 

2865 Normally, this is equivalent to :attr:`.persist_selectable`, unless 

2866 the ``with_polymorphic`` feature is in use, in which case the 

2867 full "polymorphic" selectable is returned. 

2868 

2869 """ 

2870 return self._with_polymorphic_selectable 

2871 

2872 def _with_polymorphic_args( 

2873 self, 

2874 spec: Any = None, 

2875 selectable: Union[Literal[False, None], FromClause] = False, 

2876 innerjoin: bool = False, 

2877 ) -> Tuple[Sequence[Mapper[Any]], FromClause]: 

2878 if selectable not in (None, False): 

2879 selectable = coercions.expect( 

2880 roles.FromClauseRole, 

2881 selectable, 

2882 ) 

2883 

2884 if self.with_polymorphic: 

2885 if not spec: 

2886 spec = self.with_polymorphic[0] 

2887 if selectable is False: 

2888 selectable = self.with_polymorphic[1] 

2889 elif selectable is False: 

2890 selectable = None 

2891 mappers = self._mappers_from_spec(spec, selectable) 

2892 if selectable is not None: 

2893 return mappers, selectable 

2894 else: 

2895 return mappers, self._selectable_from_mappers(mappers, innerjoin) 

2896 

2897 @HasMemoized.memoized_attribute 

2898 def _polymorphic_properties(self): 

2899 return list( 

2900 self._iterate_polymorphic_properties( 

2901 self._with_polymorphic_mappers 

2902 ) 

2903 ) 

2904 

2905 @property 

2906 def _all_column_expressions(self): 

2907 poly_properties = self._polymorphic_properties 

2908 adapter = self._polymorphic_adapter 

2909 

2910 return [ 

2911 adapter.columns[c] if adapter else c 

2912 for prop in poly_properties 

2913 if isinstance(prop, properties.ColumnProperty) 

2914 and prop._renders_in_subqueries 

2915 for c in prop.columns 

2916 ] 

2917 

2918 def _columns_plus_keys(self, polymorphic_mappers=()): 

2919 if polymorphic_mappers: 

2920 poly_properties = self._iterate_polymorphic_properties( 

2921 polymorphic_mappers 

2922 ) 

2923 else: 

2924 poly_properties = self._polymorphic_properties 

2925 

2926 return [ 

2927 (prop.key, prop.columns[0]) 

2928 for prop in poly_properties 

2929 if isinstance(prop, properties.ColumnProperty) 

2930 ] 

2931 

2932 @HasMemoized.memoized_attribute 

2933 def _polymorphic_adapter(self) -> Optional[orm_util.ORMAdapter]: 

2934 if self._has_aliased_polymorphic_fromclause: 

2935 return orm_util.ORMAdapter( 

2936 orm_util._TraceAdaptRole.MAPPER_POLYMORPHIC_ADAPTER, 

2937 self, 

2938 selectable=self.selectable, 

2939 equivalents=self._equivalent_columns, 

2940 limit_on_entity=False, 

2941 ) 

2942 else: 

2943 return None 

2944 

2945 def _iterate_polymorphic_properties(self, mappers=None): 

2946 """Return an iterator of MapperProperty objects which will render into 

2947 a SELECT.""" 

2948 if mappers is None: 

2949 mappers = self._with_polymorphic_mappers 

2950 

2951 if not mappers: 

2952 for c in self.iterate_properties: 

2953 yield c 

2954 else: 

2955 # in the polymorphic case, filter out discriminator columns 

2956 # from other mappers, as these are sometimes dependent on that 

2957 # mapper's polymorphic selectable (which we don't want rendered) 

2958 for c in util.unique_list( 

2959 chain( 

2960 *[ 

2961 list(mapper.iterate_properties) 

2962 for mapper in [self] + mappers 

2963 ] 

2964 ) 

2965 ): 

2966 if getattr(c, "_is_polymorphic_discriminator", False) and ( 

2967 self.polymorphic_on is None 

2968 or c.columns[0] is not self.polymorphic_on 

2969 ): 

2970 continue 

2971 yield c 

2972 

2973 @HasMemoized.memoized_attribute 

2974 def attrs(self) -> util.ReadOnlyProperties[MapperProperty[Any]]: 

2975 """A namespace of all :class:`.MapperProperty` objects 

2976 associated this mapper. 

2977 

2978 This is an object that provides each property based on 

2979 its key name. For instance, the mapper for a 

2980 ``User`` class which has ``User.name`` attribute would 

2981 provide ``mapper.attrs.name``, which would be the 

2982 :class:`.ColumnProperty` representing the ``name`` 

2983 column. The namespace object can also be iterated, 

2984 which would yield each :class:`.MapperProperty`. 

2985 

2986 :class:`_orm.Mapper` has several pre-filtered views 

2987 of this attribute which limit the types of properties 

2988 returned, including :attr:`.synonyms`, :attr:`.column_attrs`, 

2989 :attr:`.relationships`, and :attr:`.composites`. 

2990 

2991 .. warning:: 

2992 

2993 The :attr:`_orm.Mapper.attrs` accessor namespace is an 

2994 instance of :class:`.OrderedProperties`. This is 

2995 a dictionary-like object which includes a small number of 

2996 named methods such as :meth:`.OrderedProperties.items` 

2997 and :meth:`.OrderedProperties.values`. When 

2998 accessing attributes dynamically, favor using the dict-access 

2999 scheme, e.g. ``mapper.attrs[somename]`` over 

3000 ``getattr(mapper.attrs, somename)`` to avoid name collisions. 

3001 

3002 .. seealso:: 

3003 

3004 :attr:`_orm.Mapper.all_orm_descriptors` 

3005 

3006 """ 

3007 

3008 self._check_configure() 

3009 return util.ReadOnlyProperties(self._props) 

3010 

3011 @HasMemoized.memoized_attribute 

3012 def all_orm_descriptors(self) -> util.ReadOnlyProperties[InspectionAttr]: 

3013 """A namespace of all :class:`.InspectionAttr` attributes associated 

3014 with the mapped class. 

3015 

3016 These attributes are in all cases Python :term:`descriptors` 

3017 associated with the mapped class or its superclasses. 

3018 

3019 This namespace includes attributes that are mapped to the class 

3020 as well as attributes declared by extension modules. 

3021 It includes any Python descriptor type that inherits from 

3022 :class:`.InspectionAttr`. This includes 

3023 :class:`.QueryableAttribute`, as well as extension types such as 

3024 :class:`.hybrid_property`, :class:`.hybrid_method` and 

3025 :class:`.AssociationProxy`. 

3026 

3027 To distinguish between mapped attributes and extension attributes, 

3028 the attribute :attr:`.InspectionAttr.extension_type` will refer 

3029 to a constant that distinguishes between different extension types. 

3030 

3031 The sorting of the attributes is based on the following rules: 

3032 

3033 1. Iterate through the class and its superclasses in order from 

3034 subclass to superclass (i.e. iterate through ``cls.__mro__``) 

3035 

3036 2. For each class, yield the attributes in the order in which they 

3037 appear in ``__dict__``, with the exception of those in step 

3038 3 below. The order will be the 

3039 same as that of the class' construction, with the exception 

3040 of attributes that were added after the fact by the application 

3041 or the mapper. 

3042 

3043 3. If a certain attribute key is also in the superclass ``__dict__``, 

3044 then it's included in the iteration for that class, and not the 

3045 class in which it first appeared. 

3046 

3047 The above process produces an ordering that is deterministic in terms 

3048 of the order in which attributes were assigned to the class. 

3049 

3050 When dealing with a :class:`.QueryableAttribute`, the 

3051 :attr:`.QueryableAttribute.property` attribute refers to the 

3052 :class:`.MapperProperty` property, which is what you get when 

3053 referring to the collection of mapped properties via 

3054 :attr:`_orm.Mapper.attrs`. 

3055 

3056 .. warning:: 

3057 

3058 The :attr:`_orm.Mapper.all_orm_descriptors` 

3059 accessor namespace is an 

3060 instance of :class:`.OrderedProperties`. This is 

3061 a dictionary-like object which includes a small number of 

3062 named methods such as :meth:`.OrderedProperties.items` 

3063 and :meth:`.OrderedProperties.values`. When 

3064 accessing attributes dynamically, favor using the dict-access 

3065 scheme, e.g. ``mapper.all_orm_descriptors[somename]`` over 

3066 ``getattr(mapper.all_orm_descriptors, somename)`` to avoid name 

3067 collisions. 

3068 

3069 .. seealso:: 

3070 

3071 :attr:`_orm.Mapper.attrs` 

3072 

3073 """ 

3074 return util.ReadOnlyProperties( 

3075 dict(self.class_manager._all_sqla_attributes()) 

3076 ) 

3077 

3078 @HasMemoized.memoized_attribute 

3079 @util.preload_module("sqlalchemy.orm.descriptor_props") 

3080 def _pk_synonyms(self) -> Dict[str, str]: 

3081 """return a dictionary of {syn_attribute_name: pk_attr_name} for 

3082 all synonyms that refer to primary key columns 

3083 

3084 """ 

3085 descriptor_props = util.preloaded.orm_descriptor_props 

3086 

3087 pk_keys = {prop.key for prop in self._identity_key_props} 

3088 

3089 return { 

3090 syn.key: syn.name 

3091 for k, syn in self._props.items() 

3092 if isinstance(syn, descriptor_props.SynonymProperty) 

3093 and syn.name in pk_keys 

3094 } 

3095 

3096 @HasMemoized.memoized_attribute 

3097 @util.preload_module("sqlalchemy.orm.descriptor_props") 

3098 def synonyms(self) -> util.ReadOnlyProperties[SynonymProperty[Any]]: 

3099 """Return a namespace of all :class:`.Synonym` 

3100 properties maintained by this :class:`_orm.Mapper`. 

3101 

3102 .. seealso:: 

3103 

3104 :attr:`_orm.Mapper.attrs` - namespace of all 

3105 :class:`.MapperProperty` 

3106 objects. 

3107 

3108 """ 

3109 descriptor_props = util.preloaded.orm_descriptor_props 

3110 

3111 return self._filter_properties(descriptor_props.SynonymProperty) 

3112 

3113 @util.ro_non_memoized_property 

3114 def entity_namespace(self) -> _EntityNamespace: 

3115 return self.class_ # type: ignore[return-value] 

3116 

3117 @HasMemoized.memoized_attribute 

3118 def column_attrs(self) -> util.ReadOnlyProperties[ColumnProperty[Any]]: 

3119 """Return a namespace of all :class:`.ColumnProperty` 

3120 properties maintained by this :class:`_orm.Mapper`. 

3121 

3122 .. seealso:: 

3123 

3124 :attr:`_orm.Mapper.attrs` - namespace of all 

3125 :class:`.MapperProperty` 

3126 objects. 

3127 

3128 """ 

3129 return self._filter_properties(properties.ColumnProperty) 

3130 

3131 @HasMemoized.memoized_attribute 

3132 @util.preload_module("sqlalchemy.orm.relationships") 

3133 def relationships( 

3134 self, 

3135 ) -> util.ReadOnlyProperties[RelationshipProperty[Any]]: 

3136 """A namespace of all :class:`.Relationship` properties 

3137 maintained by this :class:`_orm.Mapper`. 

3138 

3139 .. warning:: 

3140 

3141 the :attr:`_orm.Mapper.relationships` accessor namespace is an 

3142 instance of :class:`.OrderedProperties`. This is 

3143 a dictionary-like object which includes a small number of 

3144 named methods such as :meth:`.OrderedProperties.items` 

3145 and :meth:`.OrderedProperties.values`. When 

3146 accessing attributes dynamically, favor using the dict-access 

3147 scheme, e.g. ``mapper.relationships[somename]`` over 

3148 ``getattr(mapper.relationships, somename)`` to avoid name 

3149 collisions. 

3150 

3151 .. seealso:: 

3152 

3153 :attr:`_orm.Mapper.attrs` - namespace of all 

3154 :class:`.MapperProperty` 

3155 objects. 

3156 

3157 """ 

3158 return self._filter_properties( 

3159 util.preloaded.orm_relationships.RelationshipProperty 

3160 ) 

3161 

3162 @HasMemoized.memoized_attribute 

3163 @util.preload_module("sqlalchemy.orm.descriptor_props") 

3164 def composites(self) -> util.ReadOnlyProperties[CompositeProperty[Any]]: 

3165 """Return a namespace of all :class:`.Composite` 

3166 properties maintained by this :class:`_orm.Mapper`. 

3167 

3168 .. seealso:: 

3169 

3170 :attr:`_orm.Mapper.attrs` - namespace of all 

3171 :class:`.MapperProperty` 

3172 objects. 

3173 

3174 """ 

3175 return self._filter_properties( 

3176 util.preloaded.orm_descriptor_props.CompositeProperty 

3177 ) 

3178 

3179 def _filter_properties( 

3180 self, type_: Type[_MP] 

3181 ) -> util.ReadOnlyProperties[_MP]: 

3182 self._check_configure() 

3183 return util.ReadOnlyProperties( 

3184 util.OrderedDict( 

3185 (k, v) for k, v in self._props.items() if isinstance(v, type_) 

3186 ) 

3187 ) 

3188 

3189 @HasMemoized.memoized_attribute 

3190 def _get_clause(self): 

3191 """create a "get clause" based on the primary key. this is used 

3192 by query.get() and many-to-one lazyloads to load this item 

3193 by primary key. 

3194 

3195 """ 

3196 params = [ 

3197 ( 

3198 primary_key, 

3199 sql.bindparam("pk_%d" % idx, type_=primary_key.type), 

3200 ) 

3201 for idx, primary_key in enumerate(self.primary_key, 1) 

3202 ] 

3203 return ( 

3204 sql.and_(*[k == v for (k, v) in params]), 

3205 util.column_dict(params), 

3206 ) 

3207 

3208 @HasMemoized.memoized_attribute 

3209 def _equivalent_columns(self) -> _EquivalentColumnMap: 

3210 """Create a map of all equivalent columns, based on 

3211 the determination of column pairs that are equated to 

3212 one another based on inherit condition. This is designed 

3213 to work with the queries that util.polymorphic_union 

3214 comes up with, which often don't include the columns from 

3215 the base table directly (including the subclass table columns 

3216 only). 

3217 

3218 The resulting structure is a dictionary of columns mapped 

3219 to lists of equivalent columns, e.g.:: 

3220 

3221 {tablea.col1: {tableb.col1, tablec.col1}, tablea.col2: {tabled.col2}} 

3222 

3223 """ # noqa: E501 

3224 result: _EquivalentColumnMap = {} 

3225 

3226 def visit_binary(binary): 

3227 if binary.operator == operators.eq: 

3228 if binary.left in result: 

3229 result[binary.left].add(binary.right) 

3230 else: 

3231 result[binary.left] = {binary.right} 

3232 if binary.right in result: 

3233 result[binary.right].add(binary.left) 

3234 else: 

3235 result[binary.right] = {binary.left} 

3236 

3237 for mapper in self.base_mapper.self_and_descendants: 

3238 if mapper.inherit_condition is not None: 

3239 visitors.traverse( 

3240 mapper.inherit_condition, {}, {"binary": visit_binary} 

3241 ) 

3242 

3243 return result 

3244 

3245 def _is_userland_descriptor(self, assigned_name: str, obj: Any) -> bool: 

3246 if isinstance( 

3247 obj, 

3248 ( 

3249 _MappedAttribute, 

3250 instrumentation.ClassManager, 

3251 expression.ColumnElement, 

3252 ), 

3253 ): 

3254 return False 

3255 else: 

3256 return assigned_name not in self._dataclass_fields 

3257 

3258 @HasMemoized.memoized_attribute 

3259 def _dataclass_fields(self): 

3260 return [f.name for f in util.dataclass_fields(self.class_)] 

3261 

3262 def _should_exclude(self, name, assigned_name, local, column): 

3263 """determine whether a particular property should be implicitly 

3264 present on the class. 

3265 

3266 This occurs when properties are propagated from an inherited class, or 

3267 are applied from the columns present in the mapped table. 

3268 

3269 """ 

3270 

3271 if column is not None and sql_base._never_select_column(column): 

3272 return True 

3273 

3274 # check for class-bound attributes and/or descriptors, 

3275 # either local or from an inherited class 

3276 # ignore dataclass field default values 

3277 if local: 

3278 if self.class_.__dict__.get( 

3279 assigned_name, None 

3280 ) is not None and self._is_userland_descriptor( 

3281 assigned_name, self.class_.__dict__[assigned_name] 

3282 ): 

3283 return True 

3284 else: 

3285 attr = self.class_manager._get_class_attr_mro(assigned_name, None) 

3286 if attr is not None and self._is_userland_descriptor( 

3287 assigned_name, attr 

3288 ): 

3289 return True 

3290 

3291 if ( 

3292 self.include_properties is not None 

3293 and name not in self.include_properties 

3294 and (column is None or column not in self.include_properties) 

3295 ): 

3296 self._log("not including property %s" % (name)) 

3297 return True 

3298 

3299 if self.exclude_properties is not None and ( 

3300 name in self.exclude_properties 

3301 or (column is not None and column in self.exclude_properties) 

3302 ): 

3303 self._log("excluding property %s" % (name)) 

3304 return True 

3305 

3306 return False 

3307 

3308 def common_parent(self, other: Mapper[Any]) -> bool: 

3309 """Return true if the given mapper shares a 

3310 common inherited parent as this mapper.""" 

3311 

3312 return self.base_mapper is other.base_mapper 

3313 

3314 def is_sibling(self, other: Mapper[Any]) -> bool: 

3315 """return true if the other mapper is an inheriting sibling to this 

3316 one. common parent but different branch 

3317 

3318 """ 

3319 return ( 

3320 self.base_mapper is other.base_mapper 

3321 and not self.isa(other) 

3322 and not other.isa(self) 

3323 ) 

3324 

3325 def _canload( 

3326 self, state: InstanceState[Any], allow_subtypes: bool 

3327 ) -> bool: 

3328 s = self.primary_mapper() 

3329 if self.polymorphic_on is not None or allow_subtypes: 

3330 return _state_mapper(state).isa(s) 

3331 else: 

3332 return _state_mapper(state) is s 

3333 

3334 def isa(self, other: Mapper[Any]) -> bool: 

3335 """Return True if the this mapper inherits from the given mapper.""" 

3336 

3337 m: Optional[Mapper[Any]] = self 

3338 while m and m is not other: 

3339 m = m.inherits 

3340 return bool(m) 

3341 

3342 def iterate_to_root(self) -> Iterator[Mapper[Any]]: 

3343 m: Optional[Mapper[Any]] = self 

3344 while m: 

3345 yield m 

3346 m = m.inherits 

3347 

3348 @HasMemoized.memoized_attribute 

3349 def self_and_descendants(self) -> Sequence[Mapper[Any]]: 

3350 """The collection including this mapper and all descendant mappers. 

3351 

3352 This includes not just the immediately inheriting mappers but 

3353 all their inheriting mappers as well. 

3354 

3355 """ 

3356 descendants = [] 

3357 stack = deque([self]) 

3358 while stack: 

3359 item = stack.popleft() 

3360 descendants.append(item) 

3361 stack.extend(item._inheriting_mappers) 

3362 return util.WeakSequence(descendants) 

3363 

3364 def polymorphic_iterator(self) -> Iterator[Mapper[Any]]: 

3365 """Iterate through the collection including this mapper and 

3366 all descendant mappers. 

3367 

3368 This includes not just the immediately inheriting mappers but 

3369 all their inheriting mappers as well. 

3370 

3371 To iterate through an entire hierarchy, use 

3372 ``mapper.base_mapper.polymorphic_iterator()``. 

3373 

3374 """ 

3375 return iter(self.self_and_descendants) 

3376 

3377 def primary_mapper(self) -> Mapper[Any]: 

3378 """Return the primary mapper corresponding to this mapper's class key 

3379 (class).""" 

3380 

3381 return self.class_manager.mapper 

3382 

3383 @property 

3384 def primary_base_mapper(self) -> Mapper[Any]: 

3385 return self.class_manager.mapper.base_mapper 

3386 

3387 def _result_has_identity_key(self, result, adapter=None): 

3388 pk_cols: Sequence[ColumnElement[Any]] 

3389 if adapter is not None: 

3390 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3391 else: 

3392 pk_cols = self.primary_key 

3393 rk = result.keys() 

3394 for col in pk_cols: 

3395 if col not in rk: 

3396 return False 

3397 else: 

3398 return True 

3399 

3400 def identity_key_from_row( 

3401 self, 

3402 row: Union[Row[Unpack[TupleAny]], RowMapping], 

3403 identity_token: Optional[Any] = None, 

3404 adapter: Optional[ORMAdapter] = None, 

3405 ) -> _IdentityKeyType[_O]: 

3406 """Return an identity-map key for use in storing/retrieving an 

3407 item from the identity map. 

3408 

3409 :param row: A :class:`.Row` or :class:`.RowMapping` produced from a 

3410 result set that selected from the ORM mapped primary key columns. 

3411 

3412 .. versionchanged:: 2.0 

3413 :class:`.Row` or :class:`.RowMapping` are accepted 

3414 for the "row" argument 

3415 

3416 """ 

3417 pk_cols: Sequence[ColumnElement[Any]] 

3418 if adapter is not None: 

3419 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3420 else: 

3421 pk_cols = self.primary_key 

3422 

3423 mapping: RowMapping 

3424 if hasattr(row, "_mapping"): 

3425 mapping = row._mapping 

3426 else: 

3427 mapping = row # type: ignore[assignment] 

3428 

3429 return ( 

3430 self._identity_class, 

3431 tuple(mapping[column] for column in pk_cols), 

3432 identity_token, 

3433 ) 

3434 

3435 def identity_key_from_primary_key( 

3436 self, 

3437 primary_key: Tuple[Any, ...], 

3438 identity_token: Optional[Any] = None, 

3439 ) -> _IdentityKeyType[_O]: 

3440 """Return an identity-map key for use in storing/retrieving an 

3441 item from an identity map. 

3442 

3443 :param primary_key: A list of values indicating the identifier. 

3444 

3445 """ 

3446 return ( 

3447 self._identity_class, 

3448 tuple(primary_key), 

3449 identity_token, 

3450 ) 

3451 

3452 def identity_key_from_instance(self, instance: _O) -> _IdentityKeyType[_O]: 

3453 """Return the identity key for the given instance, based on 

3454 its primary key attributes. 

3455 

3456 If the instance's state is expired, calling this method 

3457 will result in a database check to see if the object has been deleted. 

3458 If the row no longer exists, 

3459 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3460 

3461 This value is typically also found on the instance state under the 

3462 attribute name `key`. 

3463 

3464 """ 

3465 state = attributes.instance_state(instance) 

3466 return self._identity_key_from_state(state, PassiveFlag.PASSIVE_OFF) 

3467 

3468 def _identity_key_from_state( 

3469 self, 

3470 state: InstanceState[_O], 

3471 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3472 ) -> _IdentityKeyType[_O]: 

3473 dict_ = state.dict 

3474 manager = state.manager 

3475 return ( 

3476 self._identity_class, 

3477 tuple( 

3478 [ 

3479 manager[prop.key].impl.get(state, dict_, passive) 

3480 for prop in self._identity_key_props 

3481 ] 

3482 ), 

3483 state.identity_token, 

3484 ) 

3485 

3486 def primary_key_from_instance(self, instance: _O) -> Tuple[Any, ...]: 

3487 """Return the list of primary key values for the given 

3488 instance. 

3489 

3490 If the instance's state is expired, calling this method 

3491 will result in a database check to see if the object has been deleted. 

3492 If the row no longer exists, 

3493 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3494 

3495 """ 

3496 state = attributes.instance_state(instance) 

3497 identity_key = self._identity_key_from_state( 

3498 state, PassiveFlag.PASSIVE_OFF 

3499 ) 

3500 return identity_key[1] 

3501 

3502 @HasMemoized.memoized_attribute 

3503 def _persistent_sortkey_fn(self): 

3504 key_fns = [col.type.sort_key_function for col in self.primary_key] 

3505 

3506 if set(key_fns).difference([None]): 

3507 

3508 def key(state): 

3509 return tuple( 

3510 key_fn(val) if key_fn is not None else val 

3511 for key_fn, val in zip(key_fns, state.key[1]) 

3512 ) 

3513 

3514 else: 

3515 

3516 def key(state): 

3517 return state.key[1] 

3518 

3519 return key 

3520 

3521 @HasMemoized.memoized_attribute 

3522 def _identity_key_props(self): 

3523 return [self._columntoproperty[col] for col in self.primary_key] 

3524 

3525 @HasMemoized.memoized_attribute 

3526 def _all_pk_cols(self): 

3527 collection: Set[ColumnClause[Any]] = set() 

3528 for table in self.tables: 

3529 collection.update(self._pks_by_table[table]) 

3530 return collection 

3531 

3532 @HasMemoized.memoized_attribute 

3533 def _should_undefer_in_wildcard(self): 

3534 cols: Set[ColumnElement[Any]] = set(self.primary_key) 

3535 if self.polymorphic_on is not None: 

3536 cols.add(self.polymorphic_on) 

3537 return cols 

3538 

3539 @HasMemoized.memoized_attribute 

3540 def _primary_key_propkeys(self): 

3541 return {self._columntoproperty[col].key for col in self._all_pk_cols} 

3542 

3543 def _get_state_attr_by_column( 

3544 self, 

3545 state: InstanceState[_O], 

3546 dict_: _InstanceDict, 

3547 column: ColumnElement[Any], 

3548 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3549 ) -> Any: 

3550 prop = self._columntoproperty[column] 

3551 return state.manager[prop.key].impl.get(state, dict_, passive=passive) 

3552 

3553 def _set_committed_state_attr_by_column(self, state, dict_, column, value): 

3554 prop = self._columntoproperty[column] 

3555 state.manager[prop.key].impl.set_committed_value(state, dict_, value) 

3556 

3557 def _set_state_attr_by_column(self, state, dict_, column, value): 

3558 prop = self._columntoproperty[column] 

3559 state.manager[prop.key].impl.set(state, dict_, value, None) 

3560 

3561 def _get_committed_attr_by_column(self, obj, column): 

3562 state = attributes.instance_state(obj) 

3563 dict_ = attributes.instance_dict(obj) 

3564 return self._get_committed_state_attr_by_column( 

3565 state, dict_, column, passive=PassiveFlag.PASSIVE_OFF 

3566 ) 

3567 

3568 def _get_committed_state_attr_by_column( 

3569 self, state, dict_, column, passive=PassiveFlag.PASSIVE_RETURN_NO_VALUE 

3570 ): 

3571 prop = self._columntoproperty[column] 

3572 return state.manager[prop.key].impl.get_committed_value( 

3573 state, dict_, passive=passive 

3574 ) 

3575 

3576 def _optimized_get_statement(self, state, attribute_names): 

3577 """assemble a WHERE clause which retrieves a given state by primary 

3578 key, using a minimized set of tables. 

3579 

3580 Applies to a joined-table inheritance mapper where the 

3581 requested attribute names are only present on joined tables, 

3582 not the base table. The WHERE clause attempts to include 

3583 only those tables to minimize joins. 

3584 

3585 """ 

3586 props = self._props 

3587 

3588 col_attribute_names = set(attribute_names).intersection( 

3589 state.mapper.column_attrs.keys() 

3590 ) 

3591 tables: Set[FromClause] = set( 

3592 chain( 

3593 *[ 

3594 sql_util.find_tables(c, check_columns=True) 

3595 for key in col_attribute_names 

3596 for c in props[key].columns 

3597 ] 

3598 ) 

3599 ) 

3600 

3601 if self.base_mapper.local_table in tables: 

3602 return None 

3603 

3604 def visit_binary(binary): 

3605 leftcol = binary.left 

3606 rightcol = binary.right 

3607 if leftcol is None or rightcol is None: 

3608 return 

3609 

3610 if leftcol.table not in tables: 

3611 leftval = self._get_committed_state_attr_by_column( 

3612 state, 

3613 state.dict, 

3614 leftcol, 

3615 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3616 ) 

3617 if leftval in orm_util._none_set: 

3618 raise _OptGetColumnsNotAvailable() 

3619 binary.left = sql.bindparam( 

3620 None, leftval, type_=binary.right.type 

3621 ) 

3622 elif rightcol.table not in tables: 

3623 rightval = self._get_committed_state_attr_by_column( 

3624 state, 

3625 state.dict, 

3626 rightcol, 

3627 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3628 ) 

3629 if rightval in orm_util._none_set: 

3630 raise _OptGetColumnsNotAvailable() 

3631 binary.right = sql.bindparam( 

3632 None, rightval, type_=binary.right.type 

3633 ) 

3634 

3635 allconds: List[ColumnElement[bool]] = [] 

3636 

3637 start = False 

3638 

3639 # as of #7507, from the lowest base table on upwards, 

3640 # we include all intermediary tables. 

3641 

3642 for mapper in reversed(list(self.iterate_to_root())): 

3643 if mapper.local_table in tables: 

3644 start = True 

3645 elif not isinstance(mapper.local_table, expression.TableClause): 

3646 return None 

3647 if start and not mapper.single: 

3648 assert mapper.inherits 

3649 assert not mapper.concrete 

3650 assert mapper.inherit_condition is not None 

3651 allconds.append(mapper.inherit_condition) 

3652 tables.add(mapper.local_table) 

3653 

3654 # only the bottom table needs its criteria to be altered to fit 

3655 # the primary key ident - the rest of the tables upwards to the 

3656 # descendant-most class should all be present and joined to each 

3657 # other. 

3658 try: 

3659 _traversed = visitors.cloned_traverse( 

3660 allconds[0], {}, {"binary": visit_binary} 

3661 ) 

3662 except _OptGetColumnsNotAvailable: 

3663 return None 

3664 else: 

3665 allconds[0] = _traversed 

3666 

3667 cond = sql.and_(*allconds) 

3668 

3669 cols = [] 

3670 for key in col_attribute_names: 

3671 cols.extend(props[key].columns) 

3672 return ( 

3673 sql.select(*cols) 

3674 .where(cond) 

3675 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 

3676 ) 

3677 

3678 def _iterate_to_target_viawpoly(self, mapper): 

3679 if self.isa(mapper): 

3680 prev = self 

3681 for m in self.iterate_to_root(): 

3682 yield m 

3683 

3684 if m is not prev and prev not in m._with_polymorphic_mappers: 

3685 break 

3686 

3687 prev = m 

3688 if m is mapper: 

3689 break 

3690 

3691 @HasMemoized.memoized_attribute 

3692 def _would_selectinload_combinations_cache(self): 

3693 return {} 

3694 

3695 def _would_selectin_load_only_from_given_mapper(self, super_mapper): 

3696 """return True if this mapper would "selectin" polymorphic load based 

3697 on the given super mapper, and not from a setting from a subclass. 

3698 

3699 given:: 

3700 

3701 class A: ... 

3702 

3703 

3704 class B(A): 

3705 __mapper_args__ = {"polymorphic_load": "selectin"} 

3706 

3707 

3708 class C(B): ... 

3709 

3710 

3711 class D(B): 

3712 __mapper_args__ = {"polymorphic_load": "selectin"} 

3713 

3714 ``inspect(C)._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3715 returns True, because C does selectin loading because of B's setting. 

3716 

3717 OTOH, ``inspect(D) 

3718 ._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3719 returns False, because D does selectin loading because of its own 

3720 setting; when we are doing a selectin poly load from B, we want to 

3721 filter out D because it would already have its own selectin poly load 

3722 set up separately. 

3723 

3724 Added as part of #9373. 

3725 

3726 """ 

3727 cache = self._would_selectinload_combinations_cache 

3728 

3729 try: 

3730 return cache[super_mapper] 

3731 except KeyError: 

3732 pass 

3733 

3734 # assert that given object is a supermapper, meaning we already 

3735 # strong reference it directly or indirectly. this allows us 

3736 # to not worry that we are creating new strongrefs to unrelated 

3737 # mappers or other objects. 

3738 assert self.isa(super_mapper) 

3739 

3740 mapper = super_mapper 

3741 for m in self._iterate_to_target_viawpoly(mapper): 

3742 if m.polymorphic_load == "selectin": 

3743 retval = m is super_mapper 

3744 break 

3745 else: 

3746 retval = False 

3747 

3748 cache[super_mapper] = retval 

3749 return retval 

3750 

3751 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 

3752 if not enabled_via_opt: 

3753 # common case, takes place for all polymorphic loads 

3754 mapper = polymorphic_from 

3755 for m in self._iterate_to_target_viawpoly(mapper): 

3756 if m.polymorphic_load == "selectin": 

3757 return m 

3758 else: 

3759 # uncommon case, selectin load options were used 

3760 enabled_via_opt = set(enabled_via_opt) 

3761 enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 

3762 for entity in enabled_via_opt.union([polymorphic_from]): 

3763 mapper = entity.mapper 

3764 for m in self._iterate_to_target_viawpoly(mapper): 

3765 if ( 

3766 m.polymorphic_load == "selectin" 

3767 or m in enabled_via_opt_mappers 

3768 ): 

3769 return enabled_via_opt_mappers.get(m, m) 

3770 

3771 return None 

3772 

3773 @util.preload_module("sqlalchemy.orm.strategy_options") 

3774 def _subclass_load_via_in(self, entity, polymorphic_from): 

3775 """Assemble a that can load the columns local to 

3776 this subclass as a SELECT with IN. 

3777 

3778 """ 

3779 

3780 strategy_options = util.preloaded.orm_strategy_options 

3781 

3782 assert self.inherits 

3783 

3784 if self.polymorphic_on is not None: 

3785 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 

3786 keep_props = set([polymorphic_prop] + self._identity_key_props) 

3787 else: 

3788 keep_props = set(self._identity_key_props) 

3789 

3790 disable_opt = strategy_options.Load(entity) 

3791 enable_opt = strategy_options.Load(entity) 

3792 

3793 classes_to_include = {self} 

3794 m: Optional[Mapper[Any]] = self.inherits 

3795 while ( 

3796 m is not None 

3797 and m is not polymorphic_from 

3798 and m.polymorphic_load == "selectin" 

3799 ): 

3800 classes_to_include.add(m) 

3801 m = m.inherits 

3802 

3803 for prop in self.column_attrs + self.relationships: 

3804 # skip prop keys that are not instrumented on the mapped class. 

3805 # this is primarily the "_sa_polymorphic_on" property that gets 

3806 # created for an ad-hoc polymorphic_on SQL expression, issue #8704 

3807 if prop.key not in self.class_manager: 

3808 continue 

3809 

3810 if prop.parent in classes_to_include or prop in keep_props: 

3811 # "enable" options, to turn on the properties that we want to 

3812 # load by default (subject to options from the query) 

3813 if not isinstance(prop, StrategizedProperty): 

3814 continue 

3815 

3816 enable_opt = enable_opt._set_generic_strategy( 

3817 # convert string name to an attribute before passing 

3818 # to loader strategy. note this must be in terms 

3819 # of given entity, such as AliasedClass, etc. 

3820 (getattr(entity.entity_namespace, prop.key),), 

3821 dict(prop.strategy_key), 

3822 _reconcile_to_other=True, 

3823 ) 

3824 else: 

3825 # "disable" options, to turn off the properties from the 

3826 # superclass that we *don't* want to load, applied after 

3827 # the options from the query to override them 

3828 disable_opt = disable_opt._set_generic_strategy( 

3829 # convert string name to an attribute before passing 

3830 # to loader strategy. note this must be in terms 

3831 # of given entity, such as AliasedClass, etc. 

3832 (getattr(entity.entity_namespace, prop.key),), 

3833 {"do_nothing": True}, 

3834 _reconcile_to_other=False, 

3835 ) 

3836 

3837 primary_key = list(self.primary_key) 

3838 

3839 in_expr: ColumnElement[Any] 

3840 

3841 if len(primary_key) > 1: 

3842 in_expr = sql.tuple_(*primary_key) 

3843 else: 

3844 in_expr = primary_key[0] 

3845 

3846 if entity.is_aliased_class: 

3847 assert entity.mapper is self 

3848 

3849 q = sql.select(entity).set_label_style( 

3850 LABEL_STYLE_TABLENAME_PLUS_COL 

3851 ) 

3852 

3853 in_expr = entity._adapter.traverse(in_expr) 

3854 q = q.where( 

3855 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3856 ) 

3857 else: 

3858 q = sql.select(self).set_label_style( 

3859 LABEL_STYLE_TABLENAME_PLUS_COL 

3860 ) 

3861 q = q.where( 

3862 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3863 ) 

3864 

3865 return q, enable_opt, disable_opt 

3866 

3867 @HasMemoized.memoized_attribute 

3868 def _subclass_load_via_in_mapper(self): 

3869 # the default is loading this mapper against the basemost mapper 

3870 return self._subclass_load_via_in(self, self.base_mapper) 

3871 

3872 def cascade_iterator( 

3873 self, 

3874 type_: str, 

3875 state: InstanceState[_O], 

3876 halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None, 

3877 ) -> Iterator[ 

3878 Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict] 

3879 ]: 

3880 r"""Iterate each element and its mapper in an object graph, 

3881 for all relationships that meet the given cascade rule. 

3882 

3883 :param type\_: 

3884 The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 

3885 etc.). 

3886 

3887 .. note:: the ``"all"`` cascade is not accepted here. For a generic 

3888 object traversal function, see :ref:`faq_walk_objects`. 

3889 

3890 :param state: 

3891 The lead InstanceState. child items will be processed per 

3892 the relationships defined for this object's mapper. 

3893 

3894 :return: the method yields individual object instances. 

3895 

3896 .. seealso:: 

3897 

3898 :ref:`unitofwork_cascades` 

3899 

3900 :ref:`faq_walk_objects` - illustrates a generic function to 

3901 traverse all objects without relying on cascades. 

3902 

3903 """ 

3904 visited_states: Set[InstanceState[Any]] = set() 

3905 prp, mpp = object(), object() 

3906 

3907 assert state.mapper.isa(self) 

3908 

3909 # this is actually a recursive structure, fully typing it seems 

3910 # a little too difficult for what it's worth here 

3911 visitables: Deque[ 

3912 Tuple[ 

3913 Deque[Any], 

3914 object, 

3915 Optional[InstanceState[Any]], 

3916 Optional[_InstanceDict], 

3917 ] 

3918 ] 

3919 

3920 visitables = deque( 

3921 [(deque(state.mapper._props.values()), prp, state, state.dict)] 

3922 ) 

3923 

3924 while visitables: 

3925 iterator, item_type, parent_state, parent_dict = visitables[-1] 

3926 if not iterator: 

3927 visitables.pop() 

3928 continue 

3929 

3930 if item_type is prp: 

3931 prop = iterator.popleft() 

3932 if not prop.cascade or type_ not in prop.cascade: 

3933 continue 

3934 assert parent_state is not None 

3935 assert parent_dict is not None 

3936 queue = deque( 

3937 prop.cascade_iterator( 

3938 type_, 

3939 parent_state, 

3940 parent_dict, 

3941 visited_states, 

3942 halt_on, 

3943 ) 

3944 ) 

3945 if queue: 

3946 visitables.append((queue, mpp, None, None)) 

3947 elif item_type is mpp: 

3948 ( 

3949 instance, 

3950 instance_mapper, 

3951 corresponding_state, 

3952 corresponding_dict, 

3953 ) = iterator.popleft() 

3954 yield ( 

3955 instance, 

3956 instance_mapper, 

3957 corresponding_state, 

3958 corresponding_dict, 

3959 ) 

3960 visitables.append( 

3961 ( 

3962 deque(instance_mapper._props.values()), 

3963 prp, 

3964 corresponding_state, 

3965 corresponding_dict, 

3966 ) 

3967 ) 

3968 

3969 @HasMemoized.memoized_attribute 

3970 def _compiled_cache(self): 

3971 return util.LRUCache(self._compiled_cache_size) 

3972 

3973 @HasMemoized.memoized_attribute 

3974 def _multiple_persistence_tables(self): 

3975 return len(self.tables) > 1 

3976 

3977 @HasMemoized.memoized_attribute 

3978 def _sorted_tables(self): 

3979 table_to_mapper: Dict[TableClause, Mapper[Any]] = {} 

3980 

3981 for mapper in self.base_mapper.self_and_descendants: 

3982 for t in mapper.tables: 

3983 table_to_mapper.setdefault(t, mapper) 

3984 

3985 extra_dependencies = [] 

3986 for table, mapper in table_to_mapper.items(): 

3987 super_ = mapper.inherits 

3988 if super_: 

3989 extra_dependencies.extend( 

3990 [(super_table, table) for super_table in super_.tables] 

3991 ) 

3992 

3993 def skip(fk): 

3994 # attempt to skip dependencies that are not 

3995 # significant to the inheritance chain 

3996 # for two tables that are related by inheritance. 

3997 # while that dependency may be important, it's technically 

3998 # not what we mean to sort on here. 

3999 parent = table_to_mapper.get(fk.parent.table) 

4000 dep = table_to_mapper.get(fk.column.table) 

4001 if ( 

4002 parent is not None 

4003 and dep is not None 

4004 and dep is not parent 

4005 and dep.inherit_condition is not None 

4006 ): 

4007 cols = set(sql_util._find_columns(dep.inherit_condition)) 

4008 if parent.inherit_condition is not None: 

4009 cols = cols.union( 

4010 sql_util._find_columns(parent.inherit_condition) 

4011 ) 

4012 return fk.parent not in cols and fk.column not in cols 

4013 else: 

4014 return fk.parent not in cols 

4015 return False 

4016 

4017 sorted_ = sql_util.sort_tables( 

4018 table_to_mapper, 

4019 skip_fn=skip, 

4020 extra_dependencies=extra_dependencies, 

4021 ) 

4022 

4023 ret = util.OrderedDict() 

4024 for t in sorted_: 

4025 ret[t] = table_to_mapper[t] 

4026 return ret 

4027 

4028 def _memo(self, key: Any, callable_: Callable[[], _T]) -> _T: 

4029 if key in self._memoized_values: 

4030 return cast(_T, self._memoized_values[key]) 

4031 else: 

4032 self._memoized_values[key] = value = callable_() 

4033 return value 

4034 

4035 @util.memoized_property 

4036 def _table_to_equated(self): 

4037 """memoized map of tables to collections of columns to be 

4038 synchronized upwards to the base mapper.""" 

4039 

4040 result: util.defaultdict[ 

4041 Table, 

4042 List[ 

4043 Tuple[ 

4044 Mapper[Any], 

4045 List[Tuple[ColumnElement[Any], ColumnElement[Any]]], 

4046 ] 

4047 ], 

4048 ] = util.defaultdict(list) 

4049 

4050 def set_union(x, y): 

4051 return x.union(y) 

4052 

4053 for table in self._sorted_tables: 

4054 cols = set(table.c) 

4055 

4056 for m in self.iterate_to_root(): 

4057 if m._inherits_equated_pairs and cols.intersection( 

4058 reduce( 

4059 set_union, 

4060 [l.proxy_set for l, r in m._inherits_equated_pairs], 

4061 ) 

4062 ): 

4063 result[table].append((m, m._inherits_equated_pairs)) 

4064 

4065 return result 

4066 

4067 

4068class _OptGetColumnsNotAvailable(Exception): 

4069 pass 

4070 

4071 

4072def configure_mappers() -> None: 

4073 """Initialize the inter-mapper relationships of all mappers that 

4074 have been constructed thus far across all :class:`_orm.registry` 

4075 collections. 

4076 

4077 The configure step is used to reconcile and initialize the 

4078 :func:`_orm.relationship` linkages between mapped classes, as well as to 

4079 invoke configuration events such as the 

4080 :meth:`_orm.MapperEvents.before_configured` and 

4081 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM 

4082 extensions or user-defined extension hooks. 

4083 

4084 Mapper configuration is normally invoked automatically, the first time 

4085 mappings from a particular :class:`_orm.registry` are used, as well as 

4086 whenever mappings are used and additional not-yet-configured mappers have 

4087 been constructed. The automatic configuration process however is local only 

4088 to the :class:`_orm.registry` involving the target mapper and any related 

4089 :class:`_orm.registry` objects which it may depend on; this is 

4090 equivalent to invoking the :meth:`_orm.registry.configure` method 

4091 on a particular :class:`_orm.registry`. 

4092 

4093 By contrast, the :func:`_orm.configure_mappers` function will invoke the 

4094 configuration process on all :class:`_orm.registry` objects that 

4095 exist in memory, and may be useful for scenarios where many individual 

4096 :class:`_orm.registry` objects that are nonetheless interrelated are 

4097 in use. 

4098 

4099 .. versionchanged:: 1.4 

4100 

4101 As of SQLAlchemy 1.4.0b2, this function works on a 

4102 per-:class:`_orm.registry` basis, locating all :class:`_orm.registry` 

4103 objects present and invoking the :meth:`_orm.registry.configure` method 

4104 on each. The :meth:`_orm.registry.configure` method may be preferred to 

4105 limit the configuration of mappers to those local to a particular 

4106 :class:`_orm.registry` and/or declarative base class. 

4107 

4108 Points at which automatic configuration is invoked include when a mapped 

4109 class is instantiated into an instance, as well as when ORM queries 

4110 are emitted using :meth:`.Session.query` or :meth:`_orm.Session.execute` 

4111 with an ORM-enabled statement. 

4112 

4113 The mapper configure process, whether invoked by 

4114 :func:`_orm.configure_mappers` or from :meth:`_orm.registry.configure`, 

4115 provides several event hooks that can be used to augment the mapper 

4116 configuration step. These hooks include: 

4117 

4118 * :meth:`.MapperEvents.before_configured` - called once before 

4119 :func:`.configure_mappers` or :meth:`_orm.registry.configure` does any 

4120 work; this can be used to establish additional options, properties, or 

4121 related mappings before the operation proceeds. 

4122 

4123 * :meth:`.RegistryEvents.before_configured` - Like 

4124 :meth:`.MapperEvents.before_configured`, but local to a specific 

4125 :class:`_orm.registry`. 

4126 

4127 .. versionadded:: 2.1 - added :meth:`.RegistryEvents.before_configured` 

4128 

4129 * :meth:`.MapperEvents.mapper_configured` - called as each individual 

4130 :class:`_orm.Mapper` is configured within the process; will include all 

4131 mapper state except for backrefs set up by other mappers that are still 

4132 to be configured. 

4133 

4134 * :meth:`.MapperEvents.after_configured` - called once after 

4135 :func:`.configure_mappers` or :meth:`_orm.registry.configure` is 

4136 complete; at this stage, all :class:`_orm.Mapper` objects that fall 

4137 within the scope of the configuration operation will be fully configured. 

4138 Note that the calling application may still have other mappings that 

4139 haven't been produced yet, such as if they are in modules as yet 

4140 unimported, and may also have mappings that are still to be configured, 

4141 if they are in other :class:`_orm.registry` collections not part of the 

4142 current scope of configuration. 

4143 

4144 * :meth:`.RegistryEvents.after_configured` - Like 

4145 :meth:`.MapperEvents.after_configured`, but local to a specific 

4146 :class:`_orm.registry`. 

4147 

4148 .. versionadded:: 2.1 - added :meth:`.RegistryEvents.after_configured` 

4149 

4150 """ 

4151 

4152 _configure_registries(_all_registries(), cascade=True) 

4153 

4154 

4155def _configure_registries( 

4156 registries: Set[_RegistryType], cascade: bool 

4157) -> None: 

4158 for reg in registries: 

4159 if reg._new_mappers: 

4160 break 

4161 else: 

4162 return 

4163 

4164 with _CONFIGURE_MUTEX: 

4165 global _already_compiling 

4166 if _already_compiling: 

4167 return 

4168 _already_compiling = True 

4169 try: 

4170 # double-check inside mutex 

4171 for reg in registries: 

4172 if reg._new_mappers: 

4173 break 

4174 else: 

4175 return 

4176 

4177 Mapper.dispatch._for_class(Mapper).before_configured() # type: ignore # noqa: E501 

4178 

4179 # initialize properties on all mappers 

4180 # note that _mapper_registry is unordered, which 

4181 # may randomly conceal/reveal issues related to 

4182 # the order of mapper compilation 

4183 

4184 registries_configured = list( 

4185 _do_configure_registries(registries, cascade) 

4186 ) 

4187 

4188 finally: 

4189 _already_compiling = False 

4190 for reg in registries_configured: 

4191 reg.dispatch.after_configured(reg) 

4192 Mapper.dispatch._for_class(Mapper).after_configured() # type: ignore 

4193 

4194 

4195@util.preload_module("sqlalchemy.orm.decl_api") 

4196def _do_configure_registries( 

4197 registries: Set[_RegistryType], cascade: bool 

4198) -> Iterator[registry]: 

4199 registry = util.preloaded.orm_decl_api.registry 

4200 

4201 orig = set(registries) 

4202 

4203 for reg in registry._recurse_with_dependencies(registries): 

4204 if reg._new_mappers: 

4205 reg.dispatch.before_configured(reg) 

4206 

4207 has_skip = False 

4208 

4209 for mapper in reg._mappers_to_configure(): 

4210 run_configure = None 

4211 

4212 for fn in mapper.dispatch.before_mapper_configured: 

4213 run_configure = fn(mapper, mapper.class_) 

4214 if run_configure is EXT_SKIP: 

4215 has_skip = True 

4216 break 

4217 if run_configure is EXT_SKIP: 

4218 continue 

4219 

4220 if getattr(mapper, "_configure_failed", False): 

4221 e = sa_exc.InvalidRequestError( 

4222 "One or more mappers failed to initialize - " 

4223 "can't proceed with initialization of other " 

4224 "mappers. Triggering mapper: '%s'. " 

4225 "Original exception was: %s" 

4226 % (mapper, mapper._configure_failed) 

4227 ) 

4228 e._configure_failed = mapper._configure_failed # type: ignore 

4229 raise e 

4230 

4231 if not mapper.configured: 

4232 try: 

4233 mapper._post_configure_properties() 

4234 mapper._expire_memoizations() 

4235 mapper.dispatch.mapper_configured(mapper, mapper.class_) 

4236 except Exception: 

4237 exc = sys.exc_info()[1] 

4238 if not hasattr(exc, "_configure_failed"): 

4239 mapper._configure_failed = exc 

4240 raise 

4241 

4242 if reg._new_mappers: 

4243 yield reg 

4244 if not has_skip: 

4245 reg._new_mappers = False 

4246 

4247 if not cascade and reg._dependencies.difference(orig): 

4248 raise sa_exc.InvalidRequestError( 

4249 "configure was called with cascade=False but " 

4250 "additional registries remain" 

4251 ) 

4252 

4253 

4254@util.preload_module("sqlalchemy.orm.decl_api") 

4255def _dispose_registries(registries: Set[_RegistryType], cascade: bool) -> None: 

4256 registry = util.preloaded.orm_decl_api.registry 

4257 

4258 orig = set(registries) 

4259 

4260 for reg in registry._recurse_with_dependents(registries): 

4261 if not cascade and reg._dependents.difference(orig): 

4262 raise sa_exc.InvalidRequestError( 

4263 "Registry has dependent registries that are not disposed; " 

4264 "pass cascade=True to clear these also" 

4265 ) 

4266 

4267 while reg._managers: 

4268 try: 

4269 manager, _ = reg._managers.popitem() 

4270 except KeyError: 

4271 # guard against race between while and popitem 

4272 pass 

4273 else: 

4274 reg._dispose_manager_and_mapper(manager) 

4275 

4276 reg._dependents.clear() 

4277 for dep in reg._dependencies: 

4278 dep._dependents.discard(reg) 

4279 reg._dependencies.clear() 

4280 # this wasn't done in the 1.3 clear_mappers() and in fact it 

4281 # was a bug, as it could cause configure_mappers() to invoke 

4282 # the "before_configured" event even though mappers had all been 

4283 # disposed. 

4284 reg._new_mappers = False 

4285 

4286 

4287def reconstructor(fn: _Fn) -> _Fn: 

4288 """Decorate a method as the 'reconstructor' hook. 

4289 

4290 Designates a single method as the "reconstructor", an ``__init__``-like 

4291 method that will be called by the ORM after the instance has been 

4292 loaded from the database or otherwise reconstituted. 

4293 

4294 .. tip:: 

4295 

4296 The :func:`_orm.reconstructor` decorator makes use of the 

4297 :meth:`_orm.InstanceEvents.load` event hook, which can be 

4298 used directly. 

4299 

4300 The reconstructor will be invoked with no arguments. Scalar 

4301 (non-collection) database-mapped attributes of the instance will 

4302 be available for use within the function. Eagerly-loaded 

4303 collections are generally not yet available and will usually only 

4304 contain the first element. ORM state changes made to objects at 

4305 this stage will not be recorded for the next flush() operation, so 

4306 the activity within a reconstructor should be conservative. 

4307 

4308 .. seealso:: 

4309 

4310 :meth:`.InstanceEvents.load` 

4311 

4312 """ 

4313 fn.__sa_reconstructor__ = True # type: ignore[attr-defined] 

4314 return fn 

4315 

4316 

4317def validates( 

4318 *names: str, include_removes: bool = False, include_backrefs: bool = True 

4319) -> Callable[[_Fn], _Fn]: 

4320 r"""Decorate a method as a 'validator' for one or more named properties. 

4321 

4322 Designates a method as a validator, a method which receives the 

4323 name of the attribute as well as a value to be assigned, or in the 

4324 case of a collection, the value to be added to the collection. 

4325 The function can then raise validation exceptions to halt the 

4326 process from continuing (where Python's built-in ``ValueError`` 

4327 and ``AssertionError`` exceptions are reasonable choices), or can 

4328 modify or replace the value before proceeding. The function should 

4329 otherwise return the given value. 

4330 

4331 Note that a validator for a collection **cannot** issue a load of that 

4332 collection within the validation routine - this usage raises 

4333 an assertion to avoid recursion overflows. This is a reentrant 

4334 condition which is not supported. 

4335 

4336 :param \*names: list of attribute names to be validated. 

4337 :param include_removes: if True, "remove" events will be 

4338 sent as well - the validation function must accept an additional 

4339 argument "is_remove" which will be a boolean. 

4340 

4341 :param include_backrefs: defaults to ``True``; if ``False``, the 

4342 validation function will not emit if the originator is an attribute 

4343 event related via a backref. This can be used for bi-directional 

4344 :func:`.validates` usage where only one validator should emit per 

4345 attribute operation. 

4346 

4347 .. versionchanged:: 2.0.16 This parameter inadvertently defaulted to 

4348 ``False`` for releases 2.0.0 through 2.0.15. Its correct default 

4349 of ``True`` is restored in 2.0.16. 

4350 

4351 .. seealso:: 

4352 

4353 :ref:`simple_validators` - usage examples for :func:`.validates` 

4354 

4355 """ 

4356 

4357 def wrap(fn: _Fn) -> _Fn: 

4358 fn.__sa_validators__ = names # type: ignore[attr-defined] 

4359 fn.__sa_validation_opts__ = { # type: ignore[attr-defined] 

4360 "include_removes": include_removes, 

4361 "include_backrefs": include_backrefs, 

4362 } 

4363 return fn 

4364 

4365 return wrap 

4366 

4367 

4368def _event_on_load(state, ctx): 

4369 instrumenting_mapper = state.manager.mapper 

4370 

4371 if instrumenting_mapper._reconstructor: 

4372 instrumenting_mapper._reconstructor(state.obj()) 

4373 

4374 

4375def _event_on_init(state, args, kwargs): 

4376 """Run init_instance hooks. 

4377 

4378 This also includes mapper compilation, normally not needed 

4379 here but helps with some piecemeal configuration 

4380 scenarios (such as in the ORM tutorial). 

4381 

4382 """ 

4383 

4384 instrumenting_mapper = state.manager.mapper 

4385 if instrumenting_mapper: 

4386 instrumenting_mapper._check_configure() 

4387 if instrumenting_mapper._set_polymorphic_identity: 

4388 instrumenting_mapper._set_polymorphic_identity(state) 

4389 

4390 

4391class _ColumnMapping(Dict["ColumnElement[Any]", "MapperProperty[Any]"]): 

4392 """Error reporting helper for mapper._columntoproperty.""" 

4393 

4394 __slots__ = ("mapper",) 

4395 

4396 def __init__(self, mapper): 

4397 # TODO: weakref would be a good idea here 

4398 self.mapper = mapper 

4399 

4400 def __missing__(self, column): 

4401 prop = self.mapper._props.get(column) 

4402 if prop: 

4403 raise orm_exc.UnmappedColumnError( 

4404 "Column '%s.%s' is not available, due to " 

4405 "conflicting property '%s':%r" 

4406 % (column.table.name, column.name, column.key, prop) 

4407 ) 

4408 raise orm_exc.UnmappedColumnError( 

4409 "No column %s is configured on mapper %s..." 

4410 % (column, self.mapper) 

4411 )