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

1422 statements  

1# orm/mapper.py 

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

3# <see AUTHORS file> 

4# 

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

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

7# 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: ReadOnlyColumnCollection[str, Column[Any]] 

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

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

1139 

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

1141 any :class:`_schema.Table` object, 

1142 except that only those columns included in 

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

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

1145 :class:`_schema.Column` itself. Additionally, scalar expressions mapped 

1146 by :func:`.column_property` are also present here. 

1147 

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

1149 Behavior is undefined if directly modified. 

1150 

1151 """ 

1152 

1153 c: ReadOnlyColumnCollection[str, Column[Any]] 

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

1155 

1156 @util.memoized_property 

1157 def _path_registry(self) -> _CachingEntityRegistry: 

1158 return PathRegistry.per_mapper(self) 

1159 

1160 def _configure_inheritance(self): 

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

1162 being present.""" 

1163 

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

1165 self._inheriting_mappers = util.WeakSequence() 

1166 

1167 if self.inherits: 

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

1169 raise sa_exc.ArgumentError( 

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

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

1172 ) 

1173 

1174 self.dispatch._update(self.inherits.dispatch) 

1175 

1176 if self.single: 

1177 self.persist_selectable = self.inherits.persist_selectable 

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

1179 if self.concrete: 

1180 self.persist_selectable = self.local_table 

1181 for mapper in self.iterate_to_root(): 

1182 if mapper.polymorphic_on is not None: 

1183 mapper._requires_row_aliasing = True 

1184 else: 

1185 if self.inherit_condition is None: 

1186 # figure out inherit condition from our table to the 

1187 # immediate table of the inherited mapper, not its 

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

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

1190 try: 

1191 self.inherit_condition = sql_util.join_condition( 

1192 self.inherits.local_table, self.local_table 

1193 ) 

1194 except sa_exc.NoForeignKeysError as nfe: 

1195 assert self.inherits.local_table is not None 

1196 assert self.local_table is not None 

1197 raise sa_exc.NoForeignKeysError( 

1198 "Can't determine the inherit condition " 

1199 "between inherited table '%s' and " 

1200 "inheriting " 

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

1202 "foreign key relationships established. " 

1203 "Please ensure the inheriting table has " 

1204 "a foreign key relationship to the " 

1205 "inherited " 

1206 "table, or provide an " 

1207 "'on clause' using " 

1208 "the 'inherit_condition' mapper argument." 

1209 % ( 

1210 self.inherits.local_table.description, 

1211 self.local_table.description, 

1212 ) 

1213 ) from nfe 

1214 except sa_exc.AmbiguousForeignKeysError as afe: 

1215 assert self.inherits.local_table is not None 

1216 assert self.local_table is not None 

1217 raise sa_exc.AmbiguousForeignKeysError( 

1218 "Can't determine the inherit condition " 

1219 "between inherited table '%s' and " 

1220 "inheriting " 

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

1222 "foreign key relationship established. " 

1223 "Please specify the 'on clause' using " 

1224 "the 'inherit_condition' mapper argument." 

1225 % ( 

1226 self.inherits.local_table.description, 

1227 self.local_table.description, 

1228 ) 

1229 ) from afe 

1230 assert self.inherits.persist_selectable is not None 

1231 self.persist_selectable = sql.join( 

1232 self.inherits.persist_selectable, 

1233 self.local_table, 

1234 self.inherit_condition, 

1235 ) 

1236 

1237 fks = util.to_set(self.inherit_foreign_keys) 

1238 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 

1239 self.persist_selectable.onclause, 

1240 consider_as_foreign_keys=fks, 

1241 ) 

1242 else: 

1243 self.persist_selectable = self.local_table 

1244 

1245 if self.polymorphic_identity is None: 

1246 self._identity_class = self.class_ 

1247 

1248 if ( 

1249 not self.polymorphic_abstract 

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

1251 ): 

1252 util.warn( 

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

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

1255 f"'polymorphic_on' column of " 

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

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

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

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

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

1261 "class unmapped when using Declarative, set the " 

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

1263 ) 

1264 elif self.concrete: 

1265 self._identity_class = self.class_ 

1266 else: 

1267 self._identity_class = self.inherits._identity_class 

1268 

1269 if self.version_id_col is None: 

1270 self.version_id_col = self.inherits.version_id_col 

1271 self.version_id_generator = self.inherits.version_id_generator 

1272 elif ( 

1273 self.inherits.version_id_col is not None 

1274 and self.version_id_col is not self.inherits.version_id_col 

1275 ): 

1276 util.warn( 

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

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

1279 "the inherited versioning column. " 

1280 "version_id_col should only be specified on " 

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

1282 % ( 

1283 self.version_id_col.description, 

1284 self.inherits.version_id_col.description, 

1285 ) 

1286 ) 

1287 

1288 self.polymorphic_map = self.inherits.polymorphic_map 

1289 self.batch = self.inherits.batch 

1290 self.inherits._inheriting_mappers.append(self) 

1291 self.base_mapper = self.inherits.base_mapper 

1292 self.passive_updates = self.inherits.passive_updates 

1293 self.passive_deletes = ( 

1294 self.inherits.passive_deletes or self.passive_deletes 

1295 ) 

1296 self._all_tables = self.inherits._all_tables 

1297 

1298 if self.polymorphic_identity is not None: 

1299 if self.polymorphic_identity in self.polymorphic_map: 

1300 util.warn( 

1301 "Reassigning polymorphic association for identity %r " 

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

1303 "value for polymorphic_identity." 

1304 % ( 

1305 self.polymorphic_identity, 

1306 self.polymorphic_map[self.polymorphic_identity], 

1307 self, 

1308 self.polymorphic_identity, 

1309 ) 

1310 ) 

1311 self.polymorphic_map[self.polymorphic_identity] = self 

1312 

1313 if self.polymorphic_load and self.concrete: 

1314 raise sa_exc.ArgumentError( 

1315 "polymorphic_load is not currently supported " 

1316 "with concrete table inheritance" 

1317 ) 

1318 if self.polymorphic_load == "inline": 

1319 self.inherits._add_with_polymorphic_subclass(self) 

1320 elif self.polymorphic_load == "selectin": 

1321 pass 

1322 elif self.polymorphic_load is not None: 

1323 raise sa_exc.ArgumentError( 

1324 "unknown argument for polymorphic_load: %r" 

1325 % self.polymorphic_load 

1326 ) 

1327 

1328 else: 

1329 self._all_tables = set() 

1330 self.base_mapper = self 

1331 assert self.local_table is not None 

1332 self.persist_selectable = self.local_table 

1333 if self.polymorphic_identity is not None: 

1334 self.polymorphic_map[self.polymorphic_identity] = self 

1335 self._identity_class = self.class_ 

1336 

1337 if self.persist_selectable is None: 

1338 raise sa_exc.ArgumentError( 

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

1340 % self 

1341 ) 

1342 

1343 def _set_with_polymorphic( 

1344 self, with_polymorphic: Optional[_WithPolymorphicArg] 

1345 ) -> None: 

1346 if with_polymorphic == "*": 

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

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

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

1350 self.with_polymorphic = cast( 

1351 """Tuple[ 

1352 Union[ 

1353 Literal["*"], 

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

1355 ], 

1356 Optional["FromClause"], 

1357 ]""", 

1358 with_polymorphic, 

1359 ) 

1360 else: 

1361 self.with_polymorphic = (with_polymorphic, None) 

1362 elif with_polymorphic is not None: 

1363 raise sa_exc.ArgumentError( 

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

1365 ) 

1366 else: 

1367 self.with_polymorphic = None 

1368 

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

1370 self.with_polymorphic = ( 

1371 self.with_polymorphic[0], 

1372 coercions.expect( 

1373 roles.FromClauseRole, 

1374 self.with_polymorphic[1], 

1375 ), 

1376 ) 

1377 

1378 if self.configured: 

1379 self._expire_memoizations() 

1380 

1381 def _add_with_polymorphic_subclass(self, mapper): 

1382 subcl = mapper.class_ 

1383 if self.with_polymorphic is None: 

1384 self._set_with_polymorphic((subcl,)) 

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

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

1387 self._set_with_polymorphic( 

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

1389 ) 

1390 

1391 def _set_concrete_base(self, mapper): 

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

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

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

1395 

1396 assert self.concrete 

1397 assert not self.inherits 

1398 assert isinstance(mapper, Mapper) 

1399 self.inherits = mapper 

1400 self.inherits.polymorphic_map.update(self.polymorphic_map) 

1401 self.polymorphic_map = self.inherits.polymorphic_map 

1402 for mapper in self.iterate_to_root(): 

1403 if mapper.polymorphic_on is not None: 

1404 mapper._requires_row_aliasing = True 

1405 self.batch = self.inherits.batch 

1406 for mp in self.self_and_descendants: 

1407 mp.base_mapper = self.inherits.base_mapper 

1408 self.inherits._inheriting_mappers.append(self) 

1409 self.passive_updates = self.inherits.passive_updates 

1410 self._all_tables = self.inherits._all_tables 

1411 

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

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

1414 key, key, local=False, column=None 

1415 ): 

1416 self._adapt_inherited_property(key, prop, False) 

1417 

1418 def _set_polymorphic_on(self, polymorphic_on): 

1419 self.polymorphic_on = polymorphic_on 

1420 self._configure_polymorphic_setter(True) 

1421 

1422 def _configure_class_instrumentation(self): 

1423 """Associate this Mapper with the 

1424 given class and entity name. 

1425 

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

1427 name combination will return this mapper. Also decorate the 

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

1429 auto-session attachment logic. 

1430 

1431 """ 

1432 

1433 # we expect that declarative has applied the class manager 

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

1435 # this raises as of 2.0. 

1436 manager = attributes.opt_manager_of_class(self.class_) 

1437 

1438 if manager is None or not manager.registry: 

1439 raise sa_exc.InvalidRequestError( 

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

1441 "invoked directly outside of a declarative registry." 

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

1443 "function for a classical mapping." 

1444 ) 

1445 

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

1447 

1448 # this invokes the class_instrument event and sets up 

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

1450 # occur after the instrument_class event above. 

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

1452 # :( 

1453 

1454 manager = instrumentation.register_class( 

1455 self.class_, 

1456 mapper=self, 

1457 expired_attribute_loader=util.partial( 

1458 loading._load_scalar_attributes, self 

1459 ), 

1460 # finalize flag means instrument the __init__ method 

1461 # and call the class_instrument event 

1462 finalize=True, 

1463 ) 

1464 

1465 self.class_manager = manager 

1466 

1467 assert manager.registry is not None 

1468 self.registry = manager.registry 

1469 

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

1471 # e_name None or not. 

1472 if manager.mapper is None: 

1473 return 

1474 

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

1476 

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

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

1479 method = method._sa_original_init 

1480 if hasattr(method, "__func__"): 

1481 method = method.__func__ 

1482 if callable(method): 

1483 if hasattr(method, "__sa_reconstructor__"): 

1484 self._reconstructor = method 

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

1486 elif hasattr(method, "__sa_validators__"): 

1487 validation_opts = method.__sa_validation_opts__ 

1488 for name in method.__sa_validators__: 

1489 if name in self.validators: 

1490 raise sa_exc.InvalidRequestError( 

1491 "A validation function for mapped " 

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

1493 % (name, self) 

1494 ) 

1495 self.validators = self.validators.union( 

1496 {name: (method, validation_opts)} 

1497 ) 

1498 

1499 def _set_dispose_flags(self) -> None: 

1500 self.configured = True 

1501 self._ready_for_configure = True 

1502 self._dispose_called = True 

1503 

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

1505 

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

1507 try: 

1508 prop = self._props[key] 

1509 except KeyError as err: 

1510 raise sa_exc.ArgumentError( 

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

1512 "no attribute is mapped to this name." 

1513 ) from err 

1514 try: 

1515 expr = prop.expression 

1516 except AttributeError as ae: 

1517 raise sa_exc.ArgumentError( 

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

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

1520 ) from ae 

1521 if not isinstance(expr, Column): 

1522 raise sa_exc.ArgumentError( 

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

1524 "property does not refer to a single " 

1525 "mapped Column" 

1526 ) 

1527 return expr 

1528 

1529 def _configure_pks(self) -> None: 

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

1531 

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

1533 

1534 self._pks_by_table = {} 

1535 self._cols_by_table = {} 

1536 

1537 all_cols = util.column_set( 

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

1539 ) 

1540 

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

1542 

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

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

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

1546 # ordering is important since it determines the ordering of 

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

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

1549 fc.primary_key 

1550 ).intersection( 

1551 pk_cols 

1552 ) 

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

1554 all_cols 

1555 ) 

1556 

1557 if self._primary_key_argument: 

1558 coerced_pk_arg = [ 

1559 ( 

1560 self._str_arg_to_mapped_col("primary_key", c) 

1561 if isinstance(c, str) 

1562 else c 

1563 ) 

1564 for c in ( 

1565 coercions.expect( 

1566 roles.DDLConstraintColumnRole, 

1567 coerce_pk, 

1568 argname="primary_key", 

1569 ) 

1570 for coerce_pk in self._primary_key_argument 

1571 ) 

1572 ] 

1573 else: 

1574 coerced_pk_arg = None 

1575 

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

1577 # primary key mappings 

1578 if coerced_pk_arg: 

1579 for k in coerced_pk_arg: 

1580 if k.table not in self._pks_by_table: 

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

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

1583 

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

1585 elif ( 

1586 self.persist_selectable not in self._pks_by_table 

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

1588 ): 

1589 raise sa_exc.ArgumentError( 

1590 "Mapper %s could not assemble any primary " 

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

1592 % (self, self.persist_selectable.description) 

1593 ) 

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

1595 self.local_table, schema.Table 

1596 ): 

1597 util.warn( 

1598 "Could not assemble any primary " 

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

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

1601 % self.local_table.description 

1602 ) 

1603 

1604 if ( 

1605 self.inherits 

1606 and not self.concrete 

1607 and not self._primary_key_argument 

1608 ): 

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

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

1611 self.primary_key = self.inherits.primary_key 

1612 else: 

1613 # determine primary key from argument or persist_selectable pks 

1614 primary_key: Collection[ColumnElement[Any]] 

1615 

1616 if coerced_pk_arg: 

1617 primary_key = [ 

1618 cc if cc is not None else c 

1619 for cc, c in ( 

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

1621 for c in coerced_pk_arg 

1622 ) 

1623 ] 

1624 else: 

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

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

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

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

1629 primary_key = sql_util.reduce_columns( 

1630 self._pks_by_table[self.persist_selectable], 

1631 ignore_nonexistent_tables=True, 

1632 ) 

1633 

1634 if len(primary_key) == 0: 

1635 raise sa_exc.ArgumentError( 

1636 "Mapper %s could not assemble any primary " 

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

1638 % (self, self.persist_selectable.description) 

1639 ) 

1640 

1641 self.primary_key = tuple(primary_key) 

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

1643 

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

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

1646 self._readonly_props = { 

1647 self._columntoproperty[col] 

1648 for col in self._columntoproperty 

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

1650 and ( 

1651 not hasattr(col, "table") 

1652 or col.table not in self._cols_by_table 

1653 ) 

1654 } 

1655 

1656 def _configure_properties(self) -> None: 

1657 self.columns = self.c = sql_base.ColumnCollection() # type: ignore 

1658 

1659 # object attribute names mapped to MapperProperty objects 

1660 self._props = util.OrderedDict() 

1661 

1662 # table columns mapped to MapperProperty 

1663 self._columntoproperty = _ColumnMapping(self) 

1664 

1665 explicit_col_props_by_column: Dict[ 

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

1667 ] = {} 

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

1669 

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

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

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

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

1674 if self._init_properties: 

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

1676 if not isinstance(prop_arg, MapperProperty): 

1677 possible_col_prop = self._make_prop_from_column( 

1678 key, prop_arg 

1679 ) 

1680 else: 

1681 possible_col_prop = prop_arg 

1682 

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

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

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

1686 # the Table. 

1687 

1688 _map_as_property_now = True 

1689 if isinstance(possible_col_prop, properties.ColumnProperty): 

1690 for given_col in possible_col_prop.columns: 

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

1692 _map_as_property_now = False 

1693 explicit_col_props_by_key[key] = possible_col_prop 

1694 explicit_col_props_by_column[given_col] = ( 

1695 key, 

1696 possible_col_prop, 

1697 ) 

1698 

1699 if _map_as_property_now: 

1700 self._configure_property( 

1701 key, 

1702 possible_col_prop, 

1703 init=False, 

1704 ) 

1705 

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

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

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

1709 if self.inherits: 

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

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

1712 continue 

1713 

1714 incoming_prop = explicit_col_props_by_key.get(key) 

1715 if incoming_prop: 

1716 new_prop = self._reconcile_prop_with_incoming_columns( 

1717 key, 

1718 inherited_prop, 

1719 warn_only=False, 

1720 incoming_prop=incoming_prop, 

1721 ) 

1722 explicit_col_props_by_key[key] = new_prop 

1723 

1724 for inc_col in incoming_prop.columns: 

1725 explicit_col_props_by_column[inc_col] = ( 

1726 key, 

1727 new_prop, 

1728 ) 

1729 elif key not in self._props: 

1730 self._adapt_inherited_property(key, inherited_prop, False) 

1731 

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

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

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

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

1736 # reconciliation against inherited columns occurs here also. 

1737 

1738 for column in self.persist_selectable.columns: 

1739 if column in explicit_col_props_by_column: 

1740 # column was explicitly passed to properties; configure 

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

1742 # Table / selectable 

1743 key, prop = explicit_col_props_by_column[column] 

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

1745 continue 

1746 

1747 elif column in self._columntoproperty: 

1748 continue 

1749 

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

1751 if self._should_exclude( 

1752 column.key, 

1753 column_key, 

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

1755 column=column, 

1756 ): 

1757 continue 

1758 

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

1760 # of the inheriting mapper 

1761 for mapper in self.iterate_to_root(): 

1762 if column in mapper._columntoproperty: 

1763 column_key = mapper._columntoproperty[column].key 

1764 

1765 self._configure_property( 

1766 column_key, 

1767 column, 

1768 init=False, 

1769 setparent=True, 

1770 ) 

1771 

1772 def _configure_polymorphic_setter(self, init=False): 

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

1774 'polymorphic_on' column, if applicable, and not 

1775 already generated by _configure_properties (which is typical). 

1776 

1777 Also create a setter function which will assign this 

1778 attribute to the value of the 'polymorphic_identity' 

1779 upon instance construction, also if applicable. This 

1780 routine will run when an instance is created. 

1781 

1782 """ 

1783 setter = False 

1784 polymorphic_key: Optional[str] = None 

1785 

1786 if self.polymorphic_on is not None: 

1787 setter = True 

1788 

1789 if isinstance(self.polymorphic_on, str): 

1790 # polymorphic_on specified as a string - link 

1791 # it to mapped ColumnProperty 

1792 try: 

1793 self.polymorphic_on = self._props[self.polymorphic_on] 

1794 except KeyError as err: 

1795 raise sa_exc.ArgumentError( 

1796 "Can't determine polymorphic_on " 

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

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

1799 ) from err 

1800 

1801 if self.polymorphic_on in self._columntoproperty: 

1802 # polymorphic_on is a column that is already mapped 

1803 # to a ColumnProperty 

1804 prop = self._columntoproperty[self.polymorphic_on] 

1805 elif isinstance(self.polymorphic_on, MapperProperty): 

1806 # polymorphic_on is directly a MapperProperty, 

1807 # ensure it's a ColumnProperty 

1808 if not isinstance( 

1809 self.polymorphic_on, properties.ColumnProperty 

1810 ): 

1811 raise sa_exc.ArgumentError( 

1812 "Only direct column-mapped " 

1813 "property or SQL expression " 

1814 "can be passed for polymorphic_on" 

1815 ) 

1816 prop = self.polymorphic_on 

1817 else: 

1818 # polymorphic_on is a Column or SQL expression and 

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

1820 # only present in the with_polymorphic selectable or 

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

1822 # hope is compatible with this mapper's persist_selectable 

1823 col = self.persist_selectable.corresponding_column( 

1824 self.polymorphic_on 

1825 ) 

1826 if col is None: 

1827 # polymorphic_on doesn't derive from any 

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

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

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

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

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

1833 # represented somehow in either persist_selectable or 

1834 # with_polymorphic. Otherwise as of 0.7.4 we 

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

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

1837 setter = False 

1838 instrument = False 

1839 col = self.polymorphic_on 

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

1841 self.with_polymorphic is None 

1842 or self.with_polymorphic[1] is None 

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

1844 is None 

1845 ): 

1846 raise sa_exc.InvalidRequestError( 

1847 "Could not map polymorphic_on column " 

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

1849 "loads will not function properly" 

1850 % col.description 

1851 ) 

1852 else: 

1853 # column/expression that polymorphic_on derives from 

1854 # is present in our mapped table 

1855 # and is probably mapped, but polymorphic_on itself 

1856 # is not. This happens when 

1857 # the polymorphic_on is only directly present in the 

1858 # with_polymorphic selectable, as when use 

1859 # polymorphic_union. 

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

1861 instrument = True 

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

1863 if key: 

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

1865 raise sa_exc.InvalidRequestError( 

1866 "Cannot exclude or override the " 

1867 "discriminator column %r" % key 

1868 ) 

1869 else: 

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

1871 key = col.key 

1872 

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

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

1875 

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

1877 # column in the property 

1878 self.polymorphic_on = prop.columns[0] 

1879 polymorphic_key = prop.key 

1880 else: 

1881 # no polymorphic_on was set. 

1882 # check inheriting mappers for one. 

1883 for mapper in self.iterate_to_root(): 

1884 # determine if polymorphic_on of the parent 

1885 # should be propagated here. If the col 

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

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

1888 # inheritance), we can use it 

1889 if mapper.polymorphic_on is not None: 

1890 if self.persist_selectable is mapper.persist_selectable: 

1891 self.polymorphic_on = mapper.polymorphic_on 

1892 else: 

1893 self.polymorphic_on = ( 

1894 self.persist_selectable 

1895 ).corresponding_column(mapper.polymorphic_on) 

1896 # we can use the parent mapper's _set_polymorphic_identity 

1897 # directly; it ensures the polymorphic_identity of the 

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

1899 if self.polymorphic_on is not None: 

1900 self._set_polymorphic_identity = ( 

1901 mapper._set_polymorphic_identity 

1902 ) 

1903 self._polymorphic_attr_key = ( 

1904 mapper._polymorphic_attr_key 

1905 ) 

1906 self._validate_polymorphic_identity = ( 

1907 mapper._validate_polymorphic_identity 

1908 ) 

1909 else: 

1910 self._set_polymorphic_identity = None 

1911 self._polymorphic_attr_key = None 

1912 return 

1913 

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

1915 raise sa_exc.InvalidRequestError( 

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

1917 "on a mapper hierarchy which includes the " 

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

1919 ) 

1920 

1921 if setter: 

1922 

1923 def _set_polymorphic_identity(state): 

1924 dict_ = state.dict 

1925 # TODO: what happens if polymorphic_on column attribute name 

1926 # does not match .key? 

1927 

1928 polymorphic_identity = ( 

1929 state.manager.mapper.polymorphic_identity 

1930 ) 

1931 if ( 

1932 polymorphic_identity is None 

1933 and state.manager.mapper.polymorphic_abstract 

1934 ): 

1935 raise sa_exc.InvalidRequestError( 

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

1937 "mapper is marked polymorphic_abstract=True" 

1938 ) 

1939 

1940 state.get_impl(polymorphic_key).set( 

1941 state, 

1942 dict_, 

1943 polymorphic_identity, 

1944 None, 

1945 ) 

1946 

1947 self._polymorphic_attr_key = polymorphic_key 

1948 

1949 def _validate_polymorphic_identity(mapper, state, dict_): 

1950 if ( 

1951 polymorphic_key in dict_ 

1952 and dict_[polymorphic_key] 

1953 not in mapper._acceptable_polymorphic_identities 

1954 ): 

1955 util.warn_limited( 

1956 "Flushing object %s with " 

1957 "incompatible polymorphic identity %r; the " 

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

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

1960 ) 

1961 

1962 self._set_polymorphic_identity = _set_polymorphic_identity 

1963 self._validate_polymorphic_identity = ( 

1964 _validate_polymorphic_identity 

1965 ) 

1966 else: 

1967 self._polymorphic_attr_key = None 

1968 self._set_polymorphic_identity = None 

1969 

1970 _validate_polymorphic_identity = None 

1971 

1972 @HasMemoized.memoized_attribute 

1973 def _version_id_prop(self): 

1974 if self.version_id_col is not None: 

1975 return self._columntoproperty[self.version_id_col] 

1976 else: 

1977 return None 

1978 

1979 @HasMemoized.memoized_attribute 

1980 def _acceptable_polymorphic_identities(self): 

1981 identities = set() 

1982 

1983 stack = deque([self]) 

1984 while stack: 

1985 item = stack.popleft() 

1986 if item.persist_selectable is self.persist_selectable: 

1987 identities.add(item.polymorphic_identity) 

1988 stack.extend(item._inheriting_mappers) 

1989 

1990 return identities 

1991 

1992 @HasMemoized.memoized_attribute 

1993 def _prop_set(self): 

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

1995 

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

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

1998 descriptor_props = util.preloaded.orm_descriptor_props 

1999 

2000 if not self.concrete: 

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

2002 elif key not in self._props: 

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

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

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

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

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

2008 # descriptors that might have side effects when invoked. 

2009 implementing_attribute = self.class_manager._get_class_attr_mro( 

2010 key, prop 

2011 ) 

2012 if implementing_attribute is prop or ( 

2013 isinstance( 

2014 implementing_attribute, attributes.InstrumentedAttribute 

2015 ) 

2016 and implementing_attribute._parententity is prop.parent 

2017 ): 

2018 self._configure_property( 

2019 key, 

2020 descriptor_props.ConcreteInheritedProperty(), 

2021 init=init, 

2022 setparent=True, 

2023 ) 

2024 

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

2026 def _configure_property( 

2027 self, 

2028 key: str, 

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

2030 *, 

2031 init: bool = True, 

2032 setparent: bool = True, 

2033 warn_for_existing: bool = False, 

2034 ) -> MapperProperty[Any]: 

2035 descriptor_props = util.preloaded.orm_descriptor_props 

2036 self._log( 

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

2038 ) 

2039 

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

2041 # ensure a Column is turned into a ColumnProperty. 

2042 # see #12858 

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

2044 

2045 if not isinstance(prop_arg, MapperProperty): 

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

2047 key, prop_arg, early_setup 

2048 ) 

2049 else: 

2050 prop = prop_arg 

2051 

2052 if early_setup: 

2053 return prop 

2054 

2055 if isinstance(prop, properties.ColumnProperty): 

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

2057 

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

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

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

2061 if col is None and self.inherits: 

2062 path = [self] 

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

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

2065 if col is not None: 

2066 for m2 in path: 

2067 m2.persist_selectable._refresh_for_new_column(col) 

2068 col = self.persist_selectable.corresponding_column( 

2069 prop.columns[0] 

2070 ) 

2071 break 

2072 path.append(m) 

2073 

2074 # subquery expression, column not present in the mapped 

2075 # selectable. 

2076 if col is None: 

2077 col = prop.columns[0] 

2078 

2079 # column is coming in after _readonly_props was 

2080 # initialized; check for 'readonly' 

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

2082 not hasattr(col, "table") 

2083 or col.table not in self._cols_by_table 

2084 ): 

2085 self._readonly_props.add(prop) 

2086 

2087 else: 

2088 # if column is coming in after _cols_by_table was 

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

2090 if ( 

2091 hasattr(self, "_cols_by_table") 

2092 and col.table in self._cols_by_table 

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

2094 ): 

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

2096 

2097 # if this properties.ColumnProperty represents the "polymorphic 

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

2099 # columns in SELECT statements. 

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

2101 prop._is_polymorphic_discriminator = ( 

2102 col is self.polymorphic_on 

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

2104 ) 

2105 

2106 if isinstance(col, expression.Label): 

2107 # new in 1.4, get column property against expressions 

2108 # to be addressable in subqueries 

2109 col.key = col._tq_key_label = key 

2110 

2111 self.columns.add(col, key) 

2112 

2113 for col in prop.columns: 

2114 for proxy_col in col.proxy_set: 

2115 self._columntoproperty[proxy_col] = prop 

2116 

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

2118 util.warn( 

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

2120 "assigned to attribute " 

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

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

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

2124 ) 

2125 

2126 prop.key = key 

2127 

2128 if setparent: 

2129 prop.set_parent(self, init) 

2130 

2131 if key in self._props and getattr( 

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

2133 ): 

2134 syn = self._props[key]._mapped_by_synonym 

2135 raise sa_exc.ArgumentError( 

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

2137 "a ColumnProperty already exists keyed to the name " 

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

2139 ) 

2140 

2141 # replacement cases 

2142 

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

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

2145 if ( 

2146 key in self._props 

2147 and not isinstance( 

2148 self._props[key], descriptor_props.ConcreteInheritedProperty 

2149 ) 

2150 and not isinstance(prop, descriptor_props.SynonymProperty) 

2151 ): 

2152 if warn_for_existing: 

2153 util.warn_deprecated( 

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

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

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

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

2158 "release", 

2159 "2.0", 

2160 ) 

2161 oldprop = self._props[key] 

2162 self._path_registry.pop(oldprop, None) 

2163 

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

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

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

2167 # get replaced. 

2168 elif ( 

2169 warn_for_existing 

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

2171 and not isinstance(prop, descriptor_props.SynonymProperty) 

2172 and not isinstance( 

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

2174 descriptor_props.ConcreteInheritedProperty, 

2175 ) 

2176 ): 

2177 util.warn_deprecated( 

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

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

2180 "attribute of the same name. " 

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

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

2183 "release", 

2184 "2.0", 

2185 ) 

2186 

2187 self._props[key] = prop 

2188 

2189 prop.instrument_class(self) 

2190 

2191 for mapper in self._inheriting_mappers: 

2192 mapper._adapt_inherited_property(key, prop, init) 

2193 

2194 if init: 

2195 prop.init() 

2196 prop.post_instrument_class(self) 

2197 

2198 if self.configured: 

2199 self._expire_memoizations() 

2200 

2201 return prop 

2202 

2203 def _make_prop_from_column( 

2204 self, 

2205 key: str, 

2206 column: Union[ 

2207 Sequence[KeyedColumnElement[Any]], KeyedColumnElement[Any] 

2208 ], 

2209 ) -> ColumnProperty[Any]: 

2210 columns = util.to_list(column) 

2211 mapped_column = [] 

2212 for c in columns: 

2213 mc = self.persist_selectable.corresponding_column(c) 

2214 if mc is None: 

2215 mc = self.local_table.corresponding_column(c) 

2216 if mc is not None: 

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

2218 # mapped table, this corresponds to adding a 

2219 # column after the fact to the local table. 

2220 # [ticket:1523] 

2221 self.persist_selectable._refresh_for_new_column(mc) 

2222 mc = self.persist_selectable.corresponding_column(c) 

2223 if mc is None: 

2224 raise sa_exc.ArgumentError( 

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

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

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

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

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

2230 ) 

2231 mapped_column.append(mc) 

2232 return properties.ColumnProperty(*mapped_column) 

2233 

2234 def _reconcile_prop_with_incoming_columns( 

2235 self, 

2236 key: str, 

2237 existing_prop: MapperProperty[Any], 

2238 warn_only: bool, 

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

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

2241 ) -> ColumnProperty[Any]: 

2242 if incoming_prop and ( 

2243 self.concrete 

2244 or not isinstance(existing_prop, properties.ColumnProperty) 

2245 ): 

2246 return incoming_prop 

2247 

2248 existing_column = existing_prop.columns[0] 

2249 

2250 if incoming_prop and existing_column in incoming_prop.columns: 

2251 return incoming_prop 

2252 

2253 if incoming_prop is None: 

2254 assert single_column is not None 

2255 incoming_column = single_column 

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

2257 else: 

2258 assert single_column is None 

2259 incoming_column = incoming_prop.columns[0] 

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

2261 

2262 if ( 

2263 ( 

2264 not self._inherits_equated_pairs 

2265 or (equated_pair_key not in self._inherits_equated_pairs) 

2266 ) 

2267 and not existing_column.shares_lineage(incoming_column) 

2268 and existing_column is not self.version_id_col 

2269 and incoming_column is not self.version_id_col 

2270 ): 

2271 msg = ( 

2272 "Implicitly combining column %s with column " 

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

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

2275 "explicitly." 

2276 % ( 

2277 existing_prop.columns[-1], 

2278 incoming_column, 

2279 key, 

2280 ) 

2281 ) 

2282 if warn_only: 

2283 util.warn(msg) 

2284 else: 

2285 raise sa_exc.InvalidRequestError(msg) 

2286 

2287 # existing properties.ColumnProperty from an inheriting 

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

2289 new_prop = existing_prop.copy() 

2290 

2291 new_prop.columns.insert(0, incoming_column) 

2292 self._log( 

2293 "inserting column to existing list " 

2294 "in properties.ColumnProperty %s", 

2295 key, 

2296 ) 

2297 return new_prop # type: ignore 

2298 

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

2300 def _property_from_column( 

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

2302 ) -> ColumnProperty[Any]: 

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

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

2305 

2306 descriptor_props = util.preloaded.orm_descriptor_props 

2307 

2308 if early_setup: 

2309 prop = None 

2310 else: 

2311 prop = self._props.get(key) 

2312 

2313 if isinstance(prop, properties.ColumnProperty): 

2314 return self._reconcile_prop_with_incoming_columns( 

2315 key, 

2316 prop, 

2317 single_column=column, 

2318 warn_only=prop.parent is not self, 

2319 ) 

2320 elif prop is None or isinstance( 

2321 prop, descriptor_props.ConcreteInheritedProperty 

2322 ): 

2323 return self._make_prop_from_column(key, column) 

2324 else: 

2325 raise sa_exc.ArgumentError( 

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

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

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

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

2330 "to remove all awareness of the column entirely " 

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

2332 "use the 'include_properties' or 'exclude_properties' " 

2333 "mapper arguments to control specifically which table " 

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

2335 ) 

2336 

2337 @util.langhelpers.tag_method_for_warnings( 

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

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

2340 "operation.", 

2341 sa_exc.SAWarning, 

2342 ) 

2343 def _check_configure(self) -> None: 

2344 if self.registry._new_mappers: 

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

2346 

2347 def _post_configure_properties(self) -> None: 

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

2349 attached to this mapper. 

2350 

2351 This is a deferred configuration step which is intended 

2352 to execute once all mappers have been constructed. 

2353 

2354 """ 

2355 

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

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

2358 for key, prop in l: 

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

2360 

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

2362 prop.init() 

2363 

2364 if prop._configure_finished: 

2365 prop.post_instrument_class(self) 

2366 

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

2368 self.configured = True 

2369 

2370 def add_properties(self, dict_of_properties): 

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

2372 using `add_property`. 

2373 

2374 """ 

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

2376 self.add_property(key, value) 

2377 

2378 def add_property( 

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

2380 ) -> None: 

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

2382 

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

2384 property to the initial properties dictionary sent to the 

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

2386 the given MapperProperty is configured immediately. 

2387 

2388 """ 

2389 prop = self._configure_property( 

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

2391 ) 

2392 assert isinstance(prop, MapperProperty) 

2393 self._init_properties[key] = prop 

2394 

2395 def _expire_memoizations(self) -> None: 

2396 for mapper in self.iterate_to_root(): 

2397 mapper._reset_memoizations() 

2398 

2399 @property 

2400 def _log_desc(self) -> str: 

2401 return ( 

2402 "(" 

2403 + self.class_.__name__ 

2404 + "|" 

2405 + ( 

2406 self.local_table is not None 

2407 and self.local_table.description 

2408 or str(self.local_table) 

2409 ) 

2410 + ")" 

2411 ) 

2412 

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

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

2415 

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

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

2418 

2419 def __repr__(self) -> str: 

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

2421 

2422 def __str__(self) -> str: 

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

2424 self.class_.__name__, 

2425 ( 

2426 self.local_table.description 

2427 if self.local_table is not None 

2428 else self.persist_selectable.description 

2429 ), 

2430 ) 

2431 

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

2433 orphan_possible = False 

2434 for mapper in self.iterate_to_root(): 

2435 for key, cls in mapper._delete_orphans: 

2436 orphan_possible = True 

2437 

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

2439 state, key, optimistic=state.has_identity 

2440 ) 

2441 

2442 if self.legacy_is_orphan and has_parent: 

2443 return False 

2444 elif not self.legacy_is_orphan and not has_parent: 

2445 return True 

2446 

2447 if self.legacy_is_orphan: 

2448 return orphan_possible 

2449 else: 

2450 return False 

2451 

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

2453 return key in self._props 

2454 

2455 def get_property( 

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

2457 ) -> MapperProperty[Any]: 

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

2459 

2460 if _configure_mappers: 

2461 self._check_configure() 

2462 

2463 try: 

2464 return self._props[key] 

2465 except KeyError as err: 

2466 raise sa_exc.InvalidRequestError( 

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

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

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

2470 ) from err 

2471 

2472 def get_property_by_column( 

2473 self, column: ColumnElement[_T] 

2474 ) -> MapperProperty[_T]: 

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

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

2477 

2478 return self._columntoproperty[column] 

2479 

2480 @property 

2481 def iterate_properties(self): 

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

2483 

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

2485 

2486 def _mappers_from_spec( 

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

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

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

2490 represents. 

2491 

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

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

2494 

2495 """ 

2496 if spec == "*": 

2497 mappers = list(self.self_and_descendants) 

2498 elif spec: 

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

2500 for m in util.to_list(spec): 

2501 m = _class_to_mapper(m) 

2502 if not m.isa(self): 

2503 raise sa_exc.InvalidRequestError( 

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

2505 ) 

2506 

2507 if selectable is None: 

2508 mapper_set.update(m.iterate_to_root()) 

2509 else: 

2510 mapper_set.add(m) 

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

2512 else: 

2513 mappers = [] 

2514 

2515 if selectable is not None: 

2516 tables = set( 

2517 sql_util.find_tables(selectable, include_aliases=True) 

2518 ) 

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

2520 return mappers 

2521 

2522 def _selectable_from_mappers( 

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

2524 ) -> FromClause: 

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

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

2527 mapped tables. 

2528 

2529 """ 

2530 from_obj = self.persist_selectable 

2531 for m in mappers: 

2532 if m is self: 

2533 continue 

2534 if m.concrete: 

2535 raise sa_exc.InvalidRequestError( 

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

2537 "when concrete-inheriting mappers are used." 

2538 ) 

2539 elif not m.single: 

2540 if innerjoin: 

2541 from_obj = from_obj.join( 

2542 m.local_table, m.inherit_condition 

2543 ) 

2544 else: 

2545 from_obj = from_obj.outerjoin( 

2546 m.local_table, m.inherit_condition 

2547 ) 

2548 

2549 return from_obj 

2550 

2551 @HasMemoized.memoized_attribute 

2552 def _version_id_has_server_side_value(self) -> bool: 

2553 vid_col = self.version_id_col 

2554 

2555 if vid_col is None: 

2556 return False 

2557 

2558 elif not isinstance(vid_col, Column): 

2559 return True 

2560 else: 

2561 return vid_col.server_default is not None or ( 

2562 vid_col.default is not None 

2563 and ( 

2564 not vid_col.default.is_scalar 

2565 and not vid_col.default.is_callable 

2566 ) 

2567 ) 

2568 

2569 @HasMemoized.memoized_attribute 

2570 def _single_table_criteria_component(self): 

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

2572 

2573 hierarchy = tuple( 

2574 m.polymorphic_identity 

2575 for m in self.self_and_descendants 

2576 if not m.polymorphic_abstract 

2577 ) 

2578 

2579 return ( 

2580 self.polymorphic_on._annotate( 

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

2582 ), 

2583 hierarchy, 

2584 ) 

2585 else: 

2586 return None 

2587 

2588 @HasMemoized.memoized_attribute 

2589 def _single_table_criterion(self): 

2590 component = self._single_table_criteria_component 

2591 if component is not None: 

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

2593 else: 

2594 return None 

2595 

2596 @HasMemoized.memoized_attribute 

2597 def _has_aliased_polymorphic_fromclause(self): 

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

2599 like a subquery. 

2600 

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

2602 if this is present. 

2603 

2604 """ 

2605 return self.with_polymorphic and isinstance( 

2606 self.with_polymorphic[1], 

2607 expression.AliasedReturnsRows, 

2608 ) 

2609 

2610 @HasMemoized.memoized_attribute 

2611 def _should_select_with_poly_adapter(self): 

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

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

2614 rows for mapped classes and subclasses against this Mapper. 

2615 

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

2617 for this condition. 

2618 

2619 """ 

2620 

2621 # this has been simplified as of #8456. 

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

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

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

2625 # 

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

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

2628 # flattened JOIN for with_polymorphic.) 

2629 # 

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

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

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

2633 # some kind or polymorphic_union. 

2634 # 

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

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

2637 # on it (such as test_join_from_polymorphic_explicit_aliased_three). 

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

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

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

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

2642 # legacy case we should probably disable. 

2643 # 

2644 # 

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

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

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

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

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

2650 # polymorphic base class. 

2651 # 

2652 return ( 

2653 self._has_aliased_polymorphic_fromclause 

2654 or self._requires_row_aliasing 

2655 or (self.base_mapper._has_aliased_polymorphic_fromclause) 

2656 or self.base_mapper._requires_row_aliasing 

2657 ) 

2658 

2659 @HasMemoized.memoized_attribute 

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

2661 self._check_configure() 

2662 

2663 if not self.with_polymorphic: 

2664 return [] 

2665 return self._mappers_from_spec(*self.with_polymorphic) 

2666 

2667 @HasMemoized.memoized_attribute 

2668 def _post_inspect(self): 

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

2670 

2671 E.g. when Query calls: 

2672 

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

2674 

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

2676 

2677 """ 

2678 self._check_configure() 

2679 

2680 @HasMemoized_ro_memoized_attribute 

2681 def _with_polymorphic_selectable(self) -> FromClause: 

2682 if not self.with_polymorphic: 

2683 return self.persist_selectable 

2684 

2685 spec, selectable = self.with_polymorphic 

2686 if selectable is not None: 

2687 return selectable 

2688 else: 

2689 return self._selectable_from_mappers( 

2690 self._mappers_from_spec(spec, selectable), False 

2691 ) 

2692 

2693 with_polymorphic_mappers = _with_polymorphic_mappers 

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

2695 default "polymorphic" query. 

2696 

2697 """ 

2698 

2699 @HasMemoized_ro_memoized_attribute 

2700 def _insert_cols_evaluating_none(self): 

2701 return { 

2702 table: frozenset( 

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

2704 ) 

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

2706 } 

2707 

2708 @HasMemoized.memoized_attribute 

2709 def _insert_cols_as_none(self): 

2710 return { 

2711 table: frozenset( 

2712 col.key 

2713 for col in columns 

2714 if not col.primary_key 

2715 and not col.server_default 

2716 and not col.default 

2717 and not col.type.should_evaluate_none 

2718 ) 

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

2720 } 

2721 

2722 @HasMemoized.memoized_attribute 

2723 def _propkey_to_col(self): 

2724 return { 

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

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

2727 } 

2728 

2729 @HasMemoized.memoized_attribute 

2730 def _pk_keys_by_table(self): 

2731 return { 

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

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

2734 } 

2735 

2736 @HasMemoized.memoized_attribute 

2737 def _pk_attr_keys_by_table(self): 

2738 return { 

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

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

2741 } 

2742 

2743 @HasMemoized.memoized_attribute 

2744 def _server_default_cols( 

2745 self, 

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

2747 return { 

2748 table: frozenset( 

2749 [ 

2750 col 

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

2752 if col.server_default is not None 

2753 or ( 

2754 col.default is not None 

2755 and col.default.is_clause_element 

2756 ) 

2757 ] 

2758 ) 

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

2760 } 

2761 

2762 @HasMemoized.memoized_attribute 

2763 def _server_onupdate_default_cols( 

2764 self, 

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

2766 return { 

2767 table: frozenset( 

2768 [ 

2769 col 

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

2771 if col.server_onupdate is not None 

2772 or ( 

2773 col.onupdate is not None 

2774 and col.onupdate.is_clause_element 

2775 ) 

2776 ] 

2777 ) 

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

2779 } 

2780 

2781 @HasMemoized.memoized_attribute 

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

2783 return { 

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

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

2786 } 

2787 

2788 @HasMemoized.memoized_attribute 

2789 def _server_onupdate_default_col_keys( 

2790 self, 

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

2792 return { 

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

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

2795 } 

2796 

2797 @HasMemoized.memoized_attribute 

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

2799 result: Set[str] = set() 

2800 

2801 col_to_property = self._columntoproperty 

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

2803 result.update( 

2804 col_to_property[col].key 

2805 for col in columns.intersection(col_to_property) 

2806 ) 

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

2808 result.update( 

2809 col_to_property[col].key 

2810 for col in columns.intersection(col_to_property) 

2811 ) 

2812 return result 

2813 

2814 @HasMemoized.memoized_instancemethod 

2815 def __clause_element__(self): 

2816 annotations: Dict[str, Any] = { 

2817 "entity_namespace": self, 

2818 "parententity": self, 

2819 "parentmapper": self, 

2820 } 

2821 if self.persist_selectable is not self.local_table: 

2822 # joined table inheritance, with polymorphic selectable, 

2823 # etc. 

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

2825 { 

2826 "entity_namespace": self, 

2827 "parententity": self, 

2828 "parentmapper": self, 

2829 } 

2830 )._set_propagate_attrs( 

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

2832 ) 

2833 

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

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

2836 ) 

2837 

2838 @util.memoized_property 

2839 def select_identity_token(self): 

2840 return ( 

2841 expression.null() 

2842 ._annotate( 

2843 { 

2844 "entity_namespace": self, 

2845 "parententity": self, 

2846 "parentmapper": self, 

2847 "identity_token": True, 

2848 } 

2849 ) 

2850 ._set_propagate_attrs( 

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

2852 ) 

2853 ) 

2854 

2855 @property 

2856 def selectable(self) -> FromClause: 

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

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

2859 

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

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

2862 full "polymorphic" selectable is returned. 

2863 

2864 """ 

2865 return self._with_polymorphic_selectable 

2866 

2867 def _with_polymorphic_args( 

2868 self, 

2869 spec: Any = None, 

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

2871 innerjoin: bool = False, 

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

2873 if selectable not in (None, False): 

2874 selectable = coercions.expect( 

2875 roles.FromClauseRole, 

2876 selectable, 

2877 ) 

2878 

2879 if self.with_polymorphic: 

2880 if not spec: 

2881 spec = self.with_polymorphic[0] 

2882 if selectable is False: 

2883 selectable = self.with_polymorphic[1] 

2884 elif selectable is False: 

2885 selectable = None 

2886 mappers = self._mappers_from_spec(spec, selectable) 

2887 if selectable is not None: 

2888 return mappers, selectable 

2889 else: 

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

2891 

2892 @HasMemoized.memoized_attribute 

2893 def _polymorphic_properties(self): 

2894 return list( 

2895 self._iterate_polymorphic_properties( 

2896 self._with_polymorphic_mappers 

2897 ) 

2898 ) 

2899 

2900 @property 

2901 def _all_column_expressions(self): 

2902 poly_properties = self._polymorphic_properties 

2903 adapter = self._polymorphic_adapter 

2904 

2905 return [ 

2906 adapter.columns[c] if adapter else c 

2907 for prop in poly_properties 

2908 if isinstance(prop, properties.ColumnProperty) 

2909 and prop._renders_in_subqueries 

2910 for c in prop.columns 

2911 ] 

2912 

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

2914 if polymorphic_mappers: 

2915 poly_properties = self._iterate_polymorphic_properties( 

2916 polymorphic_mappers 

2917 ) 

2918 else: 

2919 poly_properties = self._polymorphic_properties 

2920 

2921 return [ 

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

2923 for prop in poly_properties 

2924 if isinstance(prop, properties.ColumnProperty) 

2925 ] 

2926 

2927 @HasMemoized.memoized_attribute 

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

2929 if self._has_aliased_polymorphic_fromclause: 

2930 return orm_util.ORMAdapter( 

2931 orm_util._TraceAdaptRole.MAPPER_POLYMORPHIC_ADAPTER, 

2932 self, 

2933 selectable=self.selectable, 

2934 equivalents=self._equivalent_columns, 

2935 limit_on_entity=False, 

2936 ) 

2937 else: 

2938 return None 

2939 

2940 def _iterate_polymorphic_properties(self, mappers=None): 

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

2942 a SELECT.""" 

2943 if mappers is None: 

2944 mappers = self._with_polymorphic_mappers 

2945 

2946 if not mappers: 

2947 for c in self.iterate_properties: 

2948 yield c 

2949 else: 

2950 # in the polymorphic case, filter out discriminator columns 

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

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

2953 for c in util.unique_list( 

2954 chain( 

2955 *[ 

2956 list(mapper.iterate_properties) 

2957 for mapper in [self] + mappers 

2958 ] 

2959 ) 

2960 ): 

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

2962 self.polymorphic_on is None 

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

2964 ): 

2965 continue 

2966 yield c 

2967 

2968 @HasMemoized.memoized_attribute 

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

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

2971 associated this mapper. 

2972 

2973 This is an object that provides each property based on 

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

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

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

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

2978 column. The namespace object can also be iterated, 

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

2980 

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

2982 of this attribute which limit the types of properties 

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

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

2985 

2986 .. warning:: 

2987 

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

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

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

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

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

2993 accessing attributes dynamically, favor using the dict-access 

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

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

2996 

2997 .. seealso:: 

2998 

2999 :attr:`_orm.Mapper.all_orm_descriptors` 

3000 

3001 """ 

3002 

3003 self._check_configure() 

3004 return util.ReadOnlyProperties(self._props) 

3005 

3006 @HasMemoized.memoized_attribute 

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

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

3009 with the mapped class. 

3010 

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

3012 associated with the mapped class or its superclasses. 

3013 

3014 This namespace includes attributes that are mapped to the class 

3015 as well as attributes declared by extension modules. 

3016 It includes any Python descriptor type that inherits from 

3017 :class:`.InspectionAttr`. This includes 

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

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

3020 :class:`.AssociationProxy`. 

3021 

3022 To distinguish between mapped attributes and extension attributes, 

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

3024 to a constant that distinguishes between different extension types. 

3025 

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

3027 

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

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

3030 

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

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

3033 3 below. The order will be the 

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

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

3036 or the mapper. 

3037 

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

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

3040 class in which it first appeared. 

3041 

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

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

3044 

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

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

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

3048 referring to the collection of mapped properties via 

3049 :attr:`_orm.Mapper.attrs`. 

3050 

3051 .. warning:: 

3052 

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

3054 accessor namespace is an 

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

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

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

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

3059 accessing attributes dynamically, favor using the dict-access 

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

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

3062 collisions. 

3063 

3064 .. seealso:: 

3065 

3066 :attr:`_orm.Mapper.attrs` 

3067 

3068 """ 

3069 return util.ReadOnlyProperties( 

3070 dict(self.class_manager._all_sqla_attributes()) 

3071 ) 

3072 

3073 @HasMemoized.memoized_attribute 

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

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

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

3077 all synonyms that refer to primary key columns 

3078 

3079 """ 

3080 descriptor_props = util.preloaded.orm_descriptor_props 

3081 

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

3083 

3084 return { 

3085 syn.key: syn.name 

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

3087 if isinstance(syn, descriptor_props.SynonymProperty) 

3088 and syn.name in pk_keys 

3089 } 

3090 

3091 @HasMemoized.memoized_attribute 

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

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

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

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

3096 

3097 .. seealso:: 

3098 

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

3100 :class:`.MapperProperty` 

3101 objects. 

3102 

3103 """ 

3104 descriptor_props = util.preloaded.orm_descriptor_props 

3105 

3106 return self._filter_properties(descriptor_props.SynonymProperty) 

3107 

3108 @util.ro_non_memoized_property 

3109 def entity_namespace(self) -> _EntityNamespace: 

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

3111 

3112 @HasMemoized.memoized_attribute 

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

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

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

3116 

3117 .. seealso:: 

3118 

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

3120 :class:`.MapperProperty` 

3121 objects. 

3122 

3123 """ 

3124 return self._filter_properties(properties.ColumnProperty) 

3125 

3126 @HasMemoized.memoized_attribute 

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

3128 def relationships( 

3129 self, 

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

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

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

3133 

3134 .. warning:: 

3135 

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

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

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

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

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

3141 accessing attributes dynamically, favor using the dict-access 

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

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

3144 collisions. 

3145 

3146 .. seealso:: 

3147 

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

3149 :class:`.MapperProperty` 

3150 objects. 

3151 

3152 """ 

3153 return self._filter_properties( 

3154 util.preloaded.orm_relationships.RelationshipProperty 

3155 ) 

3156 

3157 @HasMemoized.memoized_attribute 

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

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

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

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

3162 

3163 .. seealso:: 

3164 

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

3166 :class:`.MapperProperty` 

3167 objects. 

3168 

3169 """ 

3170 return self._filter_properties( 

3171 util.preloaded.orm_descriptor_props.CompositeProperty 

3172 ) 

3173 

3174 def _filter_properties( 

3175 self, type_: Type[_MP] 

3176 ) -> util.ReadOnlyProperties[_MP]: 

3177 self._check_configure() 

3178 return util.ReadOnlyProperties( 

3179 util.OrderedDict( 

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

3181 ) 

3182 ) 

3183 

3184 @HasMemoized.memoized_attribute 

3185 def _get_clause(self): 

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

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

3188 by primary key. 

3189 

3190 """ 

3191 params = [ 

3192 ( 

3193 primary_key, 

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

3195 ) 

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

3197 ] 

3198 return ( 

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

3200 util.column_dict(params), 

3201 ) 

3202 

3203 @HasMemoized.memoized_attribute 

3204 def _equivalent_columns(self) -> _EquivalentColumnMap: 

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

3206 the determination of column pairs that are equated to 

3207 one another based on inherit condition. This is designed 

3208 to work with the queries that util.polymorphic_union 

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

3210 the base table directly (including the subclass table columns 

3211 only). 

3212 

3213 The resulting structure is a dictionary of columns mapped 

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

3215 

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

3217 

3218 """ # noqa: E501 

3219 result: _EquivalentColumnMap = {} 

3220 

3221 def visit_binary(binary): 

3222 if binary.operator == operators.eq: 

3223 if binary.left in result: 

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

3225 else: 

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

3227 if binary.right in result: 

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

3229 else: 

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

3231 

3232 for mapper in self.base_mapper.self_and_descendants: 

3233 if mapper.inherit_condition is not None: 

3234 visitors.traverse( 

3235 mapper.inherit_condition, {}, {"binary": visit_binary} 

3236 ) 

3237 

3238 return result 

3239 

3240 def _is_userland_descriptor(self, assigned_name: str, obj: Any) -> bool: 

3241 if isinstance( 

3242 obj, 

3243 ( 

3244 _MappedAttribute, 

3245 instrumentation.ClassManager, 

3246 expression.ColumnElement, 

3247 ), 

3248 ): 

3249 return False 

3250 else: 

3251 return assigned_name not in self._dataclass_fields 

3252 

3253 @HasMemoized.memoized_attribute 

3254 def _dataclass_fields(self): 

3255 return [f.name for f in util.dataclass_fields(self.class_)] 

3256 

3257 def _should_exclude(self, name, assigned_name, local, column): 

3258 """determine whether a particular property should be implicitly 

3259 present on the class. 

3260 

3261 This occurs when properties are propagated from an inherited class, or 

3262 are applied from the columns present in the mapped table. 

3263 

3264 """ 

3265 

3266 if column is not None and sql_base._never_select_column(column): 

3267 return True 

3268 

3269 # check for class-bound attributes and/or descriptors, 

3270 # either local or from an inherited class 

3271 # ignore dataclass field default values 

3272 if local: 

3273 if self.class_.__dict__.get( 

3274 assigned_name, None 

3275 ) is not None and self._is_userland_descriptor( 

3276 assigned_name, self.class_.__dict__[assigned_name] 

3277 ): 

3278 return True 

3279 else: 

3280 attr = self.class_manager._get_class_attr_mro(assigned_name, None) 

3281 if attr is not None and self._is_userland_descriptor( 

3282 assigned_name, attr 

3283 ): 

3284 return True 

3285 

3286 if ( 

3287 self.include_properties is not None 

3288 and name not in self.include_properties 

3289 and (column is None or column not in self.include_properties) 

3290 ): 

3291 self._log("not including property %s" % (name)) 

3292 return True 

3293 

3294 if self.exclude_properties is not None and ( 

3295 name in self.exclude_properties 

3296 or (column is not None and column in self.exclude_properties) 

3297 ): 

3298 self._log("excluding property %s" % (name)) 

3299 return True 

3300 

3301 return False 

3302 

3303 def common_parent(self, other: Mapper[Any]) -> bool: 

3304 """Return true if the given mapper shares a 

3305 common inherited parent as this mapper.""" 

3306 

3307 return self.base_mapper is other.base_mapper 

3308 

3309 def is_sibling(self, other: Mapper[Any]) -> bool: 

3310 """return true if the other mapper is an inheriting sibling to this 

3311 one. common parent but different branch 

3312 

3313 """ 

3314 return ( 

3315 self.base_mapper is other.base_mapper 

3316 and not self.isa(other) 

3317 and not other.isa(self) 

3318 ) 

3319 

3320 def _canload( 

3321 self, state: InstanceState[Any], allow_subtypes: bool 

3322 ) -> bool: 

3323 s = self.primary_mapper() 

3324 if self.polymorphic_on is not None or allow_subtypes: 

3325 return _state_mapper(state).isa(s) 

3326 else: 

3327 return _state_mapper(state) is s 

3328 

3329 def isa(self, other: Mapper[Any]) -> bool: 

3330 """Return True if the this mapper inherits from the given mapper.""" 

3331 

3332 m: Optional[Mapper[Any]] = self 

3333 while m and m is not other: 

3334 m = m.inherits 

3335 return bool(m) 

3336 

3337 def iterate_to_root(self) -> Iterator[Mapper[Any]]: 

3338 m: Optional[Mapper[Any]] = self 

3339 while m: 

3340 yield m 

3341 m = m.inherits 

3342 

3343 @HasMemoized.memoized_attribute 

3344 def self_and_descendants(self) -> Sequence[Mapper[Any]]: 

3345 """The collection including this mapper and all descendant mappers. 

3346 

3347 This includes not just the immediately inheriting mappers but 

3348 all their inheriting mappers as well. 

3349 

3350 """ 

3351 descendants = [] 

3352 stack = deque([self]) 

3353 while stack: 

3354 item = stack.popleft() 

3355 descendants.append(item) 

3356 stack.extend(item._inheriting_mappers) 

3357 return util.WeakSequence(descendants) 

3358 

3359 def polymorphic_iterator(self) -> Iterator[Mapper[Any]]: 

3360 """Iterate through the collection including this mapper and 

3361 all descendant mappers. 

3362 

3363 This includes not just the immediately inheriting mappers but 

3364 all their inheriting mappers as well. 

3365 

3366 To iterate through an entire hierarchy, use 

3367 ``mapper.base_mapper.polymorphic_iterator()``. 

3368 

3369 """ 

3370 return iter(self.self_and_descendants) 

3371 

3372 def primary_mapper(self) -> Mapper[Any]: 

3373 """Return the primary mapper corresponding to this mapper's class key 

3374 (class).""" 

3375 

3376 return self.class_manager.mapper 

3377 

3378 @property 

3379 def primary_base_mapper(self) -> Mapper[Any]: 

3380 return self.class_manager.mapper.base_mapper 

3381 

3382 def _result_has_identity_key(self, result, adapter=None): 

3383 pk_cols: Sequence[ColumnElement[Any]] 

3384 if adapter is not None: 

3385 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3386 else: 

3387 pk_cols = self.primary_key 

3388 rk = result.keys() 

3389 for col in pk_cols: 

3390 if col not in rk: 

3391 return False 

3392 else: 

3393 return True 

3394 

3395 def identity_key_from_row( 

3396 self, 

3397 row: Union[Row[Unpack[TupleAny]], RowMapping], 

3398 identity_token: Optional[Any] = None, 

3399 adapter: Optional[ORMAdapter] = None, 

3400 ) -> _IdentityKeyType[_O]: 

3401 """Return an identity-map key for use in storing/retrieving an 

3402 item from the identity map. 

3403 

3404 :param row: A :class:`.Row` or :class:`.RowMapping` produced from a 

3405 result set that selected from the ORM mapped primary key columns. 

3406 

3407 .. versionchanged:: 2.0 

3408 :class:`.Row` or :class:`.RowMapping` are accepted 

3409 for the "row" argument 

3410 

3411 """ 

3412 pk_cols: Sequence[ColumnElement[Any]] 

3413 if adapter is not None: 

3414 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3415 else: 

3416 pk_cols = self.primary_key 

3417 

3418 mapping: RowMapping 

3419 if hasattr(row, "_mapping"): 

3420 mapping = row._mapping 

3421 else: 

3422 mapping = row # type: ignore[assignment] 

3423 

3424 return ( 

3425 self._identity_class, 

3426 tuple(mapping[column] for column in pk_cols), 

3427 identity_token, 

3428 ) 

3429 

3430 def identity_key_from_primary_key( 

3431 self, 

3432 primary_key: Tuple[Any, ...], 

3433 identity_token: Optional[Any] = None, 

3434 ) -> _IdentityKeyType[_O]: 

3435 """Return an identity-map key for use in storing/retrieving an 

3436 item from an identity map. 

3437 

3438 :param primary_key: A list of values indicating the identifier. 

3439 

3440 """ 

3441 return ( 

3442 self._identity_class, 

3443 tuple(primary_key), 

3444 identity_token, 

3445 ) 

3446 

3447 def identity_key_from_instance(self, instance: _O) -> _IdentityKeyType[_O]: 

3448 """Return the identity key for the given instance, based on 

3449 its primary key attributes. 

3450 

3451 If the instance's state is expired, calling this method 

3452 will result in a database check to see if the object has been deleted. 

3453 If the row no longer exists, 

3454 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3455 

3456 This value is typically also found on the instance state under the 

3457 attribute name `key`. 

3458 

3459 """ 

3460 state = attributes.instance_state(instance) 

3461 return self._identity_key_from_state(state, PassiveFlag.PASSIVE_OFF) 

3462 

3463 def _identity_key_from_state( 

3464 self, 

3465 state: InstanceState[_O], 

3466 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3467 ) -> _IdentityKeyType[_O]: 

3468 dict_ = state.dict 

3469 manager = state.manager 

3470 return ( 

3471 self._identity_class, 

3472 tuple( 

3473 [ 

3474 manager[prop.key].impl.get(state, dict_, passive) 

3475 for prop in self._identity_key_props 

3476 ] 

3477 ), 

3478 state.identity_token, 

3479 ) 

3480 

3481 def primary_key_from_instance(self, instance: _O) -> Tuple[Any, ...]: 

3482 """Return the list of primary key values for the given 

3483 instance. 

3484 

3485 If the instance's state is expired, calling this method 

3486 will result in a database check to see if the object has been deleted. 

3487 If the row no longer exists, 

3488 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3489 

3490 """ 

3491 state = attributes.instance_state(instance) 

3492 identity_key = self._identity_key_from_state( 

3493 state, PassiveFlag.PASSIVE_OFF 

3494 ) 

3495 return identity_key[1] 

3496 

3497 @HasMemoized.memoized_attribute 

3498 def _persistent_sortkey_fn(self): 

3499 key_fns = [col.type.sort_key_function for col in self.primary_key] 

3500 

3501 if set(key_fns).difference([None]): 

3502 

3503 def key(state): 

3504 return tuple( 

3505 key_fn(val) if key_fn is not None else val 

3506 for key_fn, val in zip(key_fns, state.key[1]) 

3507 ) 

3508 

3509 else: 

3510 

3511 def key(state): 

3512 return state.key[1] 

3513 

3514 return key 

3515 

3516 @HasMemoized.memoized_attribute 

3517 def _identity_key_props(self): 

3518 return [self._columntoproperty[col] for col in self.primary_key] 

3519 

3520 @HasMemoized.memoized_attribute 

3521 def _all_pk_cols(self): 

3522 collection: Set[ColumnClause[Any]] = set() 

3523 for table in self.tables: 

3524 collection.update(self._pks_by_table[table]) 

3525 return collection 

3526 

3527 @HasMemoized.memoized_attribute 

3528 def _should_undefer_in_wildcard(self): 

3529 cols: Set[ColumnElement[Any]] = set(self.primary_key) 

3530 if self.polymorphic_on is not None: 

3531 cols.add(self.polymorphic_on) 

3532 return cols 

3533 

3534 @HasMemoized.memoized_attribute 

3535 def _primary_key_propkeys(self): 

3536 return {self._columntoproperty[col].key for col in self._all_pk_cols} 

3537 

3538 def _get_state_attr_by_column( 

3539 self, 

3540 state: InstanceState[_O], 

3541 dict_: _InstanceDict, 

3542 column: ColumnElement[Any], 

3543 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3544 ) -> Any: 

3545 prop = self._columntoproperty[column] 

3546 return state.manager[prop.key].impl.get(state, dict_, passive=passive) 

3547 

3548 def _set_committed_state_attr_by_column(self, state, dict_, column, value): 

3549 prop = self._columntoproperty[column] 

3550 state.manager[prop.key].impl.set_committed_value(state, dict_, value) 

3551 

3552 def _set_state_attr_by_column(self, state, dict_, column, value): 

3553 prop = self._columntoproperty[column] 

3554 state.manager[prop.key].impl.set(state, dict_, value, None) 

3555 

3556 def _get_committed_attr_by_column(self, obj, column): 

3557 state = attributes.instance_state(obj) 

3558 dict_ = attributes.instance_dict(obj) 

3559 return self._get_committed_state_attr_by_column( 

3560 state, dict_, column, passive=PassiveFlag.PASSIVE_OFF 

3561 ) 

3562 

3563 def _get_committed_state_attr_by_column( 

3564 self, state, dict_, column, passive=PassiveFlag.PASSIVE_RETURN_NO_VALUE 

3565 ): 

3566 prop = self._columntoproperty[column] 

3567 return state.manager[prop.key].impl.get_committed_value( 

3568 state, dict_, passive=passive 

3569 ) 

3570 

3571 def _optimized_get_statement(self, state, attribute_names): 

3572 """assemble a WHERE clause which retrieves a given state by primary 

3573 key, using a minimized set of tables. 

3574 

3575 Applies to a joined-table inheritance mapper where the 

3576 requested attribute names are only present on joined tables, 

3577 not the base table. The WHERE clause attempts to include 

3578 only those tables to minimize joins. 

3579 

3580 """ 

3581 props = self._props 

3582 

3583 col_attribute_names = set(attribute_names).intersection( 

3584 state.mapper.column_attrs.keys() 

3585 ) 

3586 tables: Set[FromClause] = set( 

3587 chain( 

3588 *[ 

3589 sql_util.find_tables(c, check_columns=True) 

3590 for key in col_attribute_names 

3591 for c in props[key].columns 

3592 ] 

3593 ) 

3594 ) 

3595 

3596 if self.base_mapper.local_table in tables: 

3597 return None 

3598 

3599 def visit_binary(binary): 

3600 leftcol = binary.left 

3601 rightcol = binary.right 

3602 if leftcol is None or rightcol is None: 

3603 return 

3604 

3605 if leftcol.table not in tables: 

3606 leftval = self._get_committed_state_attr_by_column( 

3607 state, 

3608 state.dict, 

3609 leftcol, 

3610 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3611 ) 

3612 if leftval in orm_util._none_set: 

3613 raise _OptGetColumnsNotAvailable() 

3614 binary.left = sql.bindparam( 

3615 None, leftval, type_=binary.right.type 

3616 ) 

3617 elif rightcol.table not in tables: 

3618 rightval = self._get_committed_state_attr_by_column( 

3619 state, 

3620 state.dict, 

3621 rightcol, 

3622 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3623 ) 

3624 if rightval in orm_util._none_set: 

3625 raise _OptGetColumnsNotAvailable() 

3626 binary.right = sql.bindparam( 

3627 None, rightval, type_=binary.right.type 

3628 ) 

3629 

3630 allconds: List[ColumnElement[bool]] = [] 

3631 

3632 start = False 

3633 

3634 # as of #7507, from the lowest base table on upwards, 

3635 # we include all intermediary tables. 

3636 

3637 for mapper in reversed(list(self.iterate_to_root())): 

3638 if mapper.local_table in tables: 

3639 start = True 

3640 elif not isinstance(mapper.local_table, expression.TableClause): 

3641 return None 

3642 if start and not mapper.single: 

3643 assert mapper.inherits 

3644 assert not mapper.concrete 

3645 assert mapper.inherit_condition is not None 

3646 allconds.append(mapper.inherit_condition) 

3647 tables.add(mapper.local_table) 

3648 

3649 # only the bottom table needs its criteria to be altered to fit 

3650 # the primary key ident - the rest of the tables upwards to the 

3651 # descendant-most class should all be present and joined to each 

3652 # other. 

3653 try: 

3654 _traversed = visitors.cloned_traverse( 

3655 allconds[0], {}, {"binary": visit_binary} 

3656 ) 

3657 except _OptGetColumnsNotAvailable: 

3658 return None 

3659 else: 

3660 allconds[0] = _traversed 

3661 

3662 cond = sql.and_(*allconds) 

3663 

3664 cols = [] 

3665 for key in col_attribute_names: 

3666 cols.extend(props[key].columns) 

3667 return ( 

3668 sql.select(*cols) 

3669 .where(cond) 

3670 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 

3671 ) 

3672 

3673 def _iterate_to_target_viawpoly(self, mapper): 

3674 if self.isa(mapper): 

3675 prev = self 

3676 for m in self.iterate_to_root(): 

3677 yield m 

3678 

3679 if m is not prev and prev not in m._with_polymorphic_mappers: 

3680 break 

3681 

3682 prev = m 

3683 if m is mapper: 

3684 break 

3685 

3686 @HasMemoized.memoized_attribute 

3687 def _would_selectinload_combinations_cache(self): 

3688 return {} 

3689 

3690 def _would_selectin_load_only_from_given_mapper(self, super_mapper): 

3691 """return True if this mapper would "selectin" polymorphic load based 

3692 on the given super mapper, and not from a setting from a subclass. 

3693 

3694 given:: 

3695 

3696 class A: ... 

3697 

3698 

3699 class B(A): 

3700 __mapper_args__ = {"polymorphic_load": "selectin"} 

3701 

3702 

3703 class C(B): ... 

3704 

3705 

3706 class D(B): 

3707 __mapper_args__ = {"polymorphic_load": "selectin"} 

3708 

3709 ``inspect(C)._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3710 returns True, because C does selectin loading because of B's setting. 

3711 

3712 OTOH, ``inspect(D) 

3713 ._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3714 returns False, because D does selectin loading because of its own 

3715 setting; when we are doing a selectin poly load from B, we want to 

3716 filter out D because it would already have its own selectin poly load 

3717 set up separately. 

3718 

3719 Added as part of #9373. 

3720 

3721 """ 

3722 cache = self._would_selectinload_combinations_cache 

3723 

3724 try: 

3725 return cache[super_mapper] 

3726 except KeyError: 

3727 pass 

3728 

3729 # assert that given object is a supermapper, meaning we already 

3730 # strong reference it directly or indirectly. this allows us 

3731 # to not worry that we are creating new strongrefs to unrelated 

3732 # mappers or other objects. 

3733 assert self.isa(super_mapper) 

3734 

3735 mapper = super_mapper 

3736 for m in self._iterate_to_target_viawpoly(mapper): 

3737 if m.polymorphic_load == "selectin": 

3738 retval = m is super_mapper 

3739 break 

3740 else: 

3741 retval = False 

3742 

3743 cache[super_mapper] = retval 

3744 return retval 

3745 

3746 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 

3747 if not enabled_via_opt: 

3748 # common case, takes place for all polymorphic loads 

3749 mapper = polymorphic_from 

3750 for m in self._iterate_to_target_viawpoly(mapper): 

3751 if m.polymorphic_load == "selectin": 

3752 return m 

3753 else: 

3754 # uncommon case, selectin load options were used 

3755 enabled_via_opt = set(enabled_via_opt) 

3756 enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 

3757 for entity in enabled_via_opt.union([polymorphic_from]): 

3758 mapper = entity.mapper 

3759 for m in self._iterate_to_target_viawpoly(mapper): 

3760 if ( 

3761 m.polymorphic_load == "selectin" 

3762 or m in enabled_via_opt_mappers 

3763 ): 

3764 return enabled_via_opt_mappers.get(m, m) 

3765 

3766 return None 

3767 

3768 @util.preload_module("sqlalchemy.orm.strategy_options") 

3769 def _subclass_load_via_in(self, entity, polymorphic_from): 

3770 """Assemble a that can load the columns local to 

3771 this subclass as a SELECT with IN. 

3772 

3773 """ 

3774 

3775 strategy_options = util.preloaded.orm_strategy_options 

3776 

3777 assert self.inherits 

3778 

3779 if self.polymorphic_on is not None: 

3780 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 

3781 keep_props = set([polymorphic_prop] + self._identity_key_props) 

3782 else: 

3783 keep_props = set(self._identity_key_props) 

3784 

3785 disable_opt = strategy_options.Load(entity) 

3786 enable_opt = strategy_options.Load(entity) 

3787 

3788 classes_to_include = {self} 

3789 m: Optional[Mapper[Any]] = self.inherits 

3790 while ( 

3791 m is not None 

3792 and m is not polymorphic_from 

3793 and m.polymorphic_load == "selectin" 

3794 ): 

3795 classes_to_include.add(m) 

3796 m = m.inherits 

3797 

3798 for prop in self.column_attrs + self.relationships: 

3799 # skip prop keys that are not instrumented on the mapped class. 

3800 # this is primarily the "_sa_polymorphic_on" property that gets 

3801 # created for an ad-hoc polymorphic_on SQL expression, issue #8704 

3802 if prop.key not in self.class_manager: 

3803 continue 

3804 

3805 if prop.parent in classes_to_include or prop in keep_props: 

3806 # "enable" options, to turn on the properties that we want to 

3807 # load by default (subject to options from the query) 

3808 if not isinstance(prop, StrategizedProperty): 

3809 continue 

3810 

3811 enable_opt = enable_opt._set_generic_strategy( 

3812 # convert string name to an attribute before passing 

3813 # to loader strategy. note this must be in terms 

3814 # of given entity, such as AliasedClass, etc. 

3815 (getattr(entity.entity_namespace, prop.key),), 

3816 dict(prop.strategy_key), 

3817 _reconcile_to_other=True, 

3818 ) 

3819 else: 

3820 # "disable" options, to turn off the properties from the 

3821 # superclass that we *don't* want to load, applied after 

3822 # the options from the query to override them 

3823 disable_opt = disable_opt._set_generic_strategy( 

3824 # convert string name to an attribute before passing 

3825 # to loader strategy. note this must be in terms 

3826 # of given entity, such as AliasedClass, etc. 

3827 (getattr(entity.entity_namespace, prop.key),), 

3828 {"do_nothing": True}, 

3829 _reconcile_to_other=False, 

3830 ) 

3831 

3832 primary_key = list(self.primary_key) 

3833 

3834 in_expr: ColumnElement[Any] 

3835 

3836 if len(primary_key) > 1: 

3837 in_expr = sql.tuple_(*primary_key) 

3838 else: 

3839 in_expr = primary_key[0] 

3840 

3841 if entity.is_aliased_class: 

3842 assert entity.mapper is self 

3843 

3844 q = sql.select(entity).set_label_style( 

3845 LABEL_STYLE_TABLENAME_PLUS_COL 

3846 ) 

3847 

3848 in_expr = entity._adapter.traverse(in_expr) 

3849 q = q.where( 

3850 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3851 ) 

3852 else: 

3853 q = sql.select(self).set_label_style( 

3854 LABEL_STYLE_TABLENAME_PLUS_COL 

3855 ) 

3856 q = q.where( 

3857 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3858 ) 

3859 

3860 return q, enable_opt, disable_opt 

3861 

3862 @HasMemoized.memoized_attribute 

3863 def _subclass_load_via_in_mapper(self): 

3864 # the default is loading this mapper against the basemost mapper 

3865 return self._subclass_load_via_in(self, self.base_mapper) 

3866 

3867 def cascade_iterator( 

3868 self, 

3869 type_: str, 

3870 state: InstanceState[_O], 

3871 halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None, 

3872 ) -> Iterator[ 

3873 Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict] 

3874 ]: 

3875 r"""Iterate each element and its mapper in an object graph, 

3876 for all relationships that meet the given cascade rule. 

3877 

3878 :param type\_: 

3879 The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 

3880 etc.). 

3881 

3882 .. note:: the ``"all"`` cascade is not accepted here. For a generic 

3883 object traversal function, see :ref:`faq_walk_objects`. 

3884 

3885 :param state: 

3886 The lead InstanceState. child items will be processed per 

3887 the relationships defined for this object's mapper. 

3888 

3889 :return: the method yields individual object instances. 

3890 

3891 .. seealso:: 

3892 

3893 :ref:`unitofwork_cascades` 

3894 

3895 :ref:`faq_walk_objects` - illustrates a generic function to 

3896 traverse all objects without relying on cascades. 

3897 

3898 """ 

3899 visited_states: Set[InstanceState[Any]] = set() 

3900 prp, mpp = object(), object() 

3901 

3902 assert state.mapper.isa(self) 

3903 

3904 # this is actually a recursive structure, fully typing it seems 

3905 # a little too difficult for what it's worth here 

3906 visitables: Deque[ 

3907 Tuple[ 

3908 Deque[Any], 

3909 object, 

3910 Optional[InstanceState[Any]], 

3911 Optional[_InstanceDict], 

3912 ] 

3913 ] 

3914 

3915 visitables = deque( 

3916 [(deque(state.mapper._props.values()), prp, state, state.dict)] 

3917 ) 

3918 

3919 while visitables: 

3920 iterator, item_type, parent_state, parent_dict = visitables[-1] 

3921 if not iterator: 

3922 visitables.pop() 

3923 continue 

3924 

3925 if item_type is prp: 

3926 prop = iterator.popleft() 

3927 if not prop.cascade or type_ not in prop.cascade: 

3928 continue 

3929 assert parent_state is not None 

3930 assert parent_dict is not None 

3931 queue = deque( 

3932 prop.cascade_iterator( 

3933 type_, 

3934 parent_state, 

3935 parent_dict, 

3936 visited_states, 

3937 halt_on, 

3938 ) 

3939 ) 

3940 if queue: 

3941 visitables.append((queue, mpp, None, None)) 

3942 elif item_type is mpp: 

3943 ( 

3944 instance, 

3945 instance_mapper, 

3946 corresponding_state, 

3947 corresponding_dict, 

3948 ) = iterator.popleft() 

3949 yield ( 

3950 instance, 

3951 instance_mapper, 

3952 corresponding_state, 

3953 corresponding_dict, 

3954 ) 

3955 visitables.append( 

3956 ( 

3957 deque(instance_mapper._props.values()), 

3958 prp, 

3959 corresponding_state, 

3960 corresponding_dict, 

3961 ) 

3962 ) 

3963 

3964 @HasMemoized.memoized_attribute 

3965 def _compiled_cache(self): 

3966 return util.LRUCache(self._compiled_cache_size) 

3967 

3968 @HasMemoized.memoized_attribute 

3969 def _multiple_persistence_tables(self): 

3970 return len(self.tables) > 1 

3971 

3972 @HasMemoized.memoized_attribute 

3973 def _sorted_tables(self): 

3974 table_to_mapper: Dict[TableClause, Mapper[Any]] = {} 

3975 

3976 for mapper in self.base_mapper.self_and_descendants: 

3977 for t in mapper.tables: 

3978 table_to_mapper.setdefault(t, mapper) 

3979 

3980 extra_dependencies = [] 

3981 for table, mapper in table_to_mapper.items(): 

3982 super_ = mapper.inherits 

3983 if super_: 

3984 extra_dependencies.extend( 

3985 [(super_table, table) for super_table in super_.tables] 

3986 ) 

3987 

3988 def skip(fk): 

3989 # attempt to skip dependencies that are not 

3990 # significant to the inheritance chain 

3991 # for two tables that are related by inheritance. 

3992 # while that dependency may be important, it's technically 

3993 # not what we mean to sort on here. 

3994 parent = table_to_mapper.get(fk.parent.table) 

3995 dep = table_to_mapper.get(fk.column.table) 

3996 if ( 

3997 parent is not None 

3998 and dep is not None 

3999 and dep is not parent 

4000 and dep.inherit_condition is not None 

4001 ): 

4002 cols = set(sql_util._find_columns(dep.inherit_condition)) 

4003 if parent.inherit_condition is not None: 

4004 cols = cols.union( 

4005 sql_util._find_columns(parent.inherit_condition) 

4006 ) 

4007 return fk.parent not in cols and fk.column not in cols 

4008 else: 

4009 return fk.parent not in cols 

4010 return False 

4011 

4012 sorted_ = sql_util.sort_tables( 

4013 table_to_mapper, 

4014 skip_fn=skip, 

4015 extra_dependencies=extra_dependencies, 

4016 ) 

4017 

4018 ret = util.OrderedDict() 

4019 for t in sorted_: 

4020 ret[t] = table_to_mapper[t] 

4021 return ret 

4022 

4023 def _memo(self, key: Any, callable_: Callable[[], _T]) -> _T: 

4024 if key in self._memoized_values: 

4025 return cast(_T, self._memoized_values[key]) 

4026 else: 

4027 self._memoized_values[key] = value = callable_() 

4028 return value 

4029 

4030 @util.memoized_property 

4031 def _table_to_equated(self): 

4032 """memoized map of tables to collections of columns to be 

4033 synchronized upwards to the base mapper.""" 

4034 

4035 result: util.defaultdict[ 

4036 Table, 

4037 List[ 

4038 Tuple[ 

4039 Mapper[Any], 

4040 List[Tuple[ColumnElement[Any], ColumnElement[Any]]], 

4041 ] 

4042 ], 

4043 ] = util.defaultdict(list) 

4044 

4045 def set_union(x, y): 

4046 return x.union(y) 

4047 

4048 for table in self._sorted_tables: 

4049 cols = set(table.c) 

4050 

4051 for m in self.iterate_to_root(): 

4052 if m._inherits_equated_pairs and cols.intersection( 

4053 reduce( 

4054 set_union, 

4055 [l.proxy_set for l, r in m._inherits_equated_pairs], 

4056 ) 

4057 ): 

4058 result[table].append((m, m._inherits_equated_pairs)) 

4059 

4060 return result 

4061 

4062 

4063class _OptGetColumnsNotAvailable(Exception): 

4064 pass 

4065 

4066 

4067def configure_mappers() -> None: 

4068 """Initialize the inter-mapper relationships of all mappers that 

4069 have been constructed thus far across all :class:`_orm.registry` 

4070 collections. 

4071 

4072 The configure step is used to reconcile and initialize the 

4073 :func:`_orm.relationship` linkages between mapped classes, as well as to 

4074 invoke configuration events such as the 

4075 :meth:`_orm.MapperEvents.before_configured` and 

4076 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM 

4077 extensions or user-defined extension hooks. 

4078 

4079 Mapper configuration is normally invoked automatically, the first time 

4080 mappings from a particular :class:`_orm.registry` are used, as well as 

4081 whenever mappings are used and additional not-yet-configured mappers have 

4082 been constructed. The automatic configuration process however is local only 

4083 to the :class:`_orm.registry` involving the target mapper and any related 

4084 :class:`_orm.registry` objects which it may depend on; this is 

4085 equivalent to invoking the :meth:`_orm.registry.configure` method 

4086 on a particular :class:`_orm.registry`. 

4087 

4088 By contrast, the :func:`_orm.configure_mappers` function will invoke the 

4089 configuration process on all :class:`_orm.registry` objects that 

4090 exist in memory, and may be useful for scenarios where many individual 

4091 :class:`_orm.registry` objects that are nonetheless interrelated are 

4092 in use. 

4093 

4094 .. versionchanged:: 1.4 

4095 

4096 As of SQLAlchemy 1.4.0b2, this function works on a 

4097 per-:class:`_orm.registry` basis, locating all :class:`_orm.registry` 

4098 objects present and invoking the :meth:`_orm.registry.configure` method 

4099 on each. The :meth:`_orm.registry.configure` method may be preferred to 

4100 limit the configuration of mappers to those local to a particular 

4101 :class:`_orm.registry` and/or declarative base class. 

4102 

4103 Points at which automatic configuration is invoked include when a mapped 

4104 class is instantiated into an instance, as well as when ORM queries 

4105 are emitted using :meth:`.Session.query` or :meth:`_orm.Session.execute` 

4106 with an ORM-enabled statement. 

4107 

4108 The mapper configure process, whether invoked by 

4109 :func:`_orm.configure_mappers` or from :meth:`_orm.registry.configure`, 

4110 provides several event hooks that can be used to augment the mapper 

4111 configuration step. These hooks include: 

4112 

4113 * :meth:`.MapperEvents.before_configured` - called once before 

4114 :func:`.configure_mappers` or :meth:`_orm.registry.configure` does any 

4115 work; this can be used to establish additional options, properties, or 

4116 related mappings before the operation proceeds. 

4117 

4118 * :meth:`.RegistryEvents.before_configured` - Like 

4119 :meth:`.MapperEvents.before_configured`, but local to a specific 

4120 :class:`_orm.registry`. 

4121 

4122 .. versionadded:: 2.1 - added :meth:`.RegistryEvents.before_configured` 

4123 

4124 * :meth:`.MapperEvents.mapper_configured` - called as each individual 

4125 :class:`_orm.Mapper` is configured within the process; will include all 

4126 mapper state except for backrefs set up by other mappers that are still 

4127 to be configured. 

4128 

4129 * :meth:`.MapperEvents.after_configured` - called once after 

4130 :func:`.configure_mappers` or :meth:`_orm.registry.configure` is 

4131 complete; at this stage, all :class:`_orm.Mapper` objects that fall 

4132 within the scope of the configuration operation will be fully configured. 

4133 Note that the calling application may still have other mappings that 

4134 haven't been produced yet, such as if they are in modules as yet 

4135 unimported, and may also have mappings that are still to be configured, 

4136 if they are in other :class:`_orm.registry` collections not part of the 

4137 current scope of configuration. 

4138 

4139 * :meth:`.RegistryEvents.after_configured` - Like 

4140 :meth:`.MapperEvents.after_configured`, but local to a specific 

4141 :class:`_orm.registry`. 

4142 

4143 .. versionadded:: 2.1 - added :meth:`.RegistryEvents.after_configured` 

4144 

4145 """ 

4146 

4147 _configure_registries(_all_registries(), cascade=True) 

4148 

4149 

4150def _configure_registries( 

4151 registries: Set[_RegistryType], cascade: bool 

4152) -> None: 

4153 for reg in registries: 

4154 if reg._new_mappers: 

4155 break 

4156 else: 

4157 return 

4158 

4159 with _CONFIGURE_MUTEX: 

4160 global _already_compiling 

4161 if _already_compiling: 

4162 return 

4163 _already_compiling = True 

4164 try: 

4165 # double-check inside mutex 

4166 for reg in registries: 

4167 if reg._new_mappers: 

4168 break 

4169 else: 

4170 return 

4171 

4172 Mapper.dispatch._for_class(Mapper).before_configured() # type: ignore # noqa: E501 

4173 

4174 # initialize properties on all mappers 

4175 # note that _mapper_registry is unordered, which 

4176 # may randomly conceal/reveal issues related to 

4177 # the order of mapper compilation 

4178 

4179 registries_configured = list( 

4180 _do_configure_registries(registries, cascade) 

4181 ) 

4182 

4183 finally: 

4184 _already_compiling = False 

4185 for reg in registries_configured: 

4186 reg.dispatch.after_configured(reg) 

4187 Mapper.dispatch._for_class(Mapper).after_configured() # type: ignore 

4188 

4189 

4190@util.preload_module("sqlalchemy.orm.decl_api") 

4191def _do_configure_registries( 

4192 registries: Set[_RegistryType], cascade: bool 

4193) -> Iterator[registry]: 

4194 registry = util.preloaded.orm_decl_api.registry 

4195 

4196 orig = set(registries) 

4197 

4198 for reg in registry._recurse_with_dependencies(registries): 

4199 if reg._new_mappers: 

4200 reg.dispatch.before_configured(reg) 

4201 

4202 has_skip = False 

4203 

4204 for mapper in reg._mappers_to_configure(): 

4205 run_configure = None 

4206 

4207 for fn in mapper.dispatch.before_mapper_configured: 

4208 run_configure = fn(mapper, mapper.class_) 

4209 if run_configure is EXT_SKIP: 

4210 has_skip = True 

4211 break 

4212 if run_configure is EXT_SKIP: 

4213 continue 

4214 

4215 if getattr(mapper, "_configure_failed", False): 

4216 e = sa_exc.InvalidRequestError( 

4217 "One or more mappers failed to initialize - " 

4218 "can't proceed with initialization of other " 

4219 "mappers. Triggering mapper: '%s'. " 

4220 "Original exception was: %s" 

4221 % (mapper, mapper._configure_failed) 

4222 ) 

4223 e._configure_failed = mapper._configure_failed # type: ignore 

4224 raise e 

4225 

4226 if not mapper.configured: 

4227 try: 

4228 mapper._post_configure_properties() 

4229 mapper._expire_memoizations() 

4230 mapper.dispatch.mapper_configured(mapper, mapper.class_) 

4231 except Exception: 

4232 exc = sys.exc_info()[1] 

4233 if not hasattr(exc, "_configure_failed"): 

4234 mapper._configure_failed = exc 

4235 raise 

4236 

4237 if reg._new_mappers: 

4238 yield reg 

4239 if not has_skip: 

4240 reg._new_mappers = False 

4241 

4242 if not cascade and reg._dependencies.difference(orig): 

4243 raise sa_exc.InvalidRequestError( 

4244 "configure was called with cascade=False but " 

4245 "additional registries remain" 

4246 ) 

4247 

4248 

4249@util.preload_module("sqlalchemy.orm.decl_api") 

4250def _dispose_registries(registries: Set[_RegistryType], cascade: bool) -> None: 

4251 registry = util.preloaded.orm_decl_api.registry 

4252 

4253 orig = set(registries) 

4254 

4255 for reg in registry._recurse_with_dependents(registries): 

4256 if not cascade and reg._dependents.difference(orig): 

4257 raise sa_exc.InvalidRequestError( 

4258 "Registry has dependent registries that are not disposed; " 

4259 "pass cascade=True to clear these also" 

4260 ) 

4261 

4262 while reg._managers: 

4263 try: 

4264 manager, _ = reg._managers.popitem() 

4265 except KeyError: 

4266 # guard against race between while and popitem 

4267 pass 

4268 else: 

4269 reg._dispose_manager_and_mapper(manager) 

4270 

4271 reg._dependents.clear() 

4272 for dep in reg._dependencies: 

4273 dep._dependents.discard(reg) 

4274 reg._dependencies.clear() 

4275 # this wasn't done in the 1.3 clear_mappers() and in fact it 

4276 # was a bug, as it could cause configure_mappers() to invoke 

4277 # the "before_configured" event even though mappers had all been 

4278 # disposed. 

4279 reg._new_mappers = False 

4280 

4281 

4282def reconstructor(fn: _Fn) -> _Fn: 

4283 """Decorate a method as the 'reconstructor' hook. 

4284 

4285 Designates a single method as the "reconstructor", an ``__init__``-like 

4286 method that will be called by the ORM after the instance has been 

4287 loaded from the database or otherwise reconstituted. 

4288 

4289 .. tip:: 

4290 

4291 The :func:`_orm.reconstructor` decorator makes use of the 

4292 :meth:`_orm.InstanceEvents.load` event hook, which can be 

4293 used directly. 

4294 

4295 The reconstructor will be invoked with no arguments. Scalar 

4296 (non-collection) database-mapped attributes of the instance will 

4297 be available for use within the function. Eagerly-loaded 

4298 collections are generally not yet available and will usually only 

4299 contain the first element. ORM state changes made to objects at 

4300 this stage will not be recorded for the next flush() operation, so 

4301 the activity within a reconstructor should be conservative. 

4302 

4303 .. seealso:: 

4304 

4305 :meth:`.InstanceEvents.load` 

4306 

4307 """ 

4308 fn.__sa_reconstructor__ = True # type: ignore[attr-defined] 

4309 return fn 

4310 

4311 

4312def validates( 

4313 *names: str, include_removes: bool = False, include_backrefs: bool = True 

4314) -> Callable[[_Fn], _Fn]: 

4315 r"""Decorate a method as a 'validator' for one or more named properties. 

4316 

4317 Designates a method as a validator, a method which receives the 

4318 name of the attribute as well as a value to be assigned, or in the 

4319 case of a collection, the value to be added to the collection. 

4320 The function can then raise validation exceptions to halt the 

4321 process from continuing (where Python's built-in ``ValueError`` 

4322 and ``AssertionError`` exceptions are reasonable choices), or can 

4323 modify or replace the value before proceeding. The function should 

4324 otherwise return the given value. 

4325 

4326 Note that a validator for a collection **cannot** issue a load of that 

4327 collection within the validation routine - this usage raises 

4328 an assertion to avoid recursion overflows. This is a reentrant 

4329 condition which is not supported. 

4330 

4331 :param \*names: list of attribute names to be validated. 

4332 :param include_removes: if True, "remove" events will be 

4333 sent as well - the validation function must accept an additional 

4334 argument "is_remove" which will be a boolean. 

4335 

4336 :param include_backrefs: defaults to ``True``; if ``False``, the 

4337 validation function will not emit if the originator is an attribute 

4338 event related via a backref. This can be used for bi-directional 

4339 :func:`.validates` usage where only one validator should emit per 

4340 attribute operation. 

4341 

4342 .. versionchanged:: 2.0.16 This parameter inadvertently defaulted to 

4343 ``False`` for releases 2.0.0 through 2.0.15. Its correct default 

4344 of ``True`` is restored in 2.0.16. 

4345 

4346 .. seealso:: 

4347 

4348 :ref:`simple_validators` - usage examples for :func:`.validates` 

4349 

4350 """ 

4351 

4352 def wrap(fn: _Fn) -> _Fn: 

4353 fn.__sa_validators__ = names # type: ignore[attr-defined] 

4354 fn.__sa_validation_opts__ = { # type: ignore[attr-defined] 

4355 "include_removes": include_removes, 

4356 "include_backrefs": include_backrefs, 

4357 } 

4358 return fn 

4359 

4360 return wrap 

4361 

4362 

4363def _event_on_load(state, ctx): 

4364 instrumenting_mapper = state.manager.mapper 

4365 

4366 if instrumenting_mapper._reconstructor: 

4367 instrumenting_mapper._reconstructor(state.obj()) 

4368 

4369 

4370def _event_on_init(state, args, kwargs): 

4371 """Run init_instance hooks. 

4372 

4373 This also includes mapper compilation, normally not needed 

4374 here but helps with some piecemeal configuration 

4375 scenarios (such as in the ORM tutorial). 

4376 

4377 """ 

4378 

4379 instrumenting_mapper = state.manager.mapper 

4380 if instrumenting_mapper: 

4381 instrumenting_mapper._check_configure() 

4382 if instrumenting_mapper._set_polymorphic_identity: 

4383 instrumenting_mapper._set_polymorphic_identity(state) 

4384 

4385 

4386class _ColumnMapping(Dict["ColumnElement[Any]", "MapperProperty[Any]"]): 

4387 """Error reporting helper for mapper._columntoproperty.""" 

4388 

4389 __slots__ = ("mapper",) 

4390 

4391 def __init__(self, mapper): 

4392 # TODO: weakref would be a good idea here 

4393 self.mapper = mapper 

4394 

4395 def __missing__(self, column): 

4396 prop = self.mapper._props.get(column) 

4397 if prop: 

4398 raise orm_exc.UnmappedColumnError( 

4399 "Column '%s.%s' is not available, due to " 

4400 "conflicting property '%s':%r" 

4401 % (column.table.name, column.name, column.key, prop) 

4402 ) 

4403 raise orm_exc.UnmappedColumnError( 

4404 "No column %s is configured on mapper %s..." 

4405 % (column, self.mapper) 

4406 )