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

1412 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 Mapping 

37from typing import Optional 

38from typing import Sequence 

39from typing import Set 

40from typing import Tuple 

41from typing import Type 

42from typing import TYPE_CHECKING 

43from typing import TypeVar 

44from typing import Union 

45import weakref 

46 

47from . import attributes 

48from . import exc as orm_exc 

49from . import instrumentation 

50from . import loading 

51from . import properties 

52from . import util as orm_util 

53from ._typing import _O 

54from .base import _class_to_mapper 

55from .base import _parse_mapper_argument 

56from .base import _state_mapper 

57from .base import PassiveFlag 

58from .base import state_str 

59from .interfaces import _MappedAttribute 

60from .interfaces import EXT_SKIP 

61from .interfaces import InspectionAttr 

62from .interfaces import MapperProperty 

63from .interfaces import ORMEntityColumnsClauseRole 

64from .interfaces import ORMFromClauseRole 

65from .interfaces import StrategizedProperty 

66from .path_registry import PathRegistry 

67from .. import event 

68from .. import exc as sa_exc 

69from .. import inspection 

70from .. import log 

71from .. import schema 

72from .. import sql 

73from .. import util 

74from ..event import dispatcher 

75from ..event import EventTarget 

76from ..sql import base as sql_base 

77from ..sql import coercions 

78from ..sql import expression 

79from ..sql import operators 

80from ..sql import roles 

81from ..sql import TableClause 

82from ..sql import util as sql_util 

83from ..sql import visitors 

84from ..sql.cache_key import MemoizedHasCacheKey 

85from ..sql.elements import KeyedColumnElement 

86from ..sql.schema import Column 

87from ..sql.schema import Table 

88from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

89from ..util import HasMemoized 

90from ..util import HasMemoized_ro_memoized_attribute 

91from ..util.typing import Literal 

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 if not isinstance(prop_arg, MapperProperty): 

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

2042 key, prop_arg 

2043 ) 

2044 else: 

2045 prop = prop_arg 

2046 

2047 if isinstance(prop, properties.ColumnProperty): 

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

2049 

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

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

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

2053 if col is None and self.inherits: 

2054 path = [self] 

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

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

2057 if col is not None: 

2058 for m2 in path: 

2059 m2.persist_selectable._refresh_for_new_column(col) 

2060 col = self.persist_selectable.corresponding_column( 

2061 prop.columns[0] 

2062 ) 

2063 break 

2064 path.append(m) 

2065 

2066 # subquery expression, column not present in the mapped 

2067 # selectable. 

2068 if col is None: 

2069 col = prop.columns[0] 

2070 

2071 # column is coming in after _readonly_props was 

2072 # initialized; check for 'readonly' 

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

2074 not hasattr(col, "table") 

2075 or col.table not in self._cols_by_table 

2076 ): 

2077 self._readonly_props.add(prop) 

2078 

2079 else: 

2080 # if column is coming in after _cols_by_table was 

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

2082 if ( 

2083 hasattr(self, "_cols_by_table") 

2084 and col.table in self._cols_by_table 

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

2086 ): 

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

2088 

2089 # if this properties.ColumnProperty represents the "polymorphic 

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

2091 # columns in SELECT statements. 

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

2093 prop._is_polymorphic_discriminator = ( 

2094 col is self.polymorphic_on 

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

2096 ) 

2097 

2098 if isinstance(col, expression.Label): 

2099 # new in 1.4, get column property against expressions 

2100 # to be addressable in subqueries 

2101 col.key = col._tq_key_label = key 

2102 

2103 self.columns.add(col, key) 

2104 

2105 for col in prop.columns: 

2106 for proxy_col in col.proxy_set: 

2107 self._columntoproperty[proxy_col] = prop 

2108 

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

2110 util.warn( 

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

2112 "assigned to attribute " 

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

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

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

2116 ) 

2117 

2118 prop.key = key 

2119 

2120 if setparent: 

2121 prop.set_parent(self, init) 

2122 

2123 if key in self._props and getattr( 

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

2125 ): 

2126 syn = self._props[key]._mapped_by_synonym 

2127 raise sa_exc.ArgumentError( 

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

2129 "a ColumnProperty already exists keyed to the name " 

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

2131 ) 

2132 

2133 # replacement cases 

2134 

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

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

2137 if ( 

2138 key in self._props 

2139 and not isinstance( 

2140 self._props[key], descriptor_props.ConcreteInheritedProperty 

2141 ) 

2142 and not isinstance(prop, descriptor_props.SynonymProperty) 

2143 ): 

2144 if warn_for_existing: 

2145 util.warn_deprecated( 

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

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

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

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

2150 "release", 

2151 "2.0", 

2152 ) 

2153 oldprop = self._props[key] 

2154 self._path_registry.pop(oldprop, None) 

2155 

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

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

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

2159 # get replaced. 

2160 elif ( 

2161 warn_for_existing 

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

2163 and not isinstance(prop, descriptor_props.SynonymProperty) 

2164 and not isinstance( 

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

2166 descriptor_props.ConcreteInheritedProperty, 

2167 ) 

2168 ): 

2169 util.warn_deprecated( 

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

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

2172 "attribute of the same name. " 

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

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

2175 "release", 

2176 "2.0", 

2177 ) 

2178 

2179 self._props[key] = prop 

2180 

2181 prop.instrument_class(self) 

2182 

2183 for mapper in self._inheriting_mappers: 

2184 mapper._adapt_inherited_property(key, prop, init) 

2185 

2186 if init: 

2187 prop.init() 

2188 prop.post_instrument_class(self) 

2189 

2190 if self.configured: 

2191 self._expire_memoizations() 

2192 

2193 return prop 

2194 

2195 def _make_prop_from_column( 

2196 self, 

2197 key: str, 

2198 column: Union[ 

2199 Sequence[KeyedColumnElement[Any]], KeyedColumnElement[Any] 

2200 ], 

2201 ) -> ColumnProperty[Any]: 

2202 columns = util.to_list(column) 

2203 mapped_column = [] 

2204 for c in columns: 

2205 mc = self.persist_selectable.corresponding_column(c) 

2206 if mc is None: 

2207 mc = self.local_table.corresponding_column(c) 

2208 if mc is not None: 

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

2210 # mapped table, this corresponds to adding a 

2211 # column after the fact to the local table. 

2212 # [ticket:1523] 

2213 self.persist_selectable._refresh_for_new_column(mc) 

2214 mc = self.persist_selectable.corresponding_column(c) 

2215 if mc is None: 

2216 raise sa_exc.ArgumentError( 

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

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

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

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

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

2222 ) 

2223 mapped_column.append(mc) 

2224 return properties.ColumnProperty(*mapped_column) 

2225 

2226 def _reconcile_prop_with_incoming_columns( 

2227 self, 

2228 key: str, 

2229 existing_prop: MapperProperty[Any], 

2230 warn_only: bool, 

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

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

2233 ) -> ColumnProperty[Any]: 

2234 if incoming_prop and ( 

2235 self.concrete 

2236 or not isinstance(existing_prop, properties.ColumnProperty) 

2237 ): 

2238 return incoming_prop 

2239 

2240 existing_column = existing_prop.columns[0] 

2241 

2242 if incoming_prop and existing_column in incoming_prop.columns: 

2243 return incoming_prop 

2244 

2245 if incoming_prop is None: 

2246 assert single_column is not None 

2247 incoming_column = single_column 

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

2249 else: 

2250 assert single_column is None 

2251 incoming_column = incoming_prop.columns[0] 

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

2253 

2254 if ( 

2255 ( 

2256 not self._inherits_equated_pairs 

2257 or (equated_pair_key not in self._inherits_equated_pairs) 

2258 ) 

2259 and not existing_column.shares_lineage(incoming_column) 

2260 and existing_column is not self.version_id_col 

2261 and incoming_column is not self.version_id_col 

2262 ): 

2263 msg = ( 

2264 "Implicitly combining column %s with column " 

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

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

2267 "explicitly." 

2268 % ( 

2269 existing_prop.columns[-1], 

2270 incoming_column, 

2271 key, 

2272 ) 

2273 ) 

2274 if warn_only: 

2275 util.warn(msg) 

2276 else: 

2277 raise sa_exc.InvalidRequestError(msg) 

2278 

2279 # existing properties.ColumnProperty from an inheriting 

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

2281 # breakpoint() 

2282 new_prop = existing_prop.copy() 

2283 

2284 new_prop.columns.insert(0, incoming_column) 

2285 self._log( 

2286 "inserting column to existing list " 

2287 "in properties.ColumnProperty %s", 

2288 key, 

2289 ) 

2290 return new_prop # type: ignore 

2291 

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

2293 def _property_from_column( 

2294 self, 

2295 key: str, 

2296 column: KeyedColumnElement[Any], 

2297 ) -> ColumnProperty[Any]: 

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

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

2300 

2301 descriptor_props = util.preloaded.orm_descriptor_props 

2302 

2303 prop = self._props.get(key) 

2304 

2305 if isinstance(prop, properties.ColumnProperty): 

2306 return self._reconcile_prop_with_incoming_columns( 

2307 key, 

2308 prop, 

2309 single_column=column, 

2310 warn_only=prop.parent is not self, 

2311 ) 

2312 elif prop is None or isinstance( 

2313 prop, descriptor_props.ConcreteInheritedProperty 

2314 ): 

2315 return self._make_prop_from_column(key, column) 

2316 else: 

2317 raise sa_exc.ArgumentError( 

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

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

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

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

2322 "to remove all awareness of the column entirely " 

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

2324 "use the 'include_properties' or 'exclude_properties' " 

2325 "mapper arguments to control specifically which table " 

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

2327 ) 

2328 

2329 @util.langhelpers.tag_method_for_warnings( 

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

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

2332 "operation.", 

2333 sa_exc.SAWarning, 

2334 ) 

2335 def _check_configure(self) -> None: 

2336 if self.registry._new_mappers: 

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

2338 

2339 def _post_configure_properties(self) -> None: 

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

2341 attached to this mapper. 

2342 

2343 This is a deferred configuration step which is intended 

2344 to execute once all mappers have been constructed. 

2345 

2346 """ 

2347 

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

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

2350 for key, prop in l: 

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

2352 

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

2354 prop.init() 

2355 

2356 if prop._configure_finished: 

2357 prop.post_instrument_class(self) 

2358 

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

2360 self.configured = True 

2361 

2362 def add_properties(self, dict_of_properties): 

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

2364 using `add_property`. 

2365 

2366 """ 

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

2368 self.add_property(key, value) 

2369 

2370 def add_property( 

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

2372 ) -> None: 

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

2374 

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

2376 property to the initial properties dictionary sent to the 

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

2378 the given MapperProperty is configured immediately. 

2379 

2380 """ 

2381 prop = self._configure_property( 

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

2383 ) 

2384 assert isinstance(prop, MapperProperty) 

2385 self._init_properties[key] = prop 

2386 

2387 def _expire_memoizations(self) -> None: 

2388 for mapper in self.iterate_to_root(): 

2389 mapper._reset_memoizations() 

2390 

2391 @property 

2392 def _log_desc(self) -> str: 

2393 return ( 

2394 "(" 

2395 + self.class_.__name__ 

2396 + "|" 

2397 + ( 

2398 self.local_table is not None 

2399 and self.local_table.description 

2400 or str(self.local_table) 

2401 ) 

2402 + ")" 

2403 ) 

2404 

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

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

2407 

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

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

2410 

2411 def __repr__(self) -> str: 

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

2413 

2414 def __str__(self) -> str: 

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

2416 self.class_.__name__, 

2417 ( 

2418 self.local_table.description 

2419 if self.local_table is not None 

2420 else self.persist_selectable.description 

2421 ), 

2422 ) 

2423 

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

2425 orphan_possible = False 

2426 for mapper in self.iterate_to_root(): 

2427 for key, cls in mapper._delete_orphans: 

2428 orphan_possible = True 

2429 

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

2431 state, key, optimistic=state.has_identity 

2432 ) 

2433 

2434 if self.legacy_is_orphan and has_parent: 

2435 return False 

2436 elif not self.legacy_is_orphan and not has_parent: 

2437 return True 

2438 

2439 if self.legacy_is_orphan: 

2440 return orphan_possible 

2441 else: 

2442 return False 

2443 

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

2445 return key in self._props 

2446 

2447 def get_property( 

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

2449 ) -> MapperProperty[Any]: 

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

2451 

2452 if _configure_mappers: 

2453 self._check_configure() 

2454 

2455 try: 

2456 return self._props[key] 

2457 except KeyError as err: 

2458 raise sa_exc.InvalidRequestError( 

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

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

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

2462 ) from err 

2463 

2464 def get_property_by_column( 

2465 self, column: ColumnElement[_T] 

2466 ) -> MapperProperty[_T]: 

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

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

2469 

2470 return self._columntoproperty[column] 

2471 

2472 @property 

2473 def iterate_properties(self): 

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

2475 

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

2477 

2478 def _mappers_from_spec( 

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

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

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

2482 represents. 

2483 

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

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

2486 

2487 """ 

2488 if spec == "*": 

2489 mappers = list(self.self_and_descendants) 

2490 elif spec: 

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

2492 for m in util.to_list(spec): 

2493 m = _class_to_mapper(m) 

2494 if not m.isa(self): 

2495 raise sa_exc.InvalidRequestError( 

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

2497 ) 

2498 

2499 if selectable is None: 

2500 mapper_set.update(m.iterate_to_root()) 

2501 else: 

2502 mapper_set.add(m) 

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

2504 else: 

2505 mappers = [] 

2506 

2507 if selectable is not None: 

2508 tables = set( 

2509 sql_util.find_tables(selectable, include_aliases=True) 

2510 ) 

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

2512 return mappers 

2513 

2514 def _selectable_from_mappers( 

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

2516 ) -> FromClause: 

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

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

2519 mapped tables. 

2520 

2521 """ 

2522 from_obj = self.persist_selectable 

2523 for m in mappers: 

2524 if m is self: 

2525 continue 

2526 if m.concrete: 

2527 raise sa_exc.InvalidRequestError( 

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

2529 "when concrete-inheriting mappers are used." 

2530 ) 

2531 elif not m.single: 

2532 if innerjoin: 

2533 from_obj = from_obj.join( 

2534 m.local_table, m.inherit_condition 

2535 ) 

2536 else: 

2537 from_obj = from_obj.outerjoin( 

2538 m.local_table, m.inherit_condition 

2539 ) 

2540 

2541 return from_obj 

2542 

2543 @HasMemoized.memoized_attribute 

2544 def _version_id_has_server_side_value(self) -> bool: 

2545 vid_col = self.version_id_col 

2546 

2547 if vid_col is None: 

2548 return False 

2549 

2550 elif not isinstance(vid_col, Column): 

2551 return True 

2552 else: 

2553 return vid_col.server_default is not None or ( 

2554 vid_col.default is not None 

2555 and ( 

2556 not vid_col.default.is_scalar 

2557 and not vid_col.default.is_callable 

2558 ) 

2559 ) 

2560 

2561 @HasMemoized.memoized_attribute 

2562 def _single_table_criteria_component(self): 

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

2564 

2565 hierarchy = tuple( 

2566 m.polymorphic_identity 

2567 for m in self.self_and_descendants 

2568 if not m.polymorphic_abstract 

2569 ) 

2570 

2571 return ( 

2572 self.polymorphic_on._annotate( 

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

2574 ), 

2575 hierarchy, 

2576 ) 

2577 else: 

2578 return None 

2579 

2580 @HasMemoized.memoized_attribute 

2581 def _single_table_criterion(self): 

2582 component = self._single_table_criteria_component 

2583 if component is not None: 

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

2585 else: 

2586 return None 

2587 

2588 @HasMemoized.memoized_attribute 

2589 def _has_aliased_polymorphic_fromclause(self): 

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

2591 like a subquery. 

2592 

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

2594 if this is present. 

2595 

2596 """ 

2597 return self.with_polymorphic and isinstance( 

2598 self.with_polymorphic[1], 

2599 expression.AliasedReturnsRows, 

2600 ) 

2601 

2602 @HasMemoized.memoized_attribute 

2603 def _should_select_with_poly_adapter(self): 

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

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

2606 rows for mapped classes and subclasses against this Mapper. 

2607 

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

2609 for this condition. 

2610 

2611 """ 

2612 

2613 # this has been simplified as of #8456. 

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

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

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

2617 # 

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

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

2620 # flattened JOIN for with_polymorphic.) 

2621 # 

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

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

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

2625 # some kind or polymorphic_union. 

2626 # 

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

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

2629 # on it (such as test_join_from_polymorphic_explicit_aliased_three). 

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

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

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

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

2634 # legacy case we should probably disable. 

2635 # 

2636 # 

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

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

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

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

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

2642 # polymorphic base class. 

2643 # 

2644 return ( 

2645 self._has_aliased_polymorphic_fromclause 

2646 or self._requires_row_aliasing 

2647 or (self.base_mapper._has_aliased_polymorphic_fromclause) 

2648 or self.base_mapper._requires_row_aliasing 

2649 ) 

2650 

2651 @HasMemoized.memoized_attribute 

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

2653 self._check_configure() 

2654 

2655 if not self.with_polymorphic: 

2656 return [] 

2657 return self._mappers_from_spec(*self.with_polymorphic) 

2658 

2659 @HasMemoized.memoized_attribute 

2660 def _post_inspect(self): 

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

2662 

2663 E.g. when Query calls: 

2664 

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

2666 

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

2668 

2669 """ 

2670 self._check_configure() 

2671 

2672 @HasMemoized_ro_memoized_attribute 

2673 def _with_polymorphic_selectable(self) -> FromClause: 

2674 if not self.with_polymorphic: 

2675 return self.persist_selectable 

2676 

2677 spec, selectable = self.with_polymorphic 

2678 if selectable is not None: 

2679 return selectable 

2680 else: 

2681 return self._selectable_from_mappers( 

2682 self._mappers_from_spec(spec, selectable), False 

2683 ) 

2684 

2685 with_polymorphic_mappers = _with_polymorphic_mappers 

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

2687 default "polymorphic" query. 

2688 

2689 """ 

2690 

2691 @HasMemoized_ro_memoized_attribute 

2692 def _insert_cols_evaluating_none(self): 

2693 return { 

2694 table: frozenset( 

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

2696 ) 

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

2698 } 

2699 

2700 @HasMemoized.memoized_attribute 

2701 def _insert_cols_as_none(self): 

2702 return { 

2703 table: frozenset( 

2704 col.key 

2705 for col in columns 

2706 if not col.primary_key 

2707 and not col.server_default 

2708 and not col.default 

2709 and not col.type.should_evaluate_none 

2710 ) 

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

2712 } 

2713 

2714 @HasMemoized.memoized_attribute 

2715 def _propkey_to_col(self): 

2716 return { 

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

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

2719 } 

2720 

2721 @HasMemoized.memoized_attribute 

2722 def _pk_keys_by_table(self): 

2723 return { 

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

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

2726 } 

2727 

2728 @HasMemoized.memoized_attribute 

2729 def _pk_attr_keys_by_table(self): 

2730 return { 

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

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

2733 } 

2734 

2735 @HasMemoized.memoized_attribute 

2736 def _server_default_cols( 

2737 self, 

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

2739 return { 

2740 table: frozenset( 

2741 [ 

2742 col 

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

2744 if col.server_default is not None 

2745 or ( 

2746 col.default is not None 

2747 and col.default.is_clause_element 

2748 ) 

2749 ] 

2750 ) 

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

2752 } 

2753 

2754 @HasMemoized.memoized_attribute 

2755 def _server_onupdate_default_cols( 

2756 self, 

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

2758 return { 

2759 table: frozenset( 

2760 [ 

2761 col 

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

2763 if col.server_onupdate is not None 

2764 or ( 

2765 col.onupdate is not None 

2766 and col.onupdate.is_clause_element 

2767 ) 

2768 ] 

2769 ) 

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

2771 } 

2772 

2773 @HasMemoized.memoized_attribute 

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

2775 return { 

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

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

2778 } 

2779 

2780 @HasMemoized.memoized_attribute 

2781 def _server_onupdate_default_col_keys( 

2782 self, 

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

2784 return { 

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

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

2787 } 

2788 

2789 @HasMemoized.memoized_attribute 

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

2791 result: Set[str] = set() 

2792 

2793 col_to_property = self._columntoproperty 

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

2795 result.update( 

2796 col_to_property[col].key 

2797 for col in columns.intersection(col_to_property) 

2798 ) 

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

2800 result.update( 

2801 col_to_property[col].key 

2802 for col in columns.intersection(col_to_property) 

2803 ) 

2804 return result 

2805 

2806 @HasMemoized.memoized_instancemethod 

2807 def __clause_element__(self): 

2808 annotations: Dict[str, Any] = { 

2809 "entity_namespace": self, 

2810 "parententity": self, 

2811 "parentmapper": self, 

2812 } 

2813 if self.persist_selectable is not self.local_table: 

2814 # joined table inheritance, with polymorphic selectable, 

2815 # etc. 

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

2817 { 

2818 "entity_namespace": self, 

2819 "parententity": self, 

2820 "parentmapper": self, 

2821 } 

2822 )._set_propagate_attrs( 

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

2824 ) 

2825 

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

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

2828 ) 

2829 

2830 @util.memoized_property 

2831 def select_identity_token(self): 

2832 return ( 

2833 expression.null() 

2834 ._annotate( 

2835 { 

2836 "entity_namespace": self, 

2837 "parententity": self, 

2838 "parentmapper": self, 

2839 "identity_token": True, 

2840 } 

2841 ) 

2842 ._set_propagate_attrs( 

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

2844 ) 

2845 ) 

2846 

2847 @property 

2848 def selectable(self) -> FromClause: 

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

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

2851 

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

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

2854 full "polymorphic" selectable is returned. 

2855 

2856 """ 

2857 return self._with_polymorphic_selectable 

2858 

2859 def _with_polymorphic_args( 

2860 self, 

2861 spec: Any = None, 

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

2863 innerjoin: bool = False, 

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

2865 if selectable not in (None, False): 

2866 selectable = coercions.expect( 

2867 roles.FromClauseRole, 

2868 selectable, 

2869 ) 

2870 

2871 if self.with_polymorphic: 

2872 if not spec: 

2873 spec = self.with_polymorphic[0] 

2874 if selectable is False: 

2875 selectable = self.with_polymorphic[1] 

2876 elif selectable is False: 

2877 selectable = None 

2878 mappers = self._mappers_from_spec(spec, selectable) 

2879 if selectable is not None: 

2880 return mappers, selectable 

2881 else: 

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

2883 

2884 @HasMemoized.memoized_attribute 

2885 def _polymorphic_properties(self): 

2886 return list( 

2887 self._iterate_polymorphic_properties( 

2888 self._with_polymorphic_mappers 

2889 ) 

2890 ) 

2891 

2892 @property 

2893 def _all_column_expressions(self): 

2894 poly_properties = self._polymorphic_properties 

2895 adapter = self._polymorphic_adapter 

2896 

2897 return [ 

2898 adapter.columns[c] if adapter else c 

2899 for prop in poly_properties 

2900 if isinstance(prop, properties.ColumnProperty) 

2901 and prop._renders_in_subqueries 

2902 for c in prop.columns 

2903 ] 

2904 

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

2906 if polymorphic_mappers: 

2907 poly_properties = self._iterate_polymorphic_properties( 

2908 polymorphic_mappers 

2909 ) 

2910 else: 

2911 poly_properties = self._polymorphic_properties 

2912 

2913 return [ 

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

2915 for prop in poly_properties 

2916 if isinstance(prop, properties.ColumnProperty) 

2917 ] 

2918 

2919 @HasMemoized.memoized_attribute 

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

2921 if self._has_aliased_polymorphic_fromclause: 

2922 return orm_util.ORMAdapter( 

2923 orm_util._TraceAdaptRole.MAPPER_POLYMORPHIC_ADAPTER, 

2924 self, 

2925 selectable=self.selectable, 

2926 equivalents=self._equivalent_columns, 

2927 limit_on_entity=False, 

2928 ) 

2929 else: 

2930 return None 

2931 

2932 def _iterate_polymorphic_properties(self, mappers=None): 

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

2934 a SELECT.""" 

2935 if mappers is None: 

2936 mappers = self._with_polymorphic_mappers 

2937 

2938 if not mappers: 

2939 for c in self.iterate_properties: 

2940 yield c 

2941 else: 

2942 # in the polymorphic case, filter out discriminator columns 

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

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

2945 for c in util.unique_list( 

2946 chain( 

2947 *[ 

2948 list(mapper.iterate_properties) 

2949 for mapper in [self] + mappers 

2950 ] 

2951 ) 

2952 ): 

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

2954 self.polymorphic_on is None 

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

2956 ): 

2957 continue 

2958 yield c 

2959 

2960 @HasMemoized.memoized_attribute 

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

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

2963 associated this mapper. 

2964 

2965 This is an object that provides each property based on 

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

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

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

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

2970 column. The namespace object can also be iterated, 

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

2972 

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

2974 of this attribute which limit the types of properties 

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

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

2977 

2978 .. warning:: 

2979 

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

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

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

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

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

2985 accessing attributes dynamically, favor using the dict-access 

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

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

2988 

2989 .. seealso:: 

2990 

2991 :attr:`_orm.Mapper.all_orm_descriptors` 

2992 

2993 """ 

2994 

2995 self._check_configure() 

2996 return util.ReadOnlyProperties(self._props) 

2997 

2998 @HasMemoized.memoized_attribute 

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

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

3001 with the mapped class. 

3002 

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

3004 associated with the mapped class or its superclasses. 

3005 

3006 This namespace includes attributes that are mapped to the class 

3007 as well as attributes declared by extension modules. 

3008 It includes any Python descriptor type that inherits from 

3009 :class:`.InspectionAttr`. This includes 

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

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

3012 :class:`.AssociationProxy`. 

3013 

3014 To distinguish between mapped attributes and extension attributes, 

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

3016 to a constant that distinguishes between different extension types. 

3017 

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

3019 

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

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

3022 

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

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

3025 3 below. The order will be the 

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

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

3028 or the mapper. 

3029 

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

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

3032 class in which it first appeared. 

3033 

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

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

3036 

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

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

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

3040 referring to the collection of mapped properties via 

3041 :attr:`_orm.Mapper.attrs`. 

3042 

3043 .. warning:: 

3044 

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

3046 accessor namespace is an 

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

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

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

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

3051 accessing attributes dynamically, favor using the dict-access 

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

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

3054 collisions. 

3055 

3056 .. seealso:: 

3057 

3058 :attr:`_orm.Mapper.attrs` 

3059 

3060 """ 

3061 return util.ReadOnlyProperties( 

3062 dict(self.class_manager._all_sqla_attributes()) 

3063 ) 

3064 

3065 @HasMemoized.memoized_attribute 

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

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

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

3069 all synonyms that refer to primary key columns 

3070 

3071 """ 

3072 descriptor_props = util.preloaded.orm_descriptor_props 

3073 

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

3075 

3076 return { 

3077 syn.key: syn.name 

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

3079 if isinstance(syn, descriptor_props.SynonymProperty) 

3080 and syn.name in pk_keys 

3081 } 

3082 

3083 @HasMemoized.memoized_attribute 

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

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

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

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

3088 

3089 .. seealso:: 

3090 

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

3092 :class:`.MapperProperty` 

3093 objects. 

3094 

3095 """ 

3096 descriptor_props = util.preloaded.orm_descriptor_props 

3097 

3098 return self._filter_properties(descriptor_props.SynonymProperty) 

3099 

3100 @util.ro_non_memoized_property 

3101 def entity_namespace(self) -> _EntityNamespace: 

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

3103 

3104 @HasMemoized.memoized_attribute 

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

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

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

3108 

3109 .. seealso:: 

3110 

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

3112 :class:`.MapperProperty` 

3113 objects. 

3114 

3115 """ 

3116 return self._filter_properties(properties.ColumnProperty) 

3117 

3118 @HasMemoized.memoized_attribute 

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

3120 def relationships( 

3121 self, 

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

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

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

3125 

3126 .. warning:: 

3127 

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

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

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

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

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

3133 accessing attributes dynamically, favor using the dict-access 

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

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

3136 collisions. 

3137 

3138 .. seealso:: 

3139 

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

3141 :class:`.MapperProperty` 

3142 objects. 

3143 

3144 """ 

3145 return self._filter_properties( 

3146 util.preloaded.orm_relationships.RelationshipProperty 

3147 ) 

3148 

3149 @HasMemoized.memoized_attribute 

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

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

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

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

3154 

3155 .. seealso:: 

3156 

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

3158 :class:`.MapperProperty` 

3159 objects. 

3160 

3161 """ 

3162 return self._filter_properties( 

3163 util.preloaded.orm_descriptor_props.CompositeProperty 

3164 ) 

3165 

3166 def _filter_properties( 

3167 self, type_: Type[_MP] 

3168 ) -> util.ReadOnlyProperties[_MP]: 

3169 self._check_configure() 

3170 return util.ReadOnlyProperties( 

3171 util.OrderedDict( 

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

3173 ) 

3174 ) 

3175 

3176 @HasMemoized.memoized_attribute 

3177 def _get_clause(self): 

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

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

3180 by primary key. 

3181 

3182 """ 

3183 params = [ 

3184 ( 

3185 primary_key, 

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

3187 ) 

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

3189 ] 

3190 return ( 

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

3192 util.column_dict(params), 

3193 ) 

3194 

3195 @HasMemoized.memoized_attribute 

3196 def _equivalent_columns(self) -> _EquivalentColumnMap: 

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

3198 the determination of column pairs that are equated to 

3199 one another based on inherit condition. This is designed 

3200 to work with the queries that util.polymorphic_union 

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

3202 the base table directly (including the subclass table columns 

3203 only). 

3204 

3205 The resulting structure is a dictionary of columns mapped 

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

3207 

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

3209 

3210 """ # noqa: E501 

3211 result: _EquivalentColumnMap = {} 

3212 

3213 def visit_binary(binary): 

3214 if binary.operator == operators.eq: 

3215 if binary.left in result: 

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

3217 else: 

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

3219 if binary.right in result: 

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

3221 else: 

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

3223 

3224 for mapper in self.base_mapper.self_and_descendants: 

3225 if mapper.inherit_condition is not None: 

3226 visitors.traverse( 

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

3228 ) 

3229 

3230 return result 

3231 

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

3233 if isinstance( 

3234 obj, 

3235 ( 

3236 _MappedAttribute, 

3237 instrumentation.ClassManager, 

3238 expression.ColumnElement, 

3239 ), 

3240 ): 

3241 return False 

3242 else: 

3243 return assigned_name not in self._dataclass_fields 

3244 

3245 @HasMemoized.memoized_attribute 

3246 def _dataclass_fields(self): 

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

3248 

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

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

3251 present on the class. 

3252 

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

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

3255 

3256 """ 

3257 

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

3259 return True 

3260 

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

3262 # either local or from an inherited class 

3263 # ignore dataclass field default values 

3264 if local: 

3265 if self.class_.__dict__.get( 

3266 assigned_name, None 

3267 ) is not None and self._is_userland_descriptor( 

3268 assigned_name, self.class_.__dict__[assigned_name] 

3269 ): 

3270 return True 

3271 else: 

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

3273 if attr is not None and self._is_userland_descriptor( 

3274 assigned_name, attr 

3275 ): 

3276 return True 

3277 

3278 if ( 

3279 self.include_properties is not None 

3280 and name not in self.include_properties 

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

3282 ): 

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

3284 return True 

3285 

3286 if self.exclude_properties is not None and ( 

3287 name in self.exclude_properties 

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

3289 ): 

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

3291 return True 

3292 

3293 return False 

3294 

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

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

3297 common inherited parent as this mapper.""" 

3298 

3299 return self.base_mapper is other.base_mapper 

3300 

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

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

3303 one. common parent but different branch 

3304 

3305 """ 

3306 return ( 

3307 self.base_mapper is other.base_mapper 

3308 and not self.isa(other) 

3309 and not other.isa(self) 

3310 ) 

3311 

3312 def _canload( 

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

3314 ) -> bool: 

3315 s = self.primary_mapper() 

3316 if self.polymorphic_on is not None or allow_subtypes: 

3317 return _state_mapper(state).isa(s) 

3318 else: 

3319 return _state_mapper(state) is s 

3320 

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

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

3323 

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

3325 while m and m is not other: 

3326 m = m.inherits 

3327 return bool(m) 

3328 

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

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

3331 while m: 

3332 yield m 

3333 m = m.inherits 

3334 

3335 @HasMemoized.memoized_attribute 

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

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

3338 

3339 This includes not just the immediately inheriting mappers but 

3340 all their inheriting mappers as well. 

3341 

3342 """ 

3343 descendants = [] 

3344 stack = deque([self]) 

3345 while stack: 

3346 item = stack.popleft() 

3347 descendants.append(item) 

3348 stack.extend(item._inheriting_mappers) 

3349 return util.WeakSequence(descendants) 

3350 

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

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

3353 all descendant mappers. 

3354 

3355 This includes not just the immediately inheriting mappers but 

3356 all their inheriting mappers as well. 

3357 

3358 To iterate through an entire hierarchy, use 

3359 ``mapper.base_mapper.polymorphic_iterator()``. 

3360 

3361 """ 

3362 return iter(self.self_and_descendants) 

3363 

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

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

3366 (class).""" 

3367 

3368 return self.class_manager.mapper 

3369 

3370 @property 

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

3372 return self.class_manager.mapper.base_mapper 

3373 

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

3375 pk_cols: Sequence[ColumnElement[Any]] 

3376 if adapter is not None: 

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

3378 else: 

3379 pk_cols = self.primary_key 

3380 rk = result.keys() 

3381 for col in pk_cols: 

3382 if col not in rk: 

3383 return False 

3384 else: 

3385 return True 

3386 

3387 def identity_key_from_row( 

3388 self, 

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

3390 identity_token: Optional[Any] = None, 

3391 adapter: Optional[ORMAdapter] = None, 

3392 ) -> _IdentityKeyType[_O]: 

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

3394 item from the identity map. 

3395 

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

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

3398 

3399 .. versionchanged:: 2.0 

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

3401 for the "row" argument 

3402 

3403 """ 

3404 pk_cols: Sequence[ColumnElement[Any]] 

3405 if adapter is not None: 

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

3407 else: 

3408 pk_cols = self.primary_key 

3409 

3410 mapping: RowMapping 

3411 if hasattr(row, "_mapping"): 

3412 mapping = row._mapping 

3413 else: 

3414 mapping = row # type: ignore[assignment] 

3415 

3416 return ( 

3417 self._identity_class, 

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

3419 identity_token, 

3420 ) 

3421 

3422 def identity_key_from_primary_key( 

3423 self, 

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

3425 identity_token: Optional[Any] = None, 

3426 ) -> _IdentityKeyType[_O]: 

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

3428 item from an identity map. 

3429 

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

3431 

3432 """ 

3433 return ( 

3434 self._identity_class, 

3435 tuple(primary_key), 

3436 identity_token, 

3437 ) 

3438 

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

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

3441 its primary key attributes. 

3442 

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

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

3445 If the row no longer exists, 

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

3447 

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

3449 attribute name `key`. 

3450 

3451 """ 

3452 state = attributes.instance_state(instance) 

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

3454 

3455 def _identity_key_from_state( 

3456 self, 

3457 state: InstanceState[_O], 

3458 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3459 ) -> _IdentityKeyType[_O]: 

3460 dict_ = state.dict 

3461 manager = state.manager 

3462 return ( 

3463 self._identity_class, 

3464 tuple( 

3465 [ 

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

3467 for prop in self._identity_key_props 

3468 ] 

3469 ), 

3470 state.identity_token, 

3471 ) 

3472 

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

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

3475 instance. 

3476 

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

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

3479 If the row no longer exists, 

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

3481 

3482 """ 

3483 state = attributes.instance_state(instance) 

3484 identity_key = self._identity_key_from_state( 

3485 state, PassiveFlag.PASSIVE_OFF 

3486 ) 

3487 return identity_key[1] 

3488 

3489 @HasMemoized.memoized_attribute 

3490 def _persistent_sortkey_fn(self): 

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

3492 

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

3494 

3495 def key(state): 

3496 return tuple( 

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

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

3499 ) 

3500 

3501 else: 

3502 

3503 def key(state): 

3504 return state.key[1] 

3505 

3506 return key 

3507 

3508 @HasMemoized.memoized_attribute 

3509 def _identity_key_props(self): 

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

3511 

3512 @HasMemoized.memoized_attribute 

3513 def _all_pk_cols(self): 

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

3515 for table in self.tables: 

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

3517 return collection 

3518 

3519 @HasMemoized.memoized_attribute 

3520 def _should_undefer_in_wildcard(self): 

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

3522 if self.polymorphic_on is not None: 

3523 cols.add(self.polymorphic_on) 

3524 return cols 

3525 

3526 @HasMemoized.memoized_attribute 

3527 def _primary_key_propkeys(self): 

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

3529 

3530 def _get_state_attr_by_column( 

3531 self, 

3532 state: InstanceState[_O], 

3533 dict_: _InstanceDict, 

3534 column: ColumnElement[Any], 

3535 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3536 ) -> Any: 

3537 prop = self._columntoproperty[column] 

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

3539 

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

3541 prop = self._columntoproperty[column] 

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

3543 

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

3545 prop = self._columntoproperty[column] 

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

3547 

3548 def _get_committed_attr_by_column(self, obj, column): 

3549 state = attributes.instance_state(obj) 

3550 dict_ = attributes.instance_dict(obj) 

3551 return self._get_committed_state_attr_by_column( 

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

3553 ) 

3554 

3555 def _get_committed_state_attr_by_column( 

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

3557 ): 

3558 prop = self._columntoproperty[column] 

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

3560 state, dict_, passive=passive 

3561 ) 

3562 

3563 def _optimized_get_statement(self, state, attribute_names): 

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

3565 key, using a minimized set of tables. 

3566 

3567 Applies to a joined-table inheritance mapper where the 

3568 requested attribute names are only present on joined tables, 

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

3570 only those tables to minimize joins. 

3571 

3572 """ 

3573 props = self._props 

3574 

3575 col_attribute_names = set(attribute_names).intersection( 

3576 state.mapper.column_attrs.keys() 

3577 ) 

3578 tables: Set[FromClause] = set( 

3579 chain( 

3580 *[ 

3581 sql_util.find_tables(c, check_columns=True) 

3582 for key in col_attribute_names 

3583 for c in props[key].columns 

3584 ] 

3585 ) 

3586 ) 

3587 

3588 if self.base_mapper.local_table in tables: 

3589 return None 

3590 

3591 def visit_binary(binary): 

3592 leftcol = binary.left 

3593 rightcol = binary.right 

3594 if leftcol is None or rightcol is None: 

3595 return 

3596 

3597 if leftcol.table not in tables: 

3598 leftval = self._get_committed_state_attr_by_column( 

3599 state, 

3600 state.dict, 

3601 leftcol, 

3602 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3603 ) 

3604 if leftval in orm_util._none_set: 

3605 raise _OptGetColumnsNotAvailable() 

3606 binary.left = sql.bindparam( 

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

3608 ) 

3609 elif rightcol.table not in tables: 

3610 rightval = self._get_committed_state_attr_by_column( 

3611 state, 

3612 state.dict, 

3613 rightcol, 

3614 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3615 ) 

3616 if rightval in orm_util._none_set: 

3617 raise _OptGetColumnsNotAvailable() 

3618 binary.right = sql.bindparam( 

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

3620 ) 

3621 

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

3623 

3624 start = False 

3625 

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

3627 # we include all intermediary tables. 

3628 

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

3630 if mapper.local_table in tables: 

3631 start = True 

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

3633 return None 

3634 if start and not mapper.single: 

3635 assert mapper.inherits 

3636 assert not mapper.concrete 

3637 assert mapper.inherit_condition is not None 

3638 allconds.append(mapper.inherit_condition) 

3639 tables.add(mapper.local_table) 

3640 

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

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

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

3644 # other. 

3645 try: 

3646 _traversed = visitors.cloned_traverse( 

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

3648 ) 

3649 except _OptGetColumnsNotAvailable: 

3650 return None 

3651 else: 

3652 allconds[0] = _traversed 

3653 

3654 cond = sql.and_(*allconds) 

3655 

3656 cols = [] 

3657 for key in col_attribute_names: 

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

3659 return ( 

3660 sql.select(*cols) 

3661 .where(cond) 

3662 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 

3663 ) 

3664 

3665 def _iterate_to_target_viawpoly(self, mapper): 

3666 if self.isa(mapper): 

3667 prev = self 

3668 for m in self.iterate_to_root(): 

3669 yield m 

3670 

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

3672 break 

3673 

3674 prev = m 

3675 if m is mapper: 

3676 break 

3677 

3678 @HasMemoized.memoized_attribute 

3679 def _would_selectinload_combinations_cache(self): 

3680 return {} 

3681 

3682 def _would_selectin_load_only_from_given_mapper(self, super_mapper): 

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

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

3685 

3686 given:: 

3687 

3688 class A: ... 

3689 

3690 

3691 class B(A): 

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

3693 

3694 

3695 class C(B): ... 

3696 

3697 

3698 class D(B): 

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

3700 

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

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

3703 

3704 OTOH, ``inspect(D) 

3705 ._would_selectin_load_only_from_given_mapper(inspect(B))`` 

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

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

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

3709 set up separately. 

3710 

3711 Added as part of #9373. 

3712 

3713 """ 

3714 cache = self._would_selectinload_combinations_cache 

3715 

3716 try: 

3717 return cache[super_mapper] 

3718 except KeyError: 

3719 pass 

3720 

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

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

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

3724 # mappers or other objects. 

3725 assert self.isa(super_mapper) 

3726 

3727 mapper = super_mapper 

3728 for m in self._iterate_to_target_viawpoly(mapper): 

3729 if m.polymorphic_load == "selectin": 

3730 retval = m is super_mapper 

3731 break 

3732 else: 

3733 retval = False 

3734 

3735 cache[super_mapper] = retval 

3736 return retval 

3737 

3738 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 

3739 if not enabled_via_opt: 

3740 # common case, takes place for all polymorphic loads 

3741 mapper = polymorphic_from 

3742 for m in self._iterate_to_target_viawpoly(mapper): 

3743 if m.polymorphic_load == "selectin": 

3744 return m 

3745 else: 

3746 # uncommon case, selectin load options were used 

3747 enabled_via_opt = set(enabled_via_opt) 

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

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

3750 mapper = entity.mapper 

3751 for m in self._iterate_to_target_viawpoly(mapper): 

3752 if ( 

3753 m.polymorphic_load == "selectin" 

3754 or m in enabled_via_opt_mappers 

3755 ): 

3756 return enabled_via_opt_mappers.get(m, m) 

3757 

3758 return None 

3759 

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

3761 def _subclass_load_via_in(self, entity, polymorphic_from): 

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

3763 this subclass as a SELECT with IN. 

3764 

3765 """ 

3766 

3767 strategy_options = util.preloaded.orm_strategy_options 

3768 

3769 assert self.inherits 

3770 

3771 if self.polymorphic_on is not None: 

3772 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 

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

3774 else: 

3775 keep_props = set(self._identity_key_props) 

3776 

3777 disable_opt = strategy_options.Load(entity) 

3778 enable_opt = strategy_options.Load(entity) 

3779 

3780 classes_to_include = {self} 

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

3782 while ( 

3783 m is not None 

3784 and m is not polymorphic_from 

3785 and m.polymorphic_load == "selectin" 

3786 ): 

3787 classes_to_include.add(m) 

3788 m = m.inherits 

3789 

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

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

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

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

3794 if prop.key not in self.class_manager: 

3795 continue 

3796 

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

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

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

3800 if not isinstance(prop, StrategizedProperty): 

3801 continue 

3802 

3803 enable_opt = enable_opt._set_generic_strategy( 

3804 # convert string name to an attribute before passing 

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

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

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

3808 dict(prop.strategy_key), 

3809 _reconcile_to_other=True, 

3810 ) 

3811 else: 

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

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

3814 # the options from the query to override them 

3815 disable_opt = disable_opt._set_generic_strategy( 

3816 # convert string name to an attribute before passing 

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

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

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

3820 {"do_nothing": True}, 

3821 _reconcile_to_other=False, 

3822 ) 

3823 

3824 primary_key = [ 

3825 sql_util._deep_annotate(pk, {"_orm_adapt": True}) 

3826 for pk in self.primary_key 

3827 ] 

3828 

3829 in_expr: ColumnElement[Any] 

3830 

3831 if len(primary_key) > 1: 

3832 in_expr = sql.tuple_(*primary_key) 

3833 else: 

3834 in_expr = primary_key[0] 

3835 

3836 if entity.is_aliased_class: 

3837 assert entity.mapper is self 

3838 

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

3840 LABEL_STYLE_TABLENAME_PLUS_COL 

3841 ) 

3842 

3843 in_expr = entity._adapter.traverse(in_expr) 

3844 primary_key = [entity._adapter.traverse(k) for k in primary_key] 

3845 q = q.where( 

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

3847 ).order_by(*primary_key) 

3848 else: 

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

3850 LABEL_STYLE_TABLENAME_PLUS_COL 

3851 ) 

3852 q = q.where( 

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

3854 ).order_by(*primary_key) 

3855 

3856 return q, enable_opt, disable_opt 

3857 

3858 @HasMemoized.memoized_attribute 

3859 def _subclass_load_via_in_mapper(self): 

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

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

3862 

3863 def cascade_iterator( 

3864 self, 

3865 type_: str, 

3866 state: InstanceState[_O], 

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

3868 ) -> Iterator[ 

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

3870 ]: 

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

3872 for all relationships that meet the given cascade rule. 

3873 

3874 :param type\_: 

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

3876 etc.). 

3877 

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

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

3880 

3881 :param state: 

3882 The lead InstanceState. child items will be processed per 

3883 the relationships defined for this object's mapper. 

3884 

3885 :return: the method yields individual object instances. 

3886 

3887 .. seealso:: 

3888 

3889 :ref:`unitofwork_cascades` 

3890 

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

3892 traverse all objects without relying on cascades. 

3893 

3894 """ 

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

3896 prp, mpp = object(), object() 

3897 

3898 assert state.mapper.isa(self) 

3899 

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

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

3902 visitables: Deque[ 

3903 Tuple[ 

3904 Deque[Any], 

3905 object, 

3906 Optional[InstanceState[Any]], 

3907 Optional[_InstanceDict], 

3908 ] 

3909 ] 

3910 

3911 visitables = deque( 

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

3913 ) 

3914 

3915 while visitables: 

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

3917 if not iterator: 

3918 visitables.pop() 

3919 continue 

3920 

3921 if item_type is prp: 

3922 prop = iterator.popleft() 

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

3924 continue 

3925 assert parent_state is not None 

3926 assert parent_dict is not None 

3927 queue = deque( 

3928 prop.cascade_iterator( 

3929 type_, 

3930 parent_state, 

3931 parent_dict, 

3932 visited_states, 

3933 halt_on, 

3934 ) 

3935 ) 

3936 if queue: 

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

3938 elif item_type is mpp: 

3939 ( 

3940 instance, 

3941 instance_mapper, 

3942 corresponding_state, 

3943 corresponding_dict, 

3944 ) = iterator.popleft() 

3945 yield ( 

3946 instance, 

3947 instance_mapper, 

3948 corresponding_state, 

3949 corresponding_dict, 

3950 ) 

3951 visitables.append( 

3952 ( 

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

3954 prp, 

3955 corresponding_state, 

3956 corresponding_dict, 

3957 ) 

3958 ) 

3959 

3960 @HasMemoized.memoized_attribute 

3961 def _compiled_cache(self): 

3962 return util.LRUCache(self._compiled_cache_size) 

3963 

3964 @HasMemoized.memoized_attribute 

3965 def _multiple_persistence_tables(self): 

3966 return len(self.tables) > 1 

3967 

3968 @HasMemoized.memoized_attribute 

3969 def _sorted_tables(self): 

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

3971 

3972 for mapper in self.base_mapper.self_and_descendants: 

3973 for t in mapper.tables: 

3974 table_to_mapper.setdefault(t, mapper) 

3975 

3976 extra_dependencies = [] 

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

3978 super_ = mapper.inherits 

3979 if super_: 

3980 extra_dependencies.extend( 

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

3982 ) 

3983 

3984 def skip(fk): 

3985 # attempt to skip dependencies that are not 

3986 # significant to the inheritance chain 

3987 # for two tables that are related by inheritance. 

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

3989 # not what we mean to sort on here. 

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

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

3992 if ( 

3993 parent is not None 

3994 and dep is not None 

3995 and dep is not parent 

3996 and dep.inherit_condition is not None 

3997 ): 

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

3999 if parent.inherit_condition is not None: 

4000 cols = cols.union( 

4001 sql_util._find_columns(parent.inherit_condition) 

4002 ) 

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

4004 else: 

4005 return fk.parent not in cols 

4006 return False 

4007 

4008 sorted_ = sql_util.sort_tables( 

4009 table_to_mapper, 

4010 skip_fn=skip, 

4011 extra_dependencies=extra_dependencies, 

4012 ) 

4013 

4014 ret = util.OrderedDict() 

4015 for t in sorted_: 

4016 ret[t] = table_to_mapper[t] 

4017 return ret 

4018 

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

4020 if key in self._memoized_values: 

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

4022 else: 

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

4024 return value 

4025 

4026 @util.memoized_property 

4027 def _table_to_equated(self): 

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

4029 synchronized upwards to the base mapper.""" 

4030 

4031 result: util.defaultdict[ 

4032 Table, 

4033 List[ 

4034 Tuple[ 

4035 Mapper[Any], 

4036 List[Tuple[ColumnElement[Any], ColumnElement[Any]]], 

4037 ] 

4038 ], 

4039 ] = util.defaultdict(list) 

4040 

4041 def set_union(x, y): 

4042 return x.union(y) 

4043 

4044 for table in self._sorted_tables: 

4045 cols = set(table.c) 

4046 

4047 for m in self.iterate_to_root(): 

4048 if m._inherits_equated_pairs and cols.intersection( 

4049 reduce( 

4050 set_union, 

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

4052 ) 

4053 ): 

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

4055 

4056 return result 

4057 

4058 

4059class _OptGetColumnsNotAvailable(Exception): 

4060 pass 

4061 

4062 

4063def configure_mappers() -> None: 

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

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

4066 collections. 

4067 

4068 The configure step is used to reconcile and initialize the 

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

4070 invoke configuration events such as the 

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

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

4073 extensions or user-defined extension hooks. 

4074 

4075 Mapper configuration is normally invoked automatically, the first time 

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

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

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

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

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

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

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

4083 

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

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

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

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

4088 in use. 

4089 

4090 .. versionchanged:: 1.4 

4091 

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

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

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

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

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

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

4098 

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

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

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

4102 with an ORM-enabled statement. 

4103 

4104 The mapper configure process, whether invoked by 

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

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

4107 configuration step. These hooks include: 

4108 

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

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

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

4112 related mappings before the operation proceeds. 

4113 

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

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

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

4117 to be configured. 

4118 

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

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

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

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

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

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

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

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

4127 current scope of configuration. 

4128 

4129 """ 

4130 

4131 _configure_registries(_all_registries(), cascade=True) 

4132 

4133 

4134def _configure_registries( 

4135 registries: Set[_RegistryType], cascade: bool 

4136) -> None: 

4137 for reg in registries: 

4138 if reg._new_mappers: 

4139 break 

4140 else: 

4141 return 

4142 

4143 with _CONFIGURE_MUTEX: 

4144 global _already_compiling 

4145 if _already_compiling: 

4146 return 

4147 _already_compiling = True 

4148 try: 

4149 # double-check inside mutex 

4150 for reg in registries: 

4151 if reg._new_mappers: 

4152 break 

4153 else: 

4154 return 

4155 

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

4157 # initialize properties on all mappers 

4158 # note that _mapper_registry is unordered, which 

4159 # may randomly conceal/reveal issues related to 

4160 # the order of mapper compilation 

4161 

4162 _do_configure_registries(registries, cascade) 

4163 finally: 

4164 _already_compiling = False 

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

4166 

4167 

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

4169def _do_configure_registries( 

4170 registries: Set[_RegistryType], cascade: bool 

4171) -> None: 

4172 registry = util.preloaded.orm_decl_api.registry 

4173 

4174 orig = set(registries) 

4175 

4176 for reg in registry._recurse_with_dependencies(registries): 

4177 has_skip = False 

4178 

4179 for mapper in reg._mappers_to_configure(): 

4180 run_configure = None 

4181 

4182 for fn in mapper.dispatch.before_mapper_configured: 

4183 run_configure = fn(mapper, mapper.class_) 

4184 if run_configure is EXT_SKIP: 

4185 has_skip = True 

4186 break 

4187 if run_configure is EXT_SKIP: 

4188 continue 

4189 

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

4191 e = sa_exc.InvalidRequestError( 

4192 "One or more mappers failed to initialize - " 

4193 "can't proceed with initialization of other " 

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

4195 "Original exception was: %s" 

4196 % (mapper, mapper._configure_failed) 

4197 ) 

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

4199 raise e 

4200 

4201 if not mapper.configured: 

4202 try: 

4203 mapper._post_configure_properties() 

4204 mapper._expire_memoizations() 

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

4206 except Exception: 

4207 exc = sys.exc_info()[1] 

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

4209 mapper._configure_failed = exc 

4210 raise 

4211 if not has_skip: 

4212 reg._new_mappers = False 

4213 

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

4215 raise sa_exc.InvalidRequestError( 

4216 "configure was called with cascade=False but " 

4217 "additional registries remain" 

4218 ) 

4219 

4220 

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

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

4223 registry = util.preloaded.orm_decl_api.registry 

4224 

4225 orig = set(registries) 

4226 

4227 for reg in registry._recurse_with_dependents(registries): 

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

4229 raise sa_exc.InvalidRequestError( 

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

4231 "pass cascade=True to clear these also" 

4232 ) 

4233 

4234 while reg._managers: 

4235 try: 

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

4237 except KeyError: 

4238 # guard against race between while and popitem 

4239 pass 

4240 else: 

4241 reg._dispose_manager_and_mapper(manager) 

4242 

4243 reg._dependents.clear() 

4244 for dep in reg._dependencies: 

4245 dep._dependents.discard(reg) 

4246 reg._dependencies.clear() 

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

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

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

4250 # disposed. 

4251 reg._new_mappers = False 

4252 

4253 

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

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

4256 

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

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

4259 loaded from the database or otherwise reconstituted. 

4260 

4261 .. tip:: 

4262 

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

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

4265 used directly. 

4266 

4267 The reconstructor will be invoked with no arguments. Scalar 

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

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

4270 collections are generally not yet available and will usually only 

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

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

4273 the activity within a reconstructor should be conservative. 

4274 

4275 .. seealso:: 

4276 

4277 :meth:`.InstanceEvents.load` 

4278 

4279 """ 

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

4281 return fn 

4282 

4283 

4284def validates( 

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

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

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

4288 

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

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

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

4292 The function can then raise validation exceptions to halt the 

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

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

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

4296 otherwise return the given value. 

4297 

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

4299 collection within the validation routine - this usage raises 

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

4301 condition which is not supported. 

4302 

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

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

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

4306 argument "is_remove" which will be a boolean. 

4307 

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

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

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

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

4312 attribute operation. 

4313 

4314 .. versionchanged:: 2.0.16 This paramter inadvertently defaulted to 

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

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

4317 

4318 .. seealso:: 

4319 

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

4321 

4322 """ 

4323 

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

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

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

4327 "include_removes": include_removes, 

4328 "include_backrefs": include_backrefs, 

4329 } 

4330 return fn 

4331 

4332 return wrap 

4333 

4334 

4335def _event_on_load(state, ctx): 

4336 instrumenting_mapper = state.manager.mapper 

4337 

4338 if instrumenting_mapper._reconstructor: 

4339 instrumenting_mapper._reconstructor(state.obj()) 

4340 

4341 

4342def _event_on_init(state, args, kwargs): 

4343 """Run init_instance hooks. 

4344 

4345 This also includes mapper compilation, normally not needed 

4346 here but helps with some piecemeal configuration 

4347 scenarios (such as in the ORM tutorial). 

4348 

4349 """ 

4350 

4351 instrumenting_mapper = state.manager.mapper 

4352 if instrumenting_mapper: 

4353 instrumenting_mapper._check_configure() 

4354 if instrumenting_mapper._set_polymorphic_identity: 

4355 instrumenting_mapper._set_polymorphic_identity(state) 

4356 

4357 

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

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

4360 

4361 __slots__ = ("mapper",) 

4362 

4363 def __init__(self, mapper): 

4364 # TODO: weakref would be a good idea here 

4365 self.mapper = mapper 

4366 

4367 def __missing__(self, column): 

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

4369 if prop: 

4370 raise orm_exc.UnmappedColumnError( 

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

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

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

4374 ) 

4375 raise orm_exc.UnmappedColumnError( 

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

4377 % (column, self.mapper) 

4378 )