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

1440 statements  

1# orm/mapper.py 

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

3# <see AUTHORS file> 

4# 

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

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

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

8 

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

10 

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

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

13 

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

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

16 

17""" 

18 

19from __future__ import annotations 

20 

21from collections import deque 

22from functools import reduce 

23from itertools import chain 

24import sys 

25import threading 

26from typing import Any 

27from typing import Callable 

28from typing import cast 

29from typing import Collection 

30from typing import Deque 

31from typing import Dict 

32from typing import FrozenSet 

33from typing import Generic 

34from typing import Iterable 

35from typing import Iterator 

36from typing import List 

37from typing import Literal 

38from typing import Mapping 

39from typing import Optional 

40from typing import Sequence 

41from typing import Set 

42from typing import Tuple 

43from typing import Type 

44from typing import TYPE_CHECKING 

45from typing import TypeVar 

46from typing import Union 

47import weakref 

48 

49from . import attributes 

50from . import exc as orm_exc 

51from . import instrumentation 

52from . import loading 

53from . import properties 

54from . import util as orm_util 

55from ._typing import _O 

56from .base import _class_to_mapper 

57from .base import _parse_mapper_argument 

58from .base import _state_mapper 

59from .base import PassiveFlag 

60from .base import state_str 

61from .interfaces import _MappedAttribute 

62from .interfaces import EXT_SKIP 

63from .interfaces import InspectionAttr 

64from .interfaces import MapperProperty 

65from .interfaces import ORMEntityColumnsClauseRole 

66from .interfaces import ORMFromClauseRole 

67from .interfaces import StrategizedProperty 

68from .path_registry import PathRegistry 

69from .. import event 

70from .. import exc as sa_exc 

71from .. import inspection 

72from .. import log 

73from .. import schema 

74from .. import sql 

75from .. import util 

76from ..event import dispatcher 

77from ..event import EventTarget 

78from ..sql import base as sql_base 

79from ..sql import coercions 

80from ..sql import expression 

81from ..sql import operators 

82from ..sql import roles 

83from ..sql import TableClause 

84from ..sql import util as sql_util 

85from ..sql import visitors 

86from ..sql.cache_key import MemoizedHasCacheKey 

87from ..sql.elements import KeyedColumnElement 

88from ..sql.schema import Column 

89from ..sql.schema import Table 

90from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

91from ..util import HasMemoized 

92from ..util import HasMemoized_ro_memoized_attribute 

93from ..util.typing import TupleAny 

94from ..util.typing import Unpack 

95 

96if TYPE_CHECKING: 

97 from ._typing import _IdentityKeyType 

98 from ._typing import _InstanceDict 

99 from ._typing import _ORMColumnExprArgument 

100 from ._typing import _RegistryType 

101 from .decl_api import registry 

102 from .dependency import _DependencyProcessor 

103 from .descriptor_props import CompositeProperty 

104 from .descriptor_props import SynonymProperty 

105 from .events import MapperEvents 

106 from .instrumentation import ClassManager 

107 from .path_registry import _CachingEntityRegistry 

108 from .properties import ColumnProperty 

109 from .relationships import RelationshipProperty 

110 from .state import InstanceState 

111 from .util import ORMAdapter 

112 from ..engine import Row 

113 from ..engine import RowMapping 

114 from ..sql._typing import _ColumnExpressionArgument 

115 from ..sql._typing import _EquivalentColumnMap 

116 from ..sql.base import _EntityNamespace 

117 from ..sql.base import ReadOnlyColumnCollection 

118 from ..sql.elements import ColumnClause 

119 from ..sql.elements import ColumnElement 

120 from ..sql.selectable import FromClause 

121 from ..util import OrderedSet 

122 

123 

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

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

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

127 

128 

129_WithPolymorphicArg = Union[ 

130 Literal["*"], 

131 Tuple[ 

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

133 Optional["FromClause"], 

134 ], 

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

136] 

137 

138 

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

140 weakref.WeakKeyDictionary() 

141) 

142 

143 

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

145 with _CONFIGURE_MUTEX: 

146 return set(_mapper_registries) 

147 

148 

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

150 for reg in _all_registries(): 

151 yield from reg._mappers_to_configure() 

152 

153 

154_already_compiling = False 

155 

156 

157# a constant returned by _get_attr_by_column to indicate 

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

159# column 

160NO_ATTRIBUTE = util.symbol("NO_ATTRIBUTE") 

161 

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

163_CONFIGURE_MUTEX = threading.RLock() 

164 

165 

166@inspection._self_inspects 

167@log.class_logger 

168class Mapper( 

169 ORMFromClauseRole, 

170 ORMEntityColumnsClauseRole[_O], 

171 MemoizedHasCacheKey, 

172 InspectionAttr, 

173 log.Identified, 

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

175 EventTarget, 

176 Generic[_O], 

177): 

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

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

180 proceed. 

181 

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

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

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

185 :ref:`orm_mapping_classes_toplevel`. 

186 

187 """ 

188 

189 dispatch: dispatcher[Mapper[_O]] 

190 

191 _dispose_called = False 

192 _configure_failed: Any = False 

193 _ready_for_configure = False 

194 

195 def __init__( 

196 self, 

197 class_: Type[_O], 

198 local_table: Optional[FromClause] = None, 

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

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

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

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

203 inherit_foreign_keys: Optional[ 

204 Sequence[_ORMColumnExprArgument[Any]] 

205 ] = None, 

206 always_refresh: bool = False, 

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

208 version_id_generator: Optional[ 

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

210 ] = None, 

211 polymorphic_on: Optional[ 

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

213 ] = None, 

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

215 polymorphic_identity: Optional[Any] = None, 

216 concrete: bool = False, 

217 with_polymorphic: Optional[_WithPolymorphicArg] = None, 

218 polymorphic_abstract: bool = False, 

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

220 allow_partial_pks: bool = True, 

221 batch: bool = True, 

222 column_prefix: Optional[str] = None, 

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

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

225 passive_updates: bool = True, 

226 passive_deletes: bool = False, 

227 confirm_deleted_rows: bool = True, 

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

229 legacy_is_orphan: bool = False, 

230 _compiled_cache_size: int = 100, 

231 ): 

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

233 

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

235 is normally invoked through the 

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

237 :ref:`Declarative <orm_declarative_mapping>` or 

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

239 

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

241 removed; for a classical mapping configuration, use the 

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

243 

244 Parameters documented below may be passed to either the 

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

246 ``__mapper_args__`` declarative class attribute described at 

247 :ref:`orm_declarative_mapper_options`. 

248 

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

250 this argument is automatically passed as the declared class 

251 itself. 

252 

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

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

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

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

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

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

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

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

261 present. 

262 

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

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

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

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

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

268 inheritance scheme which uses 

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

270 

271 .. versionadded:: 2.0 

272 

273 .. seealso:: 

274 

275 :ref:`orm_inheritance_abstract_poly` 

276 

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

278 class will overwrite all data within object instances that already 

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

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

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

282 :meth:`_query.Query.populate_existing`. 

283 

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

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

286 possibly existing within the database. This affects whether a 

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

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

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

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

291 

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

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

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

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

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

297 

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

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

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

301 has partial NULL values will not be emitted. 

302 

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

304 of multiple entities can be batched together for efficiency. 

305 Setting to False indicates 

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

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

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

309 in between individual row persistence operations. 

310 

311 :param column_prefix: A string which will be prepended 

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

313 objects are automatically assigned as attributes to the 

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

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

316 dictionary. 

317 

318 This parameter is typically useful with imperative mappings 

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

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

321 ``user_id``, ``user_name``, and ``password``:: 

322 

323 class User(Base): 

324 __table__ = user_table 

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

326 

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

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

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

330 

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

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

333 approach to automating a naming scheme is to intercept the 

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

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

336 pattern. 

337 

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

339 table inheritance with its parent mapper. 

340 

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

342 

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

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

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

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

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

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

349 exception in a future release. 

350 

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

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

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

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

355 are needed immediately before the flush completes. 

356 

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

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

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

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

361 

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

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

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

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

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

367 attributes are not to be accessed in any case. 

368 

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

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

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

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

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

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

375 defaults will not be fetched. 

376 

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

378 :paramref:`.Mapper.eager_defaults` 

379 

380 .. seealso:: 

381 

382 :ref:`orm_server_defaults` 

383 

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

385 INSERTed at once using the 

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

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

388 feature to be very performant on supporting backends. 

389 

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

391 be excluded from mapping. 

392 

393 .. seealso:: 

394 

395 :ref:`include_exclude_cols` 

396 

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

398 names to map. 

399 

400 .. seealso:: 

401 

402 :ref:`include_exclude_cols` 

403 

404 :param inherits: A mapped class or the corresponding 

405 :class:`_orm.Mapper` 

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

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

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

409 is passed automatically as a result of the natural class 

410 hierarchy of the declared classes. 

411 

412 .. seealso:: 

413 

414 :ref:`inheritance_toplevel` 

415 

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

417 expression which will 

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

419 between the two tables. 

420 

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

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

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

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

425 

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

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

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

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

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

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

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

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

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

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

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

437 orphan object has been flushed yet or not. 

438 

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

440 for more detail on this change. 

441 

442 :param passive_deletes: Indicates DELETE behavior of foreign key 

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

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

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

446 on the superclass mapper. 

447 

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

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

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

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

452 superclass table, and not this table. 

453 

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

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

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

457 validate these attributes; note that the primary key columns 

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

459 the object as a whole. 

460 

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

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

463 to specify passive_deletes without this taking effect for 

464 all subclass mappers. 

465 

466 .. seealso:: 

467 

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

469 used with :func:`_orm.relationship` 

470 

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

472 CASCADE for joined-table inheritance mappers 

473 

474 :param passive_updates: Indicates UPDATE behavior of foreign key 

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

476 inheritance mapping. Defaults to ``True``. 

477 

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

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

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

481 on joined-table rows. 

482 

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

484 referential integrity and will not be issuing its own CASCADE 

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

486 emit an UPDATE statement for the dependent columns during a 

487 primary key change. 

488 

489 .. seealso:: 

490 

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

492 used with :func:`_orm.relationship` 

493 

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

495 CASCADE for joined-table inheritance mappers 

496 

497 :param polymorphic_load: Specifies "polymorphic loading" behavior 

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

499 table inheritance only). Valid values are: 

500 

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

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

503 in a SELECT query against the base. 

504 

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

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

507 the columns specific to this subclass. The SELECT uses 

508 IN to fetch multiple subclasses at once. 

509 

510 .. seealso:: 

511 

512 :ref:`with_polymorphic_mapper_config` 

513 

514 :ref:`polymorphic_selectin` 

515 

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

517 SQL expression used to determine the target class for an 

518 incoming row, when inheriting classes are present. 

519 

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

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

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

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

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

525 

526 class Employee(Base): 

527 __tablename__ = "employee" 

528 

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

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

531 

532 __mapper_args__ = { 

533 "polymorphic_on": discriminator, 

534 "polymorphic_identity": "employee", 

535 } 

536 

537 It may also be specified 

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

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

540 approach:: 

541 

542 class Employee(Base): 

543 __tablename__ = "employee" 

544 

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

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

547 

548 __mapper_args__ = { 

549 "polymorphic_on": case( 

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

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

552 else_="employee", 

553 ), 

554 "polymorphic_identity": "employee", 

555 } 

556 

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

558 which is of particular use when using annotated column 

559 configurations:: 

560 

561 class Employee(Base): 

562 __tablename__ = "employee" 

563 

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

565 discriminator: Mapped[str] 

566 

567 __mapper_args__ = { 

568 "polymorphic_on": "discriminator", 

569 "polymorphic_identity": "employee", 

570 } 

571 

572 When setting ``polymorphic_on`` to reference an 

573 attribute or expression that's not present in the 

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

575 of the discriminator should be persisted to the database, 

576 the value of the 

577 discriminator is not automatically set on new 

578 instances; this must be handled by the user, 

579 either through manual means or via event listeners. 

580 A typical approach to establishing such a listener 

581 looks like:: 

582 

583 from sqlalchemy import event 

584 from sqlalchemy.orm import object_mapper 

585 

586 

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

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

589 mapper = object_mapper(instance) 

590 instance.discriminator = mapper.polymorphic_identity 

591 

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

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

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

595 in the database. 

596 

597 .. warning:: 

598 

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

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

601 columns are not yet supported. 

602 

603 .. seealso:: 

604 

605 :ref:`inheritance_toplevel` 

606 

607 :param polymorphic_identity: Specifies the value which 

608 identifies this particular class as returned by the column expression 

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

610 rows are received, the value corresponding to the 

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

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

613 reconstructed object. 

614 

615 .. seealso:: 

616 

617 :ref:`inheritance_toplevel` 

618 

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

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

621 persistence behavior of that attribute. Note that 

622 :class:`_schema.Column` 

623 objects present in 

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

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

626 When using Declarative, this argument is passed automatically, 

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

628 in the declared class body. 

629 

630 .. seealso:: 

631 

632 :ref:`orm_mapping_properties` - in the 

633 :ref:`orm_mapping_classes_toplevel` 

634 

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

636 objects, or alternatively string names of attribute names which 

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

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

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

640 can be overridden here. 

641 

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

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

644 

645 .. seealso:: 

646 

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

648 

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

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

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

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

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

654 version id, a 

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

656 thrown. 

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

658 unless ``version_id_generator`` specifies an alternative version 

659 generator. 

660 

661 .. seealso:: 

662 

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

664 and rationale. 

665 

666 :param version_id_generator: Define how new version ids should 

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

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

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

670 

671 def generate_version(version): 

672 return next_version 

673 

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

675 or programmatic versioning schemes outside of the version id 

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

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

678 of important points when using this option. 

679 

680 .. seealso:: 

681 

682 :ref:`custom_version_counter` 

683 

684 :ref:`server_side_version_counter` 

685 

686 

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

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

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

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

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

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

693 loaded immediately. The second tuple argument <selectable> 

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

695 classes. 

696 

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

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

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

700 indicating polymorphic loading styles. 

701 

702 .. seealso:: 

703 

704 :ref:`with_polymorphic_mapper_config` 

705 

706 """ 

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

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

709 self.class_.__module__, 

710 self.class_.__name__, 

711 ) 

712 

713 self._primary_key_argument = util.to_list(primary_key) 

714 

715 self.always_refresh = always_refresh 

716 

717 if isinstance(version_id_col, MapperProperty): 

718 self.version_id_prop = version_id_col 

719 self.version_id_col = None 

720 else: 

721 self.version_id_col = ( 

722 coercions.expect( 

723 roles.ColumnArgumentOrKeyRole, 

724 version_id_col, 

725 argname="version_id_col", 

726 ) 

727 if version_id_col is not None 

728 else None 

729 ) 

730 

731 if version_id_generator is False: 

732 self.version_id_generator = False 

733 elif version_id_generator is None: 

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

735 else: 

736 self.version_id_generator = version_id_generator 

737 

738 self.concrete = concrete 

739 self.single = False 

740 

741 if inherits is not None: 

742 self.inherits = _parse_mapper_argument(inherits) 

743 else: 

744 self.inherits = None 

745 

746 if local_table is not None: 

747 self.local_table = coercions.expect( 

748 roles.FromClauseRole, 

749 local_table, 

750 disable_inspection=True, 

751 argname="local_table", 

752 ) 

753 elif self.inherits: 

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

755 # .local_table need not be Optional 

756 self.local_table = self.inherits.local_table 

757 self.single = True 

758 else: 

759 raise sa_exc.ArgumentError( 

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

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

762 ) 

763 

764 if inherit_condition is not None: 

765 self.inherit_condition = coercions.expect( 

766 roles.OnClauseRole, inherit_condition 

767 ) 

768 else: 

769 self.inherit_condition = None 

770 

771 self.inherit_foreign_keys = inherit_foreign_keys 

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

773 self._delete_orphans = [] 

774 self.batch = batch 

775 self.eager_defaults = eager_defaults 

776 self.column_prefix = column_prefix 

777 

778 # interim - polymorphic_on is further refined in 

779 # _configure_polymorphic_setter 

780 self.polymorphic_on = ( 

781 coercions.expect( # type: ignore[assignment] 

782 roles.ColumnArgumentOrKeyRole, 

783 polymorphic_on, 

784 argname="polymorphic_on", 

785 ) 

786 if polymorphic_on is not None 

787 else None 

788 ) 

789 self.polymorphic_abstract = polymorphic_abstract 

790 self._dependency_processors = [] 

791 self.validators = util.EMPTY_DICT 

792 self.passive_updates = passive_updates 

793 self.passive_deletes = passive_deletes 

794 self.legacy_is_orphan = legacy_is_orphan 

795 self._clause_adapter = None 

796 self._requires_row_aliasing = False 

797 self._inherits_equated_pairs = None 

798 self._memoized_values = {} 

799 self._compiled_cache_size = _compiled_cache_size 

800 self._reconstructor = None 

801 self.allow_partial_pks = allow_partial_pks 

802 

803 if self.inherits and not self.concrete: 

804 self.confirm_deleted_rows = False 

805 else: 

806 self.confirm_deleted_rows = confirm_deleted_rows 

807 

808 self._set_with_polymorphic(with_polymorphic) 

809 self.polymorphic_load = polymorphic_load 

810 

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

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

813 # the object instance for that row. 

814 self.polymorphic_identity = polymorphic_identity 

815 

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

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

818 # upon a select operation. 

819 if _polymorphic_map is None: 

820 self.polymorphic_map = {} 

821 else: 

822 self.polymorphic_map = _polymorphic_map 

823 

824 if include_properties is not None: 

825 self.include_properties = util.to_set(include_properties) 

826 else: 

827 self.include_properties = None 

828 if exclude_properties: 

829 self.exclude_properties = util.to_set(exclude_properties) 

830 else: 

831 self.exclude_properties = None 

832 

833 # prevent this mapper from being constructed 

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

835 # configure_mappers() until construction succeeds) 

836 with _CONFIGURE_MUTEX: 

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

838 class_, self 

839 ) 

840 self._configure_inheritance() 

841 self._configure_class_instrumentation() 

842 self._configure_properties() 

843 self._configure_polymorphic_setter() 

844 self._configure_pks() 

845 self.registry._flag_new_mapper(self) 

846 self._log("constructed") 

847 self._expire_memoizations() 

848 

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

850 

851 def _prefer_eager_defaults(self, dialect, table): 

852 if self.eager_defaults == "auto": 

853 if not table.implicit_returning: 

854 return False 

855 

856 return ( 

857 table in self._server_default_col_keys 

858 and dialect.insert_executemany_returning 

859 ) 

860 else: 

861 return self.eager_defaults 

862 

863 def _gen_cache_key(self, anon_map, bindparams): 

864 return (self,) 

865 

866 # ### BEGIN 

867 # ATTRIBUTE DECLARATIONS START HERE 

868 

869 is_mapper = True 

870 """Part of the inspection API.""" 

871 

872 represents_outer_join = False 

873 

874 registry: _RegistryType 

875 

876 @property 

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

878 """Part of the inspection API. 

879 

880 Returns self. 

881 

882 """ 

883 return self 

884 

885 @property 

886 def entity(self): 

887 r"""Part of the inspection API. 

888 

889 Returns self.class\_. 

890 

891 """ 

892 return self.class_ 

893 

894 class_: Type[_O] 

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

896 

897 _identity_class: Type[_O] 

898 

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

900 _dependency_processors: List[_DependencyProcessor] 

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

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

903 _all_tables: Set[TableClause] 

904 _polymorphic_attr_key: Optional[str] 

905 

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

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

908 

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

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

911 

912 _columntoproperty: _ColumnMapping 

913 

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

915 _validate_polymorphic_identity: Optional[ 

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

917 ] 

918 

919 tables: Sequence[TableClause] 

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

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

922 is aware of. 

923 

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

925 :class:`_expression.Alias` 

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

927 :class:`_schema.Table` 

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

929 

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

931 Behavior is undefined if directly modified. 

932 

933 """ 

934 

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

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

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

938 

939 The dictionary contains string attribute names as keys 

940 mapped to the actual validation method. 

941 

942 """ 

943 

944 always_refresh: bool 

945 allow_partial_pks: bool 

946 version_id_col: Optional[ColumnElement[Any]] 

947 

948 with_polymorphic: Optional[ 

949 Tuple[ 

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

951 Optional[FromClause], 

952 ] 

953 ] 

954 

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

956 

957 local_table: FromClause 

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

959 :class:`_orm.Mapper` refers. 

960 

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

962 :class:`.FromClause`. 

963 

964 The "local" table is the 

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

966 managing from an attribute access and flush perspective. For 

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

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

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

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

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

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

973 

974 .. seealso:: 

975 

976 :attr:`_orm.Mapper.persist_selectable`. 

977 

978 :attr:`_orm.Mapper.selectable`. 

979 

980 """ 

981 

982 persist_selectable: FromClause 

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

984 is mapped. 

985 

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

987 :class:`.FromClause`. 

988 

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

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

991 represents the inheriting class hierarchy overall in an inheritance 

992 scenario. 

993 

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

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

996 alternate subquery used for selecting columns. 

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

998 will be written on a persist operation. 

999 

1000 .. seealso:: 

1001 

1002 :attr:`_orm.Mapper.selectable`. 

1003 

1004 :attr:`_orm.Mapper.local_table`. 

1005 

1006 """ 

1007 

1008 inherits: Optional[Mapper[Any]] 

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

1010 inherits from, if any. 

1011 

1012 """ 

1013 

1014 inherit_condition: Optional[ColumnElement[bool]] 

1015 

1016 configured: bool = False 

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

1018 

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

1020 Behavior is undefined if directly modified. 

1021 

1022 .. seealso:: 

1023 

1024 :func:`.configure_mappers`. 

1025 

1026 """ 

1027 

1028 concrete: bool 

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

1030 inheritance mapper. 

1031 

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

1033 Behavior is undefined if directly modified. 

1034 

1035 """ 

1036 

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

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

1039 objects 

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

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

1042 

1043 This list is against the selectable in 

1044 :attr:`_orm.Mapper.persist_selectable`. 

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

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

1047 :class:`_expression.Join`, the 

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

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

1050 

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

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

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

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

1055 

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

1057 Behavior is undefined if directly modified. 

1058 

1059 """ 

1060 

1061 class_manager: ClassManager[_O] 

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

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

1064 

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

1066 Behavior is undefined if directly modified. 

1067 

1068 """ 

1069 

1070 single: bool 

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

1072 inheritance mapper. 

1073 

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

1075 

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

1077 Behavior is undefined if directly modified. 

1078 

1079 """ 

1080 

1081 polymorphic_on: Optional[KeyedColumnElement[Any]] 

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

1083 ``polymorphic_on`` argument 

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

1085 

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

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

1088 :func:`.cast`. 

1089 

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

1091 Behavior is undefined if directly modified. 

1092 

1093 """ 

1094 

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

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

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

1098 

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

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

1101 

1102 An inheritance chain of mappers will all reference the same 

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

1104 result rows to target mappers. 

1105 

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

1107 Behavior is undefined if directly modified. 

1108 

1109 """ 

1110 

1111 polymorphic_identity: Optional[Any] 

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

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

1114 

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

1116 comparable to the type of column represented by 

1117 :attr:`_orm.Mapper.polymorphic_on`. 

1118 

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

1120 Behavior is undefined if directly modified. 

1121 

1122 """ 

1123 

1124 base_mapper: Mapper[Any] 

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

1126 

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

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

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

1130 objects in the inheritance chain. 

1131 

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

1133 Behavior is undefined if directly modified. 

1134 

1135 """ 

1136 

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

1138 

1139 @util.memoized_property 

1140 def _path_registry(self) -> _CachingEntityRegistry: 

1141 return PathRegistry.per_mapper(self) 

1142 

1143 def _configure_inheritance(self): 

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

1145 being present.""" 

1146 

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

1148 self._inheriting_mappers = util.WeakSequence() 

1149 

1150 if self.inherits: 

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

1152 raise sa_exc.ArgumentError( 

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

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

1155 ) 

1156 

1157 self.dispatch._update(self.inherits.dispatch) 

1158 

1159 if self.single: 

1160 self.persist_selectable = self.inherits.persist_selectable 

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

1162 if self.concrete: 

1163 self.persist_selectable = self.local_table 

1164 for mapper in self.iterate_to_root(): 

1165 if mapper.polymorphic_on is not None: 

1166 mapper._requires_row_aliasing = True 

1167 else: 

1168 if self.inherit_condition is None: 

1169 # figure out inherit condition from our table to the 

1170 # immediate table of the inherited mapper, not its 

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

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

1173 try: 

1174 self.inherit_condition = sql_util.join_condition( 

1175 self.inherits.local_table, self.local_table 

1176 ) 

1177 except sa_exc.NoForeignKeysError as nfe: 

1178 assert self.inherits.local_table is not None 

1179 assert self.local_table is not None 

1180 raise sa_exc.NoForeignKeysError( 

1181 "Can't determine the inherit condition " 

1182 "between inherited table '%s' and " 

1183 "inheriting " 

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

1185 "foreign key relationships established. " 

1186 "Please ensure the inheriting table has " 

1187 "a foreign key relationship to the " 

1188 "inherited " 

1189 "table, or provide an " 

1190 "'on clause' using " 

1191 "the 'inherit_condition' mapper argument." 

1192 % ( 

1193 self.inherits.local_table.description, 

1194 self.local_table.description, 

1195 ) 

1196 ) from nfe 

1197 except sa_exc.AmbiguousForeignKeysError as afe: 

1198 assert self.inherits.local_table is not None 

1199 assert self.local_table is not None 

1200 raise sa_exc.AmbiguousForeignKeysError( 

1201 "Can't determine the inherit condition " 

1202 "between inherited table '%s' and " 

1203 "inheriting " 

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

1205 "foreign key relationship established. " 

1206 "Please specify the 'on clause' using " 

1207 "the 'inherit_condition' mapper argument." 

1208 % ( 

1209 self.inherits.local_table.description, 

1210 self.local_table.description, 

1211 ) 

1212 ) from afe 

1213 assert self.inherits.persist_selectable is not None 

1214 self.persist_selectable = sql.join( 

1215 self.inherits.persist_selectable, 

1216 self.local_table, 

1217 self.inherit_condition, 

1218 ) 

1219 

1220 fks = util.to_set(self.inherit_foreign_keys) 

1221 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 

1222 self.persist_selectable.onclause, 

1223 consider_as_foreign_keys=fks, 

1224 ) 

1225 else: 

1226 self.persist_selectable = self.local_table 

1227 

1228 if self.polymorphic_identity is None: 

1229 self._identity_class = self.class_ 

1230 

1231 if ( 

1232 not self.polymorphic_abstract 

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

1234 ): 

1235 util.warn( 

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

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

1238 f"'polymorphic_on' column of " 

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

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

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

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

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

1244 "class unmapped when using Declarative, set the " 

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

1246 ) 

1247 elif self.concrete: 

1248 self._identity_class = self.class_ 

1249 else: 

1250 self._identity_class = self.inherits._identity_class 

1251 

1252 if self.version_id_col is None: 

1253 self.version_id_col = self.inherits.version_id_col 

1254 self.version_id_generator = self.inherits.version_id_generator 

1255 elif ( 

1256 self.inherits.version_id_col is not None 

1257 and self.version_id_col is not self.inherits.version_id_col 

1258 ): 

1259 util.warn( 

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

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

1262 "the inherited versioning column. " 

1263 "version_id_col should only be specified on " 

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

1265 % ( 

1266 self.version_id_col.description, 

1267 self.inherits.version_id_col.description, 

1268 ) 

1269 ) 

1270 

1271 self.polymorphic_map = self.inherits.polymorphic_map 

1272 self.batch = self.inherits.batch 

1273 self.inherits._inheriting_mappers.append(self) 

1274 self.base_mapper = self.inherits.base_mapper 

1275 self.passive_updates = self.inherits.passive_updates 

1276 self.passive_deletes = ( 

1277 self.inherits.passive_deletes or self.passive_deletes 

1278 ) 

1279 self._all_tables = self.inherits._all_tables 

1280 

1281 if self.polymorphic_identity is not None: 

1282 if self.polymorphic_identity in self.polymorphic_map: 

1283 util.warn( 

1284 "Reassigning polymorphic association for identity %r " 

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

1286 "value for polymorphic_identity." 

1287 % ( 

1288 self.polymorphic_identity, 

1289 self.polymorphic_map[self.polymorphic_identity], 

1290 self, 

1291 self.polymorphic_identity, 

1292 ) 

1293 ) 

1294 self.polymorphic_map[self.polymorphic_identity] = self 

1295 

1296 if self.polymorphic_load and self.concrete: 

1297 raise sa_exc.ArgumentError( 

1298 "polymorphic_load is not currently supported " 

1299 "with concrete table inheritance" 

1300 ) 

1301 if self.polymorphic_load == "inline": 

1302 self.inherits._add_with_polymorphic_subclass(self) 

1303 elif self.polymorphic_load == "selectin": 

1304 pass 

1305 elif self.polymorphic_load is not None: 

1306 raise sa_exc.ArgumentError( 

1307 "unknown argument for polymorphic_load: %r" 

1308 % self.polymorphic_load 

1309 ) 

1310 

1311 else: 

1312 self._all_tables = set() 

1313 self.base_mapper = self 

1314 assert self.local_table is not None 

1315 self.persist_selectable = self.local_table 

1316 if self.polymorphic_identity is not None: 

1317 self.polymorphic_map[self.polymorphic_identity] = self 

1318 self._identity_class = self.class_ 

1319 

1320 if self.persist_selectable is None: 

1321 raise sa_exc.ArgumentError( 

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

1323 % self 

1324 ) 

1325 

1326 def _set_with_polymorphic( 

1327 self, with_polymorphic: Optional[_WithPolymorphicArg] 

1328 ) -> None: 

1329 if with_polymorphic == "*": 

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

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

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

1333 self.with_polymorphic = cast( 

1334 """Tuple[ 

1335 Union[ 

1336 Literal["*"], 

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

1338 ], 

1339 Optional["FromClause"], 

1340 ]""", 

1341 with_polymorphic, 

1342 ) 

1343 else: 

1344 self.with_polymorphic = (with_polymorphic, None) 

1345 elif with_polymorphic is not None: 

1346 raise sa_exc.ArgumentError( 

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

1348 ) 

1349 else: 

1350 self.with_polymorphic = None 

1351 

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

1353 self.with_polymorphic = ( 

1354 self.with_polymorphic[0], 

1355 coercions.expect( 

1356 roles.FromClauseRole, 

1357 self.with_polymorphic[1], 

1358 ), 

1359 ) 

1360 

1361 if self.configured: 

1362 self._expire_memoizations() 

1363 

1364 def _add_with_polymorphic_subclass(self, mapper): 

1365 subcl = mapper.class_ 

1366 if self.with_polymorphic is None: 

1367 self._set_with_polymorphic((subcl,)) 

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

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

1370 self._set_with_polymorphic( 

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

1372 ) 

1373 

1374 def _set_concrete_base(self, mapper): 

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

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

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

1378 

1379 assert self.concrete 

1380 assert not self.inherits 

1381 assert isinstance(mapper, Mapper) 

1382 self.inherits = mapper 

1383 self.inherits.polymorphic_map.update(self.polymorphic_map) 

1384 self.polymorphic_map = self.inherits.polymorphic_map 

1385 for mapper in self.iterate_to_root(): 

1386 if mapper.polymorphic_on is not None: 

1387 mapper._requires_row_aliasing = True 

1388 self.batch = self.inherits.batch 

1389 for mp in self.self_and_descendants: 

1390 mp.base_mapper = self.inherits.base_mapper 

1391 self.inherits._inheriting_mappers.append(self) 

1392 self.passive_updates = self.inherits.passive_updates 

1393 self._all_tables = self.inherits._all_tables 

1394 

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

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

1397 key, key, local=False, column=None 

1398 ): 

1399 self._adapt_inherited_property(key, prop, False) 

1400 

1401 def _set_polymorphic_on(self, polymorphic_on): 

1402 self.polymorphic_on = polymorphic_on 

1403 self._configure_polymorphic_setter(True) 

1404 

1405 def _configure_class_instrumentation(self): 

1406 """Associate this Mapper with the 

1407 given class and entity name. 

1408 

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

1410 name combination will return this mapper. Also decorate the 

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

1412 auto-session attachment logic. 

1413 

1414 """ 

1415 

1416 # we expect that declarative has applied the class manager 

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

1418 # this raises as of 2.0. 

1419 manager = attributes.opt_manager_of_class(self.class_) 

1420 

1421 if manager is None or not manager.registry: 

1422 raise sa_exc.InvalidRequestError( 

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

1424 "invoked directly outside of a declarative registry." 

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

1426 "function for a classical mapping." 

1427 ) 

1428 

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

1430 

1431 # this invokes the class_instrument event and sets up 

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

1433 # occur after the instrument_class event above. 

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

1435 # :( 

1436 

1437 manager = instrumentation.register_class( 

1438 self.class_, 

1439 mapper=self, 

1440 expired_attribute_loader=util.partial( 

1441 loading._load_scalar_attributes, self 

1442 ), 

1443 # finalize flag means instrument the __init__ method 

1444 # and call the class_instrument event 

1445 finalize=True, 

1446 ) 

1447 

1448 self.class_manager = manager 

1449 

1450 assert manager.registry is not None 

1451 self.registry = manager.registry 

1452 

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

1454 # e_name None or not. 

1455 if manager.mapper is None: 

1456 return 

1457 

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

1459 

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

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

1462 method = method._sa_original_init 

1463 if hasattr(method, "__func__"): 

1464 method = method.__func__ 

1465 if callable(method): 

1466 if hasattr(method, "__sa_reconstructor__"): 

1467 self._reconstructor = method 

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

1469 elif hasattr(method, "__sa_validators__"): 

1470 validation_opts = method.__sa_validation_opts__ 

1471 for name in method.__sa_validators__: 

1472 if name in self.validators: 

1473 raise sa_exc.InvalidRequestError( 

1474 "A validation function for mapped " 

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

1476 % (name, self) 

1477 ) 

1478 self.validators = self.validators.union( 

1479 {name: (method, validation_opts)} 

1480 ) 

1481 

1482 def _set_dispose_flags(self) -> None: 

1483 self.configured = True 

1484 self._ready_for_configure = True 

1485 self._dispose_called = True 

1486 

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

1488 

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

1490 try: 

1491 prop = self._props[key] 

1492 except KeyError as err: 

1493 raise sa_exc.ArgumentError( 

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

1495 "no attribute is mapped to this name." 

1496 ) from err 

1497 try: 

1498 expr = prop.expression 

1499 except AttributeError as ae: 

1500 raise sa_exc.ArgumentError( 

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

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

1503 ) from ae 

1504 if not isinstance(expr, Column): 

1505 raise sa_exc.ArgumentError( 

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

1507 "property does not refer to a single " 

1508 "mapped Column" 

1509 ) 

1510 return expr 

1511 

1512 def _configure_pks(self) -> None: 

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

1514 

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

1516 

1517 self._pks_by_table = {} 

1518 self._cols_by_table = {} 

1519 

1520 all_cols = util.column_set( 

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

1522 ) 

1523 

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

1525 

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

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

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

1529 # ordering is important since it determines the ordering of 

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

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

1532 fc.primary_key 

1533 ).intersection( 

1534 pk_cols 

1535 ) 

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

1537 all_cols 

1538 ) 

1539 

1540 if self._primary_key_argument: 

1541 coerced_pk_arg = [ 

1542 ( 

1543 self._str_arg_to_mapped_col("primary_key", c) 

1544 if isinstance(c, str) 

1545 else c 

1546 ) 

1547 for c in ( 

1548 coercions.expect( 

1549 roles.DDLConstraintColumnRole, 

1550 coerce_pk, 

1551 argname="primary_key", 

1552 ) 

1553 for coerce_pk in self._primary_key_argument 

1554 ) 

1555 ] 

1556 else: 

1557 coerced_pk_arg = None 

1558 

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

1560 # primary key mappings 

1561 if coerced_pk_arg: 

1562 for k in coerced_pk_arg: 

1563 if k.table not in self._pks_by_table: 

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

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

1566 

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

1568 elif ( 

1569 self.persist_selectable not in self._pks_by_table 

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

1571 ): 

1572 raise sa_exc.ArgumentError( 

1573 "Mapper %s could not assemble any primary " 

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

1575 % (self, self.persist_selectable.description) 

1576 ) 

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

1578 self.local_table, schema.Table 

1579 ): 

1580 util.warn( 

1581 "Could not assemble any primary " 

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

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

1584 % self.local_table.description 

1585 ) 

1586 

1587 if ( 

1588 self.inherits 

1589 and not self.concrete 

1590 and not self._primary_key_argument 

1591 ): 

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

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

1594 self.primary_key = self.inherits.primary_key 

1595 else: 

1596 # determine primary key from argument or persist_selectable pks 

1597 primary_key: Collection[ColumnElement[Any]] 

1598 

1599 if coerced_pk_arg: 

1600 primary_key = [ 

1601 cc if cc is not None else c 

1602 for cc, c in ( 

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

1604 for c in coerced_pk_arg 

1605 ) 

1606 ] 

1607 else: 

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

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

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

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

1612 primary_key = sql_util.reduce_columns( 

1613 self._pks_by_table[self.persist_selectable], 

1614 ignore_nonexistent_tables=True, 

1615 ) 

1616 

1617 if len(primary_key) == 0: 

1618 raise sa_exc.ArgumentError( 

1619 "Mapper %s could not assemble any primary " 

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

1621 % (self, self.persist_selectable.description) 

1622 ) 

1623 

1624 self.primary_key = tuple(primary_key) 

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

1626 

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

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

1629 self._readonly_props = { 

1630 self._columntoproperty[col] 

1631 for col in self._columntoproperty 

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

1633 and ( 

1634 not hasattr(col, "table") 

1635 or col.table not in self._cols_by_table 

1636 ) 

1637 } 

1638 

1639 def _configure_properties(self) -> None: 

1640 self._columns = sql_base.WriteableColumnCollection() 

1641 

1642 # object attribute names mapped to MapperProperty objects 

1643 self._props = util.OrderedDict() 

1644 

1645 # table columns mapped to MapperProperty 

1646 self._columntoproperty = _ColumnMapping(self) 

1647 

1648 explicit_col_props_by_column: Dict[ 

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

1650 ] = {} 

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

1652 

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

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

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

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

1657 if self._init_properties: 

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

1659 if not isinstance(prop_arg, MapperProperty): 

1660 possible_col_prop = self._make_prop_from_column( 

1661 key, prop_arg 

1662 ) 

1663 else: 

1664 possible_col_prop = prop_arg 

1665 

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

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

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

1669 # the Table. 

1670 

1671 _map_as_property_now = True 

1672 if isinstance(possible_col_prop, properties.ColumnProperty): 

1673 for given_col in possible_col_prop.columns: 

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

1675 _map_as_property_now = False 

1676 explicit_col_props_by_key[key] = possible_col_prop 

1677 explicit_col_props_by_column[given_col] = ( 

1678 key, 

1679 possible_col_prop, 

1680 ) 

1681 

1682 if _map_as_property_now: 

1683 self._configure_property( 

1684 key, 

1685 possible_col_prop, 

1686 init=False, 

1687 ) 

1688 

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

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

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

1692 if self.inherits: 

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

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

1695 continue 

1696 

1697 incoming_prop = explicit_col_props_by_key.get(key) 

1698 if incoming_prop: 

1699 new_prop = self._reconcile_prop_with_incoming_columns( 

1700 key, 

1701 inherited_prop, 

1702 warn_only=False, 

1703 incoming_prop=incoming_prop, 

1704 ) 

1705 explicit_col_props_by_key[key] = new_prop 

1706 

1707 for inc_col in incoming_prop.columns: 

1708 explicit_col_props_by_column[inc_col] = ( 

1709 key, 

1710 new_prop, 

1711 ) 

1712 elif key not in self._props: 

1713 self._adapt_inherited_property(key, inherited_prop, False) 

1714 

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

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

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

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

1719 # reconciliation against inherited columns occurs here also. 

1720 

1721 for column in self.persist_selectable.columns: 

1722 if column in explicit_col_props_by_column: 

1723 # column was explicitly passed to properties; configure 

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

1725 # Table / selectable 

1726 key, prop = explicit_col_props_by_column[column] 

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

1728 continue 

1729 

1730 elif column in self._columntoproperty: 

1731 continue 

1732 

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

1734 if self._should_exclude( 

1735 column.key, 

1736 column_key, 

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

1738 column=column, 

1739 ): 

1740 continue 

1741 

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

1743 # of the inheriting mapper 

1744 for mapper in self.iterate_to_root(): 

1745 if column in mapper._columntoproperty: 

1746 column_key = mapper._columntoproperty[column].key 

1747 

1748 self._configure_property( 

1749 column_key, 

1750 column, 

1751 init=False, 

1752 setparent=True, 

1753 ) 

1754 

1755 def _configure_polymorphic_setter(self, init=False): 

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

1757 'polymorphic_on' column, if applicable, and not 

1758 already generated by _configure_properties (which is typical). 

1759 

1760 Also create a setter function which will assign this 

1761 attribute to the value of the 'polymorphic_identity' 

1762 upon instance construction, also if applicable. This 

1763 routine will run when an instance is created. 

1764 

1765 """ 

1766 setter = False 

1767 polymorphic_key: Optional[str] = None 

1768 

1769 if self.polymorphic_on is not None: 

1770 setter = True 

1771 

1772 if isinstance(self.polymorphic_on, str): 

1773 # polymorphic_on specified as a string - link 

1774 # it to mapped ColumnProperty 

1775 try: 

1776 self.polymorphic_on = self._props[self.polymorphic_on] 

1777 except KeyError as err: 

1778 raise sa_exc.ArgumentError( 

1779 "Can't determine polymorphic_on " 

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

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

1782 ) from err 

1783 

1784 if self.polymorphic_on in self._columntoproperty: 

1785 # polymorphic_on is a column that is already mapped 

1786 # to a ColumnProperty 

1787 prop = self._columntoproperty[self.polymorphic_on] 

1788 elif isinstance(self.polymorphic_on, MapperProperty): 

1789 # polymorphic_on is directly a MapperProperty, 

1790 # ensure it's a ColumnProperty 

1791 if not isinstance( 

1792 self.polymorphic_on, properties.ColumnProperty 

1793 ): 

1794 raise sa_exc.ArgumentError( 

1795 "Only direct column-mapped " 

1796 "property or SQL expression " 

1797 "can be passed for polymorphic_on" 

1798 ) 

1799 prop = self.polymorphic_on 

1800 else: 

1801 # polymorphic_on is a Column or SQL expression and 

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

1803 # only present in the with_polymorphic selectable or 

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

1805 # hope is compatible with this mapper's persist_selectable 

1806 col = self.persist_selectable.corresponding_column( 

1807 self.polymorphic_on 

1808 ) 

1809 if col is None: 

1810 # polymorphic_on doesn't derive from any 

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

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

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

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

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

1816 # represented somehow in either persist_selectable or 

1817 # with_polymorphic. Otherwise as of 0.7.4 we 

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

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

1820 setter = False 

1821 instrument = False 

1822 col = self.polymorphic_on 

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

1824 self.with_polymorphic is None 

1825 or self.with_polymorphic[1] is None 

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

1827 is None 

1828 ): 

1829 raise sa_exc.InvalidRequestError( 

1830 "Could not map polymorphic_on column " 

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

1832 "loads will not function properly" 

1833 % col.description 

1834 ) 

1835 else: 

1836 # column/expression that polymorphic_on derives from 

1837 # is present in our mapped table 

1838 # and is probably mapped, but polymorphic_on itself 

1839 # is not. This happens when 

1840 # the polymorphic_on is only directly present in the 

1841 # with_polymorphic selectable, as when use 

1842 # polymorphic_union. 

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

1844 instrument = True 

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

1846 if key: 

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

1848 raise sa_exc.InvalidRequestError( 

1849 "Cannot exclude or override the " 

1850 "discriminator column %r" % key 

1851 ) 

1852 else: 

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

1854 key = col.key 

1855 

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

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

1858 

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

1860 # column in the property 

1861 self.polymorphic_on = prop.columns[0] 

1862 polymorphic_key = prop.key 

1863 else: 

1864 # no polymorphic_on was set. 

1865 # check inheriting mappers for one. 

1866 for mapper in self.iterate_to_root(): 

1867 # determine if polymorphic_on of the parent 

1868 # should be propagated here. If the col 

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

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

1871 # inheritance), we can use it 

1872 if mapper.polymorphic_on is not None: 

1873 if self.persist_selectable is mapper.persist_selectable: 

1874 self.polymorphic_on = mapper.polymorphic_on 

1875 else: 

1876 self.polymorphic_on = ( 

1877 self.persist_selectable 

1878 ).corresponding_column(mapper.polymorphic_on) 

1879 # we can use the parent mapper's _set_polymorphic_identity 

1880 # directly; it ensures the polymorphic_identity of the 

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

1882 if self.polymorphic_on is not None: 

1883 self._set_polymorphic_identity = ( 

1884 mapper._set_polymorphic_identity 

1885 ) 

1886 self._polymorphic_attr_key = ( 

1887 mapper._polymorphic_attr_key 

1888 ) 

1889 self._validate_polymorphic_identity = ( 

1890 mapper._validate_polymorphic_identity 

1891 ) 

1892 else: 

1893 self._set_polymorphic_identity = None 

1894 self._polymorphic_attr_key = None 

1895 return 

1896 

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

1898 raise sa_exc.InvalidRequestError( 

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

1900 "on a mapper hierarchy which includes the " 

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

1902 ) 

1903 

1904 if setter: 

1905 

1906 def _set_polymorphic_identity(state): 

1907 dict_ = state.dict 

1908 # TODO: what happens if polymorphic_on column attribute name 

1909 # does not match .key? 

1910 

1911 polymorphic_identity = ( 

1912 state.manager.mapper.polymorphic_identity 

1913 ) 

1914 if ( 

1915 polymorphic_identity is None 

1916 and state.manager.mapper.polymorphic_abstract 

1917 ): 

1918 raise sa_exc.InvalidRequestError( 

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

1920 "mapper is marked polymorphic_abstract=True" 

1921 ) 

1922 

1923 state.get_impl(polymorphic_key).set( 

1924 state, 

1925 dict_, 

1926 polymorphic_identity, 

1927 None, 

1928 ) 

1929 

1930 self._polymorphic_attr_key = polymorphic_key 

1931 

1932 def _validate_polymorphic_identity(mapper, state, dict_): 

1933 if ( 

1934 polymorphic_key in dict_ 

1935 and dict_[polymorphic_key] 

1936 not in mapper._acceptable_polymorphic_identities 

1937 ): 

1938 util.warn_limited( 

1939 "Flushing object %s with " 

1940 "incompatible polymorphic identity %r; the " 

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

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

1943 ) 

1944 

1945 self._set_polymorphic_identity = _set_polymorphic_identity 

1946 self._validate_polymorphic_identity = ( 

1947 _validate_polymorphic_identity 

1948 ) 

1949 else: 

1950 self._polymorphic_attr_key = None 

1951 self._set_polymorphic_identity = None 

1952 

1953 _validate_polymorphic_identity = None 

1954 

1955 @HasMemoized.memoized_attribute 

1956 def _local_pk_cols(self) -> set[Any]: 

1957 pk_cols = util.column_set(self.primary_key) 

1958 equiv = self._equivalent_columns 

1959 for pk_col in pk_cols.intersection(equiv): 

1960 pk_cols.update(equiv[pk_col]) 

1961 return util.column_set( 

1962 col for col in pk_cols if col.table is self.local_table 

1963 ) 

1964 

1965 @HasMemoized.memoized_attribute 

1966 def _version_id_prop(self): 

1967 if self.version_id_col is not None: 

1968 return self._columntoproperty[self.version_id_col] 

1969 else: 

1970 return None 

1971 

1972 @HasMemoized.memoized_attribute 

1973 def _acceptable_polymorphic_identities(self): 

1974 identities = set() 

1975 

1976 stack = deque([self]) 

1977 while stack: 

1978 item = stack.popleft() 

1979 if item.persist_selectable is self.persist_selectable: 

1980 identities.add(item.polymorphic_identity) 

1981 stack.extend(item._inheriting_mappers) 

1982 

1983 return identities 

1984 

1985 @HasMemoized.memoized_attribute 

1986 def _prop_set(self): 

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

1988 

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

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

1991 descriptor_props = util.preloaded.orm_descriptor_props 

1992 

1993 if not self.concrete: 

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

1995 elif key not in self._props: 

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

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

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

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

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

2001 # descriptors that might have side effects when invoked. 

2002 implementing_attribute = self.class_manager._get_class_attr_mro( 

2003 key, prop 

2004 ) 

2005 if implementing_attribute is prop or ( 

2006 isinstance( 

2007 implementing_attribute, attributes.InstrumentedAttribute 

2008 ) 

2009 and implementing_attribute._parententity is prop.parent 

2010 ): 

2011 self._configure_property( 

2012 key, 

2013 descriptor_props.ConcreteInheritedProperty(), 

2014 init=init, 

2015 setparent=True, 

2016 ) 

2017 

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

2019 def _configure_property( 

2020 self, 

2021 key: str, 

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

2023 *, 

2024 init: bool = True, 

2025 setparent: bool = True, 

2026 warn_for_existing: bool = False, 

2027 ) -> MapperProperty[Any]: 

2028 descriptor_props = util.preloaded.orm_descriptor_props 

2029 self._log( 

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

2031 ) 

2032 

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

2034 # ensure a Column is turned into a ColumnProperty. 

2035 # see #12858 

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

2037 

2038 if not isinstance(prop_arg, MapperProperty): 

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

2040 key, prop_arg, early_setup 

2041 ) 

2042 else: 

2043 prop = prop_arg 

2044 

2045 if early_setup: 

2046 return prop 

2047 

2048 if isinstance(prop, properties.ColumnProperty): 

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

2050 

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

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

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

2054 if col is None and self.inherits: 

2055 path = [self] 

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

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

2058 if col is not None: 

2059 for m2 in path: 

2060 m2.persist_selectable._refresh_for_new_column(col) 

2061 col = self.persist_selectable.corresponding_column( 

2062 prop.columns[0] 

2063 ) 

2064 break 

2065 path.append(m) 

2066 

2067 # subquery expression, column not present in the mapped 

2068 # selectable. 

2069 if col is None: 

2070 col = prop.columns[0] 

2071 

2072 # column is coming in after _readonly_props was 

2073 # initialized; check for 'readonly' 

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

2075 not hasattr(col, "table") 

2076 or col.table not in self._cols_by_table 

2077 ): 

2078 self._readonly_props.add(prop) 

2079 

2080 else: 

2081 # if column is coming in after _cols_by_table was 

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

2083 if ( 

2084 hasattr(self, "_cols_by_table") 

2085 and col.table in self._cols_by_table 

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

2087 ): 

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

2089 

2090 # if this properties.ColumnProperty represents the "polymorphic 

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

2092 # columns in SELECT statements. 

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

2094 prop._is_polymorphic_discriminator = ( 

2095 col is self.polymorphic_on 

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

2097 ) 

2098 

2099 if isinstance(col, expression.Label): 

2100 # new in 1.4, get column property against expressions 

2101 # to be addressable in subqueries 

2102 col.key = col._tq_key_label = key 

2103 

2104 self._columns.add(col, key) 

2105 

2106 for col in prop.columns: 

2107 for proxy_col in col.proxy_set: 

2108 self._columntoproperty[proxy_col] = prop 

2109 

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

2111 util.warn( 

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

2113 "assigned to attribute " 

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

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

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

2117 ) 

2118 

2119 prop.key = key 

2120 

2121 if setparent: 

2122 prop.set_parent(self, init) 

2123 

2124 if key in self._props and getattr( 

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

2126 ): 

2127 syn = self._props[key]._mapped_by_synonym 

2128 raise sa_exc.ArgumentError( 

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

2130 "a ColumnProperty already exists keyed to the name " 

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

2132 ) 

2133 

2134 # replacement cases 

2135 

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

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

2138 if ( 

2139 key in self._props 

2140 and not isinstance( 

2141 self._props[key], descriptor_props.ConcreteInheritedProperty 

2142 ) 

2143 and not isinstance(prop, descriptor_props.SynonymProperty) 

2144 ): 

2145 if warn_for_existing: 

2146 util.warn_deprecated( 

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

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

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

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

2151 "release", 

2152 "2.0", 

2153 ) 

2154 oldprop = self._props[key] 

2155 self._path_registry.pop(oldprop, None) 

2156 

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

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

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

2160 # get replaced. 

2161 elif ( 

2162 warn_for_existing 

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

2164 and not isinstance(prop, descriptor_props.SynonymProperty) 

2165 and not isinstance( 

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

2167 descriptor_props.ConcreteInheritedProperty, 

2168 ) 

2169 ): 

2170 util.warn_deprecated( 

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

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

2173 "attribute of the same name. " 

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

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

2176 "release", 

2177 "2.0", 

2178 ) 

2179 

2180 self._props[key] = prop 

2181 

2182 prop.instrument_class(self) 

2183 

2184 for mapper in self._inheriting_mappers: 

2185 mapper._adapt_inherited_property(key, prop, init) 

2186 

2187 if init: 

2188 prop.init() 

2189 prop.post_instrument_class(self) 

2190 

2191 if self.configured: 

2192 self._expire_memoizations() 

2193 

2194 return prop 

2195 

2196 def _make_prop_from_column( 

2197 self, 

2198 key: str, 

2199 column: Union[ 

2200 Sequence[KeyedColumnElement[Any]], KeyedColumnElement[Any] 

2201 ], 

2202 ) -> ColumnProperty[Any]: 

2203 columns = util.to_list(column) 

2204 mapped_column = [] 

2205 for c in columns: 

2206 mc = self.persist_selectable.corresponding_column(c) 

2207 if mc is None: 

2208 mc = self.local_table.corresponding_column(c) 

2209 if mc is not None: 

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

2211 # mapped table, this corresponds to adding a 

2212 # column after the fact to the local table. 

2213 # [ticket:1523] 

2214 self.persist_selectable._refresh_for_new_column(mc) 

2215 mc = self.persist_selectable.corresponding_column(c) 

2216 if mc is None: 

2217 raise sa_exc.ArgumentError( 

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

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

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

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

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

2223 ) 

2224 mapped_column.append(mc) 

2225 return properties.ColumnProperty(*mapped_column) 

2226 

2227 def _reconcile_prop_with_incoming_columns( 

2228 self, 

2229 key: str, 

2230 existing_prop: MapperProperty[Any], 

2231 warn_only: bool, 

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

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

2234 ) -> ColumnProperty[Any]: 

2235 if incoming_prop and ( 

2236 self.concrete 

2237 or not isinstance(existing_prop, properties.ColumnProperty) 

2238 ): 

2239 return incoming_prop 

2240 

2241 existing_column = existing_prop.columns[0] 

2242 

2243 if incoming_prop and existing_column in incoming_prop.columns: 

2244 return incoming_prop 

2245 

2246 if incoming_prop is None: 

2247 assert single_column is not None 

2248 incoming_column = single_column 

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

2250 else: 

2251 assert single_column is None 

2252 incoming_column = incoming_prop.columns[0] 

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

2254 

2255 if ( 

2256 ( 

2257 not self._inherits_equated_pairs 

2258 or (equated_pair_key not in self._inherits_equated_pairs) 

2259 ) 

2260 and not existing_column.shares_lineage(incoming_column) 

2261 and existing_column is not self.version_id_col 

2262 and incoming_column is not self.version_id_col 

2263 ): 

2264 msg = ( 

2265 "Implicitly combining column %s with column " 

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

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

2268 "explicitly." 

2269 % ( 

2270 existing_prop.columns[-1], 

2271 incoming_column, 

2272 key, 

2273 ) 

2274 ) 

2275 if warn_only: 

2276 util.warn(msg) 

2277 else: 

2278 raise sa_exc.InvalidRequestError(msg) 

2279 

2280 # existing properties.ColumnProperty from an inheriting 

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

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[no-any-return] 

2291 

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

2293 def _property_from_column( 

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

2295 ) -> ColumnProperty[Any]: 

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

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

2298 

2299 descriptor_props = util.preloaded.orm_descriptor_props 

2300 

2301 if early_setup: 

2302 prop = None 

2303 else: 

2304 prop = self._props.get(key) 

2305 

2306 if isinstance(prop, properties.ColumnProperty): 

2307 return self._reconcile_prop_with_incoming_columns( 

2308 key, 

2309 prop, 

2310 single_column=column, 

2311 warn_only=prop.parent is not self, 

2312 ) 

2313 elif prop is None or isinstance( 

2314 prop, descriptor_props.ConcreteInheritedProperty 

2315 ): 

2316 return self._make_prop_from_column(key, column) 

2317 else: 

2318 raise sa_exc.ArgumentError( 

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

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

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

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

2323 "to remove all awareness of the column entirely " 

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

2325 "use the 'include_properties' or 'exclude_properties' " 

2326 "mapper arguments to control specifically which table " 

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

2328 ) 

2329 

2330 @util.langhelpers.tag_method_for_warnings( 

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

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

2333 "operation.", 

2334 sa_exc.SAWarning, 

2335 ) 

2336 def _check_configure(self) -> None: 

2337 if self.registry._new_mappers: 

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

2339 

2340 def _post_configure_properties(self) -> None: 

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

2342 attached to this mapper. 

2343 

2344 This is a deferred configuration step which is intended 

2345 to execute once all mappers have been constructed. 

2346 

2347 """ 

2348 

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

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

2351 for key, prop in l: 

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

2353 

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

2355 prop.init() 

2356 

2357 if prop._configure_finished: 

2358 prop.post_instrument_class(self) 

2359 

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

2361 self.configured = True 

2362 

2363 def add_properties(self, dict_of_properties): 

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

2365 using `add_property`. 

2366 

2367 """ 

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

2369 self.add_property(key, value) 

2370 

2371 def add_property( 

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

2373 ) -> None: 

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

2375 

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

2377 property to the initial properties dictionary sent to the 

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

2379 the given MapperProperty is configured immediately. 

2380 

2381 """ 

2382 prop = self._configure_property( 

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

2384 ) 

2385 assert isinstance(prop, MapperProperty) 

2386 self._init_properties[key] = prop 

2387 

2388 def _expire_memoizations(self) -> None: 

2389 for mapper in self.iterate_to_root(): 

2390 mapper._reset_memoizations() 

2391 

2392 @property 

2393 def _log_desc(self) -> str: 

2394 return ( 

2395 "(" 

2396 + self.class_.__name__ 

2397 + "|" 

2398 + ( 

2399 self.local_table is not None 

2400 and self.local_table.description 

2401 or str(self.local_table) 

2402 ) 

2403 + ")" 

2404 ) 

2405 

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

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

2408 

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

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

2411 

2412 def __repr__(self) -> str: 

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

2414 

2415 def __str__(self) -> str: 

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

2417 self.class_.__name__, 

2418 ( 

2419 self.local_table.description 

2420 if self.local_table is not None 

2421 else self.persist_selectable.description 

2422 ), 

2423 ) 

2424 

2425 def path_string(self) -> str: 

2426 """Return a user-facing name for this :class:`_orm.Mapper`, 

2427 for use in a :class:`_orm.PathRegistry` string representation. 

2428 

2429 """ 

2430 return self.class_.__name__ 

2431 

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

2433 orphan_possible = False 

2434 for mapper in self.iterate_to_root(): 

2435 for key, cls in mapper._delete_orphans: 

2436 orphan_possible = True 

2437 

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

2439 state, key, optimistic=state.has_identity 

2440 ) 

2441 

2442 if self.legacy_is_orphan and has_parent: 

2443 return False 

2444 elif not self.legacy_is_orphan and not has_parent: 

2445 return True 

2446 

2447 if self.legacy_is_orphan: 

2448 return orphan_possible 

2449 else: 

2450 return False 

2451 

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

2453 return key in self._props 

2454 

2455 def get_property( 

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

2457 ) -> MapperProperty[Any]: 

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

2459 

2460 if _configure_mappers: 

2461 self._check_configure() 

2462 

2463 try: 

2464 return self._props[key] 

2465 except KeyError as err: 

2466 raise sa_exc.InvalidRequestError( 

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

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

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

2470 ) from err 

2471 

2472 def get_property_by_column( 

2473 self, column: ColumnElement[_T] 

2474 ) -> MapperProperty[_T]: 

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

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

2477 

2478 return self._columntoproperty[column] 

2479 

2480 @HasMemoized.memoized_attribute 

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

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

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

2484 

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

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

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

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

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

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

2491 

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

2493 Behavior is undefined if directly modified. 

2494 

2495 """ 

2496 return self._columns.as_readonly() 

2497 

2498 @HasMemoized.memoized_attribute 

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

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

2501 return self._columns.as_readonly() 

2502 

2503 @property 

2504 def iterate_properties(self): 

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

2506 

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

2508 

2509 def _mappers_from_spec( 

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

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

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

2513 represents. 

2514 

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

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

2517 

2518 """ 

2519 if spec == "*": 

2520 mappers = list(self.self_and_descendants) 

2521 elif spec: 

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

2523 for m in util.to_list(spec): 

2524 m = _class_to_mapper(m) 

2525 if not m.isa(self): 

2526 raise sa_exc.InvalidRequestError( 

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

2528 ) 

2529 

2530 if selectable is None: 

2531 mapper_set.update(m.iterate_to_root()) 

2532 else: 

2533 mapper_set.add(m) 

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

2535 else: 

2536 mappers = [] 

2537 

2538 if selectable is not None: 

2539 tables = set( 

2540 sql_util.find_tables(selectable, include_aliases=True) 

2541 ) 

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

2543 return mappers 

2544 

2545 def _selectable_from_mappers( 

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

2547 ) -> FromClause: 

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

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

2550 mapped tables. 

2551 

2552 """ 

2553 from_obj = self.persist_selectable 

2554 for m in mappers: 

2555 if m is self: 

2556 continue 

2557 if m.concrete: 

2558 raise sa_exc.InvalidRequestError( 

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

2560 "when concrete-inheriting mappers are used." 

2561 ) 

2562 elif not m.single: 

2563 if innerjoin: 

2564 from_obj = from_obj.join( 

2565 m.local_table, m.inherit_condition 

2566 ) 

2567 else: 

2568 from_obj = from_obj.outerjoin( 

2569 m.local_table, m.inherit_condition 

2570 ) 

2571 

2572 return from_obj 

2573 

2574 @HasMemoized.memoized_attribute 

2575 def _version_id_has_server_side_value(self) -> bool: 

2576 vid_col = self.version_id_col 

2577 

2578 if vid_col is None: 

2579 return False 

2580 

2581 elif not isinstance(vid_col, Column): 

2582 return True 

2583 else: 

2584 return vid_col.server_default is not None or ( 

2585 vid_col.default is not None 

2586 and ( 

2587 not vid_col.default.is_scalar 

2588 and not vid_col.default.is_callable 

2589 ) 

2590 ) 

2591 

2592 @HasMemoized.memoized_attribute 

2593 def _single_table_criteria_component(self): 

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

2595 

2596 hierarchy = tuple( 

2597 m.polymorphic_identity 

2598 for m in self.self_and_descendants 

2599 if not m.polymorphic_abstract 

2600 ) 

2601 

2602 return ( 

2603 self.polymorphic_on._annotate( 

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

2605 ), 

2606 hierarchy, 

2607 ) 

2608 else: 

2609 return None 

2610 

2611 @HasMemoized.memoized_attribute 

2612 def _single_table_criterion(self): 

2613 component = self._single_table_criteria_component 

2614 if component is not None: 

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

2616 else: 

2617 return None 

2618 

2619 @HasMemoized.memoized_attribute 

2620 def _has_aliased_polymorphic_fromclause(self): 

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

2622 like a subquery. 

2623 

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

2625 if this is present. 

2626 

2627 """ 

2628 return self.with_polymorphic and isinstance( 

2629 self.with_polymorphic[1], 

2630 expression.AliasedReturnsRows, 

2631 ) 

2632 

2633 @HasMemoized.memoized_attribute 

2634 def _should_select_with_poly_adapter(self): 

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

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

2637 rows for mapped classes and subclasses against this Mapper. 

2638 

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

2640 for this condition. 

2641 

2642 """ 

2643 

2644 # this has been simplified as of #8456. 

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

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

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

2648 # 

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

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

2651 # flattened JOIN for with_polymorphic.) 

2652 # 

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

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

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

2656 # some kind or polymorphic_union. 

2657 # 

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

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

2660 # on it (such as test_join_from_polymorphic_explicit_aliased_three). 

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

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

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

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

2665 # legacy case we should probably disable. 

2666 # 

2667 # 

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

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

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

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

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

2673 # polymorphic base class. 

2674 # 

2675 return ( 

2676 self._has_aliased_polymorphic_fromclause 

2677 or self._requires_row_aliasing 

2678 or (self.base_mapper._has_aliased_polymorphic_fromclause) 

2679 or self.base_mapper._requires_row_aliasing 

2680 ) 

2681 

2682 @HasMemoized.memoized_attribute 

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

2684 self._check_configure() 

2685 

2686 if not self.with_polymorphic: 

2687 return [] 

2688 return self._mappers_from_spec(*self.with_polymorphic) 

2689 

2690 @HasMemoized.memoized_attribute 

2691 def _post_inspect(self): 

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

2693 

2694 E.g. when Query calls: 

2695 

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

2697 

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

2699 

2700 """ 

2701 self._check_configure() 

2702 

2703 @HasMemoized_ro_memoized_attribute 

2704 def _with_polymorphic_selectable(self) -> FromClause: 

2705 if not self.with_polymorphic: 

2706 return self.persist_selectable 

2707 

2708 spec, selectable = self.with_polymorphic 

2709 if selectable is not None: 

2710 return selectable 

2711 else: 

2712 return self._selectable_from_mappers( 

2713 self._mappers_from_spec(spec, selectable), False 

2714 ) 

2715 

2716 with_polymorphic_mappers = _with_polymorphic_mappers 

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

2718 default "polymorphic" query. 

2719 

2720 """ 

2721 

2722 @HasMemoized_ro_memoized_attribute 

2723 def _insert_cols_evaluating_none(self): 

2724 return { 

2725 table: frozenset( 

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

2727 ) 

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

2729 } 

2730 

2731 @HasMemoized.memoized_attribute 

2732 def _insert_cols_as_none(self): 

2733 return { 

2734 table: frozenset( 

2735 col.key 

2736 for col in columns 

2737 if not col.primary_key 

2738 and not col.server_default 

2739 and not col.default 

2740 and not col.type.should_evaluate_none 

2741 ) 

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

2743 } 

2744 

2745 @HasMemoized.memoized_attribute 

2746 def _propkey_to_col(self): 

2747 return { 

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

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

2750 } 

2751 

2752 @HasMemoized.memoized_attribute 

2753 def _pk_keys_by_table(self): 

2754 return { 

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

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

2757 } 

2758 

2759 @HasMemoized.memoized_attribute 

2760 def _pk_attr_keys_by_table(self): 

2761 return { 

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

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

2764 } 

2765 

2766 @HasMemoized.memoized_attribute 

2767 def _server_default_cols( 

2768 self, 

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

2770 return { 

2771 table: frozenset( 

2772 [ 

2773 col 

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

2775 if col.server_default is not None 

2776 or ( 

2777 col.default is not None 

2778 and col.default.is_clause_element 

2779 ) 

2780 ] 

2781 ) 

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

2783 } 

2784 

2785 @HasMemoized.memoized_attribute 

2786 def _server_onupdate_default_cols( 

2787 self, 

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

2789 return { 

2790 table: frozenset( 

2791 [ 

2792 col 

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

2794 if col.server_onupdate is not None 

2795 or ( 

2796 col.onupdate is not None 

2797 and col.onupdate.is_clause_element 

2798 ) 

2799 ] 

2800 ) 

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

2802 } 

2803 

2804 @HasMemoized.memoized_attribute 

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

2806 return { 

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

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

2809 } 

2810 

2811 @HasMemoized.memoized_attribute 

2812 def _server_onupdate_default_col_keys( 

2813 self, 

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

2815 return { 

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

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

2818 } 

2819 

2820 @HasMemoized.memoized_attribute 

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

2822 result: Set[str] = set() 

2823 

2824 col_to_property = self._columntoproperty 

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

2826 result.update( 

2827 col_to_property[col].key 

2828 for col in columns.intersection(col_to_property) 

2829 ) 

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

2831 result.update( 

2832 col_to_property[col].key 

2833 for col in columns.intersection(col_to_property) 

2834 ) 

2835 return result 

2836 

2837 @HasMemoized.memoized_instancemethod 

2838 def __clause_element__(self): 

2839 annotations: Dict[str, Any] = { 

2840 "entity_namespace": self, 

2841 "parententity": self, 

2842 "parentmapper": self, 

2843 } 

2844 if self.persist_selectable is not self.local_table: 

2845 # joined table inheritance, with polymorphic selectable, 

2846 # etc. 

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

2848 { 

2849 "entity_namespace": self, 

2850 "parententity": self, 

2851 "parentmapper": self, 

2852 } 

2853 )._set_propagate_attrs( 

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

2855 ) 

2856 

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

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

2859 ) 

2860 

2861 @util.memoized_property 

2862 def select_identity_token(self): 

2863 return ( 

2864 expression.null() 

2865 ._annotate( 

2866 { 

2867 "entity_namespace": self, 

2868 "parententity": self, 

2869 "parentmapper": self, 

2870 "identity_token": True, 

2871 } 

2872 ) 

2873 ._set_propagate_attrs( 

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

2875 ) 

2876 ) 

2877 

2878 @property 

2879 def selectable(self) -> FromClause: 

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

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

2882 

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

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

2885 full "polymorphic" selectable is returned. 

2886 

2887 """ 

2888 return self._with_polymorphic_selectable 

2889 

2890 def _with_polymorphic_args( 

2891 self, 

2892 spec: Any = None, 

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

2894 innerjoin: bool = False, 

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

2896 if selectable not in (None, False): 

2897 selectable = coercions.expect( 

2898 roles.FromClauseRole, 

2899 selectable, 

2900 ) 

2901 

2902 if self.with_polymorphic: 

2903 if not spec: 

2904 spec = self.with_polymorphic[0] 

2905 if selectable is False: 

2906 selectable = self.with_polymorphic[1] 

2907 elif selectable is False: 

2908 selectable = None 

2909 mappers = self._mappers_from_spec(spec, selectable) 

2910 if selectable is not None: 

2911 return mappers, selectable 

2912 else: 

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

2914 

2915 @HasMemoized.memoized_attribute 

2916 def _polymorphic_properties(self): 

2917 return list( 

2918 self._iterate_polymorphic_properties( 

2919 self._with_polymorphic_mappers 

2920 ) 

2921 ) 

2922 

2923 @property 

2924 def _all_column_expressions(self): 

2925 poly_properties = self._polymorphic_properties 

2926 adapter = self._polymorphic_adapter 

2927 

2928 return [ 

2929 adapter.columns[c] if adapter else c 

2930 for prop in poly_properties 

2931 if isinstance(prop, properties.ColumnProperty) 

2932 and prop._renders_in_subqueries 

2933 for c in prop.columns 

2934 ] 

2935 

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

2937 if polymorphic_mappers: 

2938 poly_properties = self._iterate_polymorphic_properties( 

2939 polymorphic_mappers 

2940 ) 

2941 else: 

2942 poly_properties = self._polymorphic_properties 

2943 

2944 return [ 

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

2946 for prop in poly_properties 

2947 if isinstance(prop, properties.ColumnProperty) 

2948 ] 

2949 

2950 @HasMemoized.memoized_attribute 

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

2952 if self._has_aliased_polymorphic_fromclause: 

2953 return orm_util.ORMAdapter( 

2954 orm_util._TraceAdaptRole.MAPPER_POLYMORPHIC_ADAPTER, 

2955 self, 

2956 selectable=self.selectable, 

2957 equivalents=self._equivalent_columns, 

2958 limit_on_entity=False, 

2959 ) 

2960 else: 

2961 return None 

2962 

2963 def _iterate_polymorphic_properties(self, mappers=None): 

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

2965 a SELECT.""" 

2966 if mappers is None: 

2967 mappers = self._with_polymorphic_mappers 

2968 

2969 if not mappers: 

2970 for c in self.iterate_properties: 

2971 yield c 

2972 else: 

2973 # in the polymorphic case, filter out discriminator columns 

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

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

2976 for c in util.unique_list( 

2977 chain( 

2978 *[ 

2979 list(mapper.iterate_properties) 

2980 for mapper in [self] + mappers 

2981 ] 

2982 ) 

2983 ): 

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

2985 self.polymorphic_on is None 

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

2987 ): 

2988 continue 

2989 yield c 

2990 

2991 @HasMemoized.memoized_attribute 

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

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

2994 associated this mapper. 

2995 

2996 This is an object that provides each property based on 

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

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

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

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

3001 column. The namespace object can also be iterated, 

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

3003 

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

3005 of this attribute which limit the types of properties 

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

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

3008 

3009 .. warning:: 

3010 

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

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

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

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

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

3016 accessing attributes dynamically, favor using the dict-access 

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

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

3019 

3020 .. seealso:: 

3021 

3022 :attr:`_orm.Mapper.all_orm_descriptors` 

3023 

3024 """ 

3025 

3026 self._check_configure() 

3027 return util.ReadOnlyProperties(self._props) 

3028 

3029 @HasMemoized.memoized_attribute 

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

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

3032 with the mapped class. 

3033 

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

3035 associated with the mapped class or its superclasses. 

3036 

3037 This namespace includes attributes that are mapped to the class 

3038 as well as attributes declared by extension modules. 

3039 It includes any Python descriptor type that inherits from 

3040 :class:`.InspectionAttr`. This includes 

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

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

3043 :class:`.AssociationProxy`. 

3044 

3045 To distinguish between mapped attributes and extension attributes, 

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

3047 to a constant that distinguishes between different extension types. 

3048 

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

3050 

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

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

3053 

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

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

3056 3 below. The order will be the 

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

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

3059 or the mapper. 

3060 

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

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

3063 class in which it first appeared. 

3064 

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

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

3067 

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

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

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

3071 referring to the collection of mapped properties via 

3072 :attr:`_orm.Mapper.attrs`. 

3073 

3074 .. warning:: 

3075 

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

3077 accessor namespace is an 

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

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

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

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

3082 accessing attributes dynamically, favor using the dict-access 

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

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

3085 collisions. 

3086 

3087 .. seealso:: 

3088 

3089 :attr:`_orm.Mapper.attrs` 

3090 

3091 """ 

3092 return util.ReadOnlyProperties( 

3093 dict(self.class_manager._all_sqla_attributes()) 

3094 ) 

3095 

3096 @HasMemoized.memoized_attribute 

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

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

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

3100 all synonyms that refer to primary key columns 

3101 

3102 """ 

3103 descriptor_props = util.preloaded.orm_descriptor_props 

3104 

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

3106 

3107 return { 

3108 syn.key: syn.name 

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

3110 if isinstance(syn, descriptor_props.SynonymProperty) 

3111 and syn.name in pk_keys 

3112 } 

3113 

3114 @HasMemoized.memoized_attribute 

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

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

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

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

3119 

3120 .. seealso:: 

3121 

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

3123 :class:`.MapperProperty` 

3124 objects. 

3125 

3126 """ 

3127 descriptor_props = util.preloaded.orm_descriptor_props 

3128 

3129 return self._filter_properties(descriptor_props.SynonymProperty) 

3130 

3131 @util.ro_non_memoized_property 

3132 def entity_namespace(self) -> _EntityNamespace: 

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

3134 

3135 @HasMemoized.memoized_attribute 

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

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

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

3139 

3140 .. seealso:: 

3141 

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

3143 :class:`.MapperProperty` 

3144 objects. 

3145 

3146 """ 

3147 return self._filter_properties(properties.ColumnProperty) 

3148 

3149 @HasMemoized.memoized_attribute 

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

3151 def relationships( 

3152 self, 

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

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

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

3156 

3157 .. warning:: 

3158 

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

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

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

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

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

3164 accessing attributes dynamically, favor using the dict-access 

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

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

3167 collisions. 

3168 

3169 .. seealso:: 

3170 

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

3172 :class:`.MapperProperty` 

3173 objects. 

3174 

3175 """ 

3176 return self._filter_properties( 

3177 util.preloaded.orm_relationships.RelationshipProperty 

3178 ) 

3179 

3180 @HasMemoized.memoized_attribute 

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

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

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

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

3185 

3186 .. seealso:: 

3187 

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

3189 :class:`.MapperProperty` 

3190 objects. 

3191 

3192 """ 

3193 return self._filter_properties( 

3194 util.preloaded.orm_descriptor_props.CompositeProperty 

3195 ) 

3196 

3197 def _filter_properties( 

3198 self, type_: Type[_MP] 

3199 ) -> util.ReadOnlyProperties[_MP]: 

3200 self._check_configure() 

3201 return util.ReadOnlyProperties( 

3202 util.OrderedDict( 

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

3204 ) 

3205 ) 

3206 

3207 @HasMemoized.memoized_attribute 

3208 def _get_clause(self): 

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

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

3211 by primary key. 

3212 

3213 """ 

3214 params = [ 

3215 ( 

3216 primary_key, 

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

3218 ) 

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

3220 ] 

3221 return ( 

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

3223 util.column_dict(params), 

3224 ) 

3225 

3226 @HasMemoized.memoized_attribute 

3227 def _equivalent_columns(self) -> _EquivalentColumnMap: 

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

3229 the determination of column pairs that are equated to 

3230 one another based on inherit condition. This is designed 

3231 to work with the queries that util.polymorphic_union 

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

3233 the base table directly (including the subclass table columns 

3234 only). 

3235 

3236 The resulting structure is a dictionary of columns mapped 

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

3238 

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

3240 

3241 """ # noqa: E501 

3242 result: _EquivalentColumnMap = {} 

3243 

3244 def visit_binary(binary): 

3245 if binary.operator == operators.eq: 

3246 if binary.left in result: 

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

3248 else: 

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

3250 if binary.right in result: 

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

3252 else: 

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

3254 

3255 for mapper in self.base_mapper.self_and_descendants: 

3256 if mapper.inherit_condition is not None: 

3257 visitors.traverse( 

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

3259 ) 

3260 

3261 return result 

3262 

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

3264 if isinstance( 

3265 obj, 

3266 ( 

3267 _MappedAttribute, 

3268 instrumentation.ClassManager, 

3269 expression.ColumnElement, 

3270 ), 

3271 ): 

3272 return False 

3273 else: 

3274 return assigned_name not in self._dataclass_fields 

3275 

3276 @HasMemoized.memoized_attribute 

3277 def _dataclass_fields(self): 

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

3279 

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

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

3282 present on the class. 

3283 

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

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

3286 

3287 """ 

3288 

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

3290 return True 

3291 

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

3293 # either local or from an inherited class 

3294 # ignore dataclass field default values 

3295 if local: 

3296 if self.class_.__dict__.get( 

3297 assigned_name, None 

3298 ) is not None and self._is_userland_descriptor( 

3299 assigned_name, self.class_.__dict__[assigned_name] 

3300 ): 

3301 return True 

3302 else: 

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

3304 if attr is not None and self._is_userland_descriptor( 

3305 assigned_name, attr 

3306 ): 

3307 return True 

3308 

3309 if ( 

3310 self.include_properties is not None 

3311 and name not in self.include_properties 

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

3313 ): 

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

3315 return True 

3316 

3317 if self.exclude_properties is not None and ( 

3318 name in self.exclude_properties 

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

3320 ): 

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

3322 return True 

3323 

3324 return False 

3325 

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

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

3328 common inherited parent as this mapper.""" 

3329 

3330 return self.base_mapper is other.base_mapper 

3331 

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

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

3334 one. common parent but different branch 

3335 

3336 """ 

3337 return ( 

3338 self.base_mapper is other.base_mapper 

3339 and not self.isa(other) 

3340 and not other.isa(self) 

3341 ) 

3342 

3343 def _canload( 

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

3345 ) -> bool: 

3346 s = self.primary_mapper() 

3347 if self.polymorphic_on is not None or allow_subtypes: 

3348 return _state_mapper(state).isa(s) 

3349 else: 

3350 return _state_mapper(state) is s 

3351 

3352 def isa(self, other: Mapper[Any]) -> bool: 

3353 """Return True if the this mapper inherits from the given mapper.""" 

3354 

3355 m: Optional[Mapper[Any]] = self 

3356 while m and m is not other: 

3357 m = m.inherits 

3358 return bool(m) 

3359 

3360 def iterate_to_root(self) -> Iterator[Mapper[Any]]: 

3361 m: Optional[Mapper[Any]] = self 

3362 while m: 

3363 yield m 

3364 m = m.inherits 

3365 

3366 @HasMemoized.memoized_attribute 

3367 def self_and_descendants(self) -> Sequence[Mapper[Any]]: 

3368 """The collection including this mapper and all descendant mappers. 

3369 

3370 This includes not just the immediately inheriting mappers but 

3371 all their inheriting mappers as well. 

3372 

3373 """ 

3374 descendants = [] 

3375 stack = deque([self]) 

3376 while stack: 

3377 item = stack.popleft() 

3378 descendants.append(item) 

3379 stack.extend(item._inheriting_mappers) 

3380 return util.WeakSequence(descendants) 

3381 

3382 def polymorphic_iterator(self) -> Iterator[Mapper[Any]]: 

3383 """Iterate through the collection including this mapper and 

3384 all descendant mappers. 

3385 

3386 This includes not just the immediately inheriting mappers but 

3387 all their inheriting mappers as well. 

3388 

3389 To iterate through an entire hierarchy, use 

3390 ``mapper.base_mapper.polymorphic_iterator()``. 

3391 

3392 """ 

3393 return iter(self.self_and_descendants) 

3394 

3395 def primary_mapper(self) -> Mapper[Any]: 

3396 """Return the primary mapper corresponding to this mapper's class key 

3397 (class).""" 

3398 

3399 return self.class_manager.mapper 

3400 

3401 @property 

3402 def primary_base_mapper(self) -> Mapper[Any]: 

3403 return self.class_manager.mapper.base_mapper 

3404 

3405 def _result_has_identity_key(self, result, adapter=None): 

3406 pk_cols: Sequence[ColumnElement[Any]] 

3407 if adapter is not None: 

3408 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3409 else: 

3410 pk_cols = self.primary_key 

3411 rk = result.keys() 

3412 for col in pk_cols: 

3413 if col not in rk: 

3414 return False 

3415 else: 

3416 return True 

3417 

3418 def identity_key_from_row( 

3419 self, 

3420 row: Union[Row[Unpack[TupleAny]], RowMapping], 

3421 identity_token: Optional[Any] = None, 

3422 adapter: Optional[ORMAdapter] = None, 

3423 ) -> _IdentityKeyType[_O]: 

3424 """Return an identity-map key for use in storing/retrieving an 

3425 item from the identity map. 

3426 

3427 :param row: A :class:`.Row` or :class:`.RowMapping` produced from a 

3428 result set that selected from the ORM mapped primary key columns. 

3429 

3430 .. versionchanged:: 2.0 

3431 :class:`.Row` or :class:`.RowMapping` are accepted 

3432 for the "row" argument 

3433 

3434 """ 

3435 pk_cols: Sequence[ColumnElement[Any]] 

3436 if adapter is not None: 

3437 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3438 else: 

3439 pk_cols = self.primary_key 

3440 

3441 mapping: RowMapping 

3442 if hasattr(row, "_mapping"): 

3443 mapping = row._mapping 

3444 else: 

3445 mapping = row # type: ignore[assignment] 

3446 

3447 return ( 

3448 self._identity_class, 

3449 tuple(mapping[column] for column in pk_cols), 

3450 identity_token, 

3451 ) 

3452 

3453 def identity_key_from_primary_key( 

3454 self, 

3455 primary_key: Tuple[Any, ...], 

3456 identity_token: Optional[Any] = None, 

3457 ) -> _IdentityKeyType[_O]: 

3458 """Return an identity-map key for use in storing/retrieving an 

3459 item from an identity map. 

3460 

3461 :param primary_key: A list of values indicating the identifier. 

3462 

3463 """ 

3464 return ( 

3465 self._identity_class, 

3466 tuple(primary_key), 

3467 identity_token, 

3468 ) 

3469 

3470 def identity_key_from_instance(self, instance: _O) -> _IdentityKeyType[_O]: 

3471 """Return the identity key for the given instance, based on 

3472 its primary key attributes. 

3473 

3474 If the instance's state is expired, calling this method 

3475 will result in a database check to see if the object has been deleted. 

3476 If the row no longer exists, 

3477 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3478 

3479 This value is typically also found on the instance state under the 

3480 attribute name `key`. 

3481 

3482 """ 

3483 state = attributes.instance_state(instance) 

3484 return self._identity_key_from_state(state, PassiveFlag.PASSIVE_OFF) 

3485 

3486 def _identity_key_from_state( 

3487 self, 

3488 state: InstanceState[_O], 

3489 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3490 ) -> _IdentityKeyType[_O]: 

3491 dict_ = state.dict 

3492 manager = state.manager 

3493 return ( 

3494 self._identity_class, 

3495 tuple( 

3496 [ 

3497 manager[prop.key].impl.get(state, dict_, passive) 

3498 for prop in self._identity_key_props 

3499 ] 

3500 ), 

3501 state.identity_token, 

3502 ) 

3503 

3504 def primary_key_from_instance(self, instance: _O) -> Tuple[Any, ...]: 

3505 """Return the list of primary key values for the given 

3506 instance. 

3507 

3508 If the instance's state is expired, calling this method 

3509 will result in a database check to see if the object has been deleted. 

3510 If the row no longer exists, 

3511 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3512 

3513 """ 

3514 state = attributes.instance_state(instance) 

3515 identity_key = self._identity_key_from_state( 

3516 state, PassiveFlag.PASSIVE_OFF 

3517 ) 

3518 return identity_key[1] 

3519 

3520 @HasMemoized.memoized_attribute 

3521 def _persistent_sortkey_fn(self): 

3522 key_fns = [col.type.sort_key_function for col in self.primary_key] 

3523 

3524 if set(key_fns).difference([None]): 

3525 

3526 def key(state): 

3527 return tuple( 

3528 key_fn(val) if key_fn is not None else val 

3529 for key_fn, val in zip(key_fns, state.key[1]) 

3530 ) 

3531 

3532 else: 

3533 

3534 def key(state): 

3535 return state.key[1] 

3536 

3537 return key 

3538 

3539 @HasMemoized.memoized_attribute 

3540 def _identity_key_props(self): 

3541 return [self._columntoproperty[col] for col in self.primary_key] 

3542 

3543 @HasMemoized.memoized_attribute 

3544 def _all_pk_cols(self): 

3545 collection: Set[ColumnClause[Any]] = set() 

3546 for table in self.tables: 

3547 collection.update(self._pks_by_table[table]) 

3548 return collection 

3549 

3550 @HasMemoized.memoized_attribute 

3551 def _should_undefer_in_wildcard(self): 

3552 cols: Set[ColumnElement[Any]] = set(self.primary_key) 

3553 if self.polymorphic_on is not None: 

3554 cols.add(self.polymorphic_on) 

3555 return cols 

3556 

3557 @HasMemoized.memoized_attribute 

3558 def _primary_key_propkeys(self): 

3559 return {self._columntoproperty[col].key for col in self._all_pk_cols} 

3560 

3561 def _get_state_attr_by_column( 

3562 self, 

3563 state: InstanceState[_O], 

3564 dict_: _InstanceDict, 

3565 column: ColumnElement[Any], 

3566 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3567 ) -> Any: 

3568 prop = self._columntoproperty[column] 

3569 return state.manager[prop.key].impl.get(state, dict_, passive=passive) 

3570 

3571 def _state_ident_getter(self, columns, passive): 

3572 lookup_keys = [self._columntoproperty[col].key for col in columns] 

3573 missing = object() 

3574 

3575 def get_ident(state, state_dict): 

3576 return tuple( 

3577 ( 

3578 v 

3579 if (v := state_dict.get(lk, missing)) is not missing 

3580 else self._get_state_attr_by_column( 

3581 state, state_dict, col, passive=passive 

3582 ) 

3583 ) 

3584 for lk, col in zip(lookup_keys, columns) 

3585 ) 

3586 

3587 return get_ident 

3588 

3589 def _set_committed_state_attr_by_column(self, state, dict_, column, value): 

3590 prop = self._columntoproperty[column] 

3591 state.manager[prop.key].impl.set_committed_value(state, dict_, value) 

3592 

3593 def _set_state_attr_by_column(self, state, dict_, column, value): 

3594 prop = self._columntoproperty[column] 

3595 state.manager[prop.key].impl.set(state, dict_, value, None) 

3596 

3597 def _get_committed_attr_by_column(self, obj, column): 

3598 state = attributes.instance_state(obj) 

3599 dict_ = attributes.instance_dict(obj) 

3600 return self._get_committed_state_attr_by_column( 

3601 state, dict_, column, passive=PassiveFlag.PASSIVE_OFF 

3602 ) 

3603 

3604 def _get_committed_state_attr_by_column( 

3605 self, state, dict_, column, passive=PassiveFlag.PASSIVE_RETURN_NO_VALUE 

3606 ): 

3607 prop = self._columntoproperty[column] 

3608 return state.manager[prop.key].impl.get_committed_value( 

3609 state, dict_, passive=passive 

3610 ) 

3611 

3612 def _optimized_get_statement(self, state, attribute_names): 

3613 """assemble a WHERE clause which retrieves a given state by primary 

3614 key, using a minimized set of tables. 

3615 

3616 Applies to a joined-table inheritance mapper where the 

3617 requested attribute names are only present on joined tables, 

3618 not the base table. The WHERE clause attempts to include 

3619 only those tables to minimize joins. 

3620 

3621 """ 

3622 props = self._props 

3623 

3624 col_attribute_names = set(attribute_names).intersection( 

3625 state.mapper.column_attrs.keys() 

3626 ) 

3627 tables: Set[FromClause] = set( 

3628 chain( 

3629 *[ 

3630 sql_util.find_tables(c, check_columns=True) 

3631 for key in col_attribute_names 

3632 for c in props[key].columns 

3633 ] 

3634 ) 

3635 ) 

3636 

3637 if self.base_mapper.local_table in tables: 

3638 return None 

3639 

3640 def visit_binary(binary): 

3641 leftcol = binary.left 

3642 rightcol = binary.right 

3643 if leftcol is None or rightcol is None: 

3644 return 

3645 

3646 if leftcol.table not in tables: 

3647 leftval = self._get_committed_state_attr_by_column( 

3648 state, 

3649 state.dict, 

3650 leftcol, 

3651 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3652 ) 

3653 if leftval in orm_util._none_set: 

3654 raise _OptGetColumnsNotAvailable() 

3655 binary.left = sql.bindparam( 

3656 None, leftval, type_=binary.right.type 

3657 ) 

3658 elif rightcol.table not in tables: 

3659 rightval = self._get_committed_state_attr_by_column( 

3660 state, 

3661 state.dict, 

3662 rightcol, 

3663 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3664 ) 

3665 if rightval in orm_util._none_set: 

3666 raise _OptGetColumnsNotAvailable() 

3667 binary.right = sql.bindparam( 

3668 None, rightval, type_=binary.right.type 

3669 ) 

3670 

3671 allconds: List[ColumnElement[bool]] = [] 

3672 

3673 start = False 

3674 

3675 # as of #7507, from the lowest base table on upwards, 

3676 # we include all intermediary tables. 

3677 

3678 for mapper in reversed(list(self.iterate_to_root())): 

3679 if mapper.local_table in tables: 

3680 start = True 

3681 elif not isinstance(mapper.local_table, expression.TableClause): 

3682 return None 

3683 if start and not mapper.single: 

3684 assert mapper.inherits 

3685 assert not mapper.concrete 

3686 assert mapper.inherit_condition is not None 

3687 allconds.append(mapper.inherit_condition) 

3688 tables.add(mapper.local_table) 

3689 

3690 # only the bottom table needs its criteria to be altered to fit 

3691 # the primary key ident - the rest of the tables upwards to the 

3692 # descendant-most class should all be present and joined to each 

3693 # other. 

3694 try: 

3695 _traversed = visitors.cloned_traverse( 

3696 allconds[0], {}, {"binary": visit_binary} 

3697 ) 

3698 except _OptGetColumnsNotAvailable: 

3699 return None 

3700 else: 

3701 allconds[0] = _traversed 

3702 

3703 cond = sql.and_(*allconds) 

3704 

3705 cols = [] 

3706 for key in col_attribute_names: 

3707 cols.extend(props[key].columns) 

3708 return ( 

3709 sql.select(*cols) 

3710 .where(cond) 

3711 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 

3712 ) 

3713 

3714 def _iterate_to_target_viawpoly(self, mapper): 

3715 if self.isa(mapper): 

3716 prev = self 

3717 for m in self.iterate_to_root(): 

3718 yield m 

3719 

3720 if m is not prev and prev not in m._with_polymorphic_mappers: 

3721 break 

3722 

3723 prev = m 

3724 if m is mapper: 

3725 break 

3726 

3727 @HasMemoized.memoized_attribute 

3728 def _would_selectinload_combinations_cache(self): 

3729 return {} 

3730 

3731 def _would_selectin_load_only_from_given_mapper(self, super_mapper): 

3732 """return True if this mapper would "selectin" polymorphic load based 

3733 on the given super mapper, and not from a setting from a subclass. 

3734 

3735 given:: 

3736 

3737 class A: ... 

3738 

3739 

3740 class B(A): 

3741 __mapper_args__ = {"polymorphic_load": "selectin"} 

3742 

3743 

3744 class C(B): ... 

3745 

3746 

3747 class D(B): 

3748 __mapper_args__ = {"polymorphic_load": "selectin"} 

3749 

3750 ``inspect(C)._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3751 returns True, because C does selectin loading because of B's setting. 

3752 

3753 OTOH, ``inspect(D) 

3754 ._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3755 returns False, because D does selectin loading because of its own 

3756 setting; when we are doing a selectin poly load from B, we want to 

3757 filter out D because it would already have its own selectin poly load 

3758 set up separately. 

3759 

3760 Added as part of #9373. 

3761 

3762 """ 

3763 cache = self._would_selectinload_combinations_cache 

3764 

3765 try: 

3766 return cache[super_mapper] 

3767 except KeyError: 

3768 pass 

3769 

3770 # assert that given object is a supermapper, meaning we already 

3771 # strong reference it directly or indirectly. this allows us 

3772 # to not worry that we are creating new strongrefs to unrelated 

3773 # mappers or other objects. 

3774 assert self.isa(super_mapper) 

3775 

3776 mapper = super_mapper 

3777 for m in self._iterate_to_target_viawpoly(mapper): 

3778 if m.polymorphic_load == "selectin": 

3779 retval = m is super_mapper 

3780 break 

3781 else: 

3782 retval = False 

3783 

3784 cache[super_mapper] = retval 

3785 return retval 

3786 

3787 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 

3788 if not enabled_via_opt: 

3789 # common case, takes place for all polymorphic loads 

3790 mapper = polymorphic_from 

3791 for m in self._iterate_to_target_viawpoly(mapper): 

3792 if m.polymorphic_load == "selectin": 

3793 return m 

3794 else: 

3795 # uncommon case, selectin load options were used 

3796 enabled_via_opt = set(enabled_via_opt) 

3797 enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 

3798 for entity in enabled_via_opt.union([polymorphic_from]): 

3799 mapper = entity.mapper 

3800 for m in self._iterate_to_target_viawpoly(mapper): 

3801 if ( 

3802 m.polymorphic_load == "selectin" 

3803 or m in enabled_via_opt_mappers 

3804 ): 

3805 return enabled_via_opt_mappers.get(m, m) 

3806 

3807 return None 

3808 

3809 @util.preload_module("sqlalchemy.orm.strategy_options") 

3810 def _subclass_load_via_in(self, entity, polymorphic_from): 

3811 """Assemble a that can load the columns local to 

3812 this subclass as a SELECT with IN. 

3813 

3814 """ 

3815 

3816 strategy_options = util.preloaded.orm_strategy_options 

3817 

3818 assert self.inherits 

3819 

3820 if self.polymorphic_on is not None: 

3821 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 

3822 keep_props = set([polymorphic_prop] + self._identity_key_props) 

3823 else: 

3824 keep_props = set(self._identity_key_props) 

3825 

3826 disable_opt = strategy_options.Load(entity) 

3827 enable_opt = strategy_options.Load(entity) 

3828 

3829 classes_to_include = {self} 

3830 m: Optional[Mapper[Any]] = self.inherits 

3831 while ( 

3832 m is not None 

3833 and m is not polymorphic_from 

3834 and m.polymorphic_load == "selectin" 

3835 ): 

3836 classes_to_include.add(m) 

3837 m = m.inherits 

3838 

3839 for prop in self.column_attrs + self.relationships: 

3840 # skip prop keys that are not instrumented on the mapped class. 

3841 # this is primarily the "_sa_polymorphic_on" property that gets 

3842 # created for an ad-hoc polymorphic_on SQL expression, issue #8704 

3843 if prop.key not in self.class_manager: 

3844 continue 

3845 

3846 if prop.parent in classes_to_include or prop in keep_props: 

3847 # "enable" options, to turn on the properties that we want to 

3848 # load by default (subject to options from the query) 

3849 if not isinstance(prop, StrategizedProperty): 

3850 continue 

3851 

3852 enable_opt = enable_opt._set_generic_strategy( 

3853 # convert string name to an attribute before passing 

3854 # to loader strategy. note this must be in terms 

3855 # of given entity, such as AliasedClass, etc. 

3856 (getattr(entity.entity_namespace, prop.key),), 

3857 dict(prop.strategy_key), 

3858 _reconcile_to_other=True, 

3859 ) 

3860 else: 

3861 # "disable" options, to turn off the properties from the 

3862 # superclass that we *don't* want to load, applied after 

3863 # the options from the query to override them 

3864 disable_opt = disable_opt._set_generic_strategy( 

3865 # convert string name to an attribute before passing 

3866 # to loader strategy. note this must be in terms 

3867 # of given entity, such as AliasedClass, etc. 

3868 (getattr(entity.entity_namespace, prop.key),), 

3869 {"do_nothing": True}, 

3870 _reconcile_to_other=False, 

3871 ) 

3872 

3873 primary_key = list(self.primary_key) 

3874 

3875 in_expr: ColumnElement[Any] 

3876 

3877 if len(primary_key) > 1: 

3878 in_expr = sql.tuple_(*primary_key) 

3879 else: 

3880 in_expr = primary_key[0] 

3881 

3882 if entity.is_aliased_class: 

3883 assert entity.mapper is self 

3884 

3885 q = sql.select(entity).set_label_style( 

3886 LABEL_STYLE_TABLENAME_PLUS_COL 

3887 ) 

3888 

3889 in_expr = entity._adapter.traverse(in_expr) 

3890 q = q.where( 

3891 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3892 ) 

3893 else: 

3894 q = sql.select(self).set_label_style( 

3895 LABEL_STYLE_TABLENAME_PLUS_COL 

3896 ) 

3897 q = q.where( 

3898 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3899 ) 

3900 

3901 return q, enable_opt, disable_opt 

3902 

3903 @HasMemoized.memoized_attribute 

3904 def _subclass_load_via_in_mapper(self): 

3905 # the default is loading this mapper against the basemost mapper 

3906 return self._subclass_load_via_in(self, self.base_mapper) 

3907 

3908 def cascade_iterator( 

3909 self, 

3910 type_: str, 

3911 state: InstanceState[_O], 

3912 halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None, 

3913 ) -> Iterator[ 

3914 Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict] 

3915 ]: 

3916 r"""Iterate each element and its mapper in an object graph, 

3917 for all relationships that meet the given cascade rule. 

3918 

3919 :param type\_: 

3920 The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 

3921 etc.). 

3922 

3923 .. note:: the ``"all"`` cascade is not accepted here. For a generic 

3924 object traversal function, see :ref:`faq_walk_objects`. 

3925 

3926 :param state: 

3927 The lead InstanceState. child items will be processed per 

3928 the relationships defined for this object's mapper. 

3929 

3930 :return: the method yields individual object instances. 

3931 

3932 .. seealso:: 

3933 

3934 :ref:`unitofwork_cascades` 

3935 

3936 :ref:`faq_walk_objects` - illustrates a generic function to 

3937 traverse all objects without relying on cascades. 

3938 

3939 """ 

3940 visited_states: Set[InstanceState[Any]] = set() 

3941 prp, mpp = object(), object() 

3942 

3943 assert state.mapper.isa(self) 

3944 

3945 # this is actually a recursive structure, fully typing it seems 

3946 # a little too difficult for what it's worth here 

3947 visitables: Deque[ 

3948 Tuple[ 

3949 Deque[Any], 

3950 object, 

3951 Optional[InstanceState[Any]], 

3952 Optional[_InstanceDict], 

3953 ] 

3954 ] 

3955 

3956 visitables = deque( 

3957 [(deque(state.mapper._props.values()), prp, state, state.dict)] 

3958 ) 

3959 

3960 while visitables: 

3961 iterator, item_type, parent_state, parent_dict = visitables[-1] 

3962 if not iterator: 

3963 visitables.pop() 

3964 continue 

3965 

3966 if item_type is prp: 

3967 prop = iterator.popleft() 

3968 if not prop.cascade or type_ not in prop.cascade: 

3969 continue 

3970 assert parent_state is not None 

3971 assert parent_dict is not None 

3972 queue = deque( 

3973 prop.cascade_iterator( 

3974 type_, 

3975 parent_state, 

3976 parent_dict, 

3977 visited_states, 

3978 halt_on, 

3979 ) 

3980 ) 

3981 if queue: 

3982 visitables.append((queue, mpp, None, None)) 

3983 elif item_type is mpp: 

3984 ( 

3985 instance, 

3986 instance_mapper, 

3987 corresponding_state, 

3988 corresponding_dict, 

3989 ) = iterator.popleft() 

3990 yield ( 

3991 instance, 

3992 instance_mapper, 

3993 corresponding_state, 

3994 corresponding_dict, 

3995 ) 

3996 visitables.append( 

3997 ( 

3998 deque(instance_mapper._props.values()), 

3999 prp, 

4000 corresponding_state, 

4001 corresponding_dict, 

4002 ) 

4003 ) 

4004 

4005 @HasMemoized.memoized_attribute 

4006 def _compiled_cache(self): 

4007 return util.LRUCache(self._compiled_cache_size) 

4008 

4009 @HasMemoized.memoized_attribute 

4010 def _multiple_persistence_tables(self): 

4011 return len(self.tables) > 1 

4012 

4013 @HasMemoized.memoized_attribute 

4014 def _sorted_tables(self): 

4015 table_to_mapper: Dict[TableClause, Mapper[Any]] = {} 

4016 

4017 for mapper in self.base_mapper.self_and_descendants: 

4018 for t in mapper.tables: 

4019 table_to_mapper.setdefault(t, mapper) 

4020 

4021 extra_dependencies = [] 

4022 for table, mapper in table_to_mapper.items(): 

4023 super_ = mapper.inherits 

4024 if super_: 

4025 extra_dependencies.extend( 

4026 [(super_table, table) for super_table in super_.tables] 

4027 ) 

4028 

4029 def skip(fk): 

4030 # attempt to skip dependencies that are not 

4031 # significant to the inheritance chain 

4032 # for two tables that are related by inheritance. 

4033 # while that dependency may be important, it's technically 

4034 # not what we mean to sort on here. 

4035 parent = table_to_mapper.get(fk.parent.table) 

4036 dep = table_to_mapper.get(fk.column.table) 

4037 if ( 

4038 parent is not None 

4039 and dep is not None 

4040 and dep is not parent 

4041 and dep.inherit_condition is not None 

4042 ): 

4043 cols = set(sql_util._find_columns(dep.inherit_condition)) 

4044 if parent.inherit_condition is not None: 

4045 cols = cols.union( 

4046 sql_util._find_columns(parent.inherit_condition) 

4047 ) 

4048 return fk.parent not in cols and fk.column not in cols 

4049 else: 

4050 return fk.parent not in cols 

4051 return False 

4052 

4053 sorted_ = sql_util.sort_tables( 

4054 table_to_mapper, 

4055 skip_fn=skip, 

4056 extra_dependencies=extra_dependencies, 

4057 ) 

4058 

4059 ret = util.OrderedDict() 

4060 for t in sorted_: 

4061 ret[t] = table_to_mapper[t] 

4062 return ret 

4063 

4064 def _memo(self, key: Any, callable_: Callable[[], _T]) -> _T: 

4065 if key in self._memoized_values: 

4066 return cast(_T, self._memoized_values[key]) 

4067 else: 

4068 self._memoized_values[key] = value = callable_() 

4069 return value 

4070 

4071 @util.memoized_property 

4072 def _table_to_equated(self): 

4073 """memoized map of tables to collections of columns to be 

4074 synchronized upwards to the base mapper.""" 

4075 

4076 result: util.defaultdict[ 

4077 Table, 

4078 List[ 

4079 Tuple[ 

4080 Mapper[Any], 

4081 List[Tuple[ColumnElement[Any], ColumnElement[Any]]], 

4082 ] 

4083 ], 

4084 ] = util.defaultdict(list) 

4085 

4086 def set_union(x, y): 

4087 return x.union(y) 

4088 

4089 for table in self._sorted_tables: 

4090 cols = set(table.c) 

4091 

4092 for m in self.iterate_to_root(): 

4093 if m._inherits_equated_pairs and cols.intersection( 

4094 reduce( 

4095 set_union, 

4096 [l.proxy_set for l, r in m._inherits_equated_pairs], 

4097 ) 

4098 ): 

4099 result[table].append((m, m._inherits_equated_pairs)) 

4100 

4101 return result 

4102 

4103 

4104class _OptGetColumnsNotAvailable(Exception): 

4105 pass 

4106 

4107 

4108def configure_mappers() -> None: 

4109 """Initialize the inter-mapper relationships of all mappers that 

4110 have been constructed thus far across all :class:`_orm.registry` 

4111 collections. 

4112 

4113 The configure step is used to reconcile and initialize the 

4114 :func:`_orm.relationship` linkages between mapped classes, as well as to 

4115 invoke configuration events such as the 

4116 :meth:`_orm.MapperEvents.before_configured` and 

4117 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM 

4118 extensions or user-defined extension hooks. 

4119 

4120 Mapper configuration is normally invoked automatically, the first time 

4121 mappings from a particular :class:`_orm.registry` are used, as well as 

4122 whenever mappings are used and additional not-yet-configured mappers have 

4123 been constructed. The automatic configuration process however is local only 

4124 to the :class:`_orm.registry` involving the target mapper and any related 

4125 :class:`_orm.registry` objects which it may depend on; this is 

4126 equivalent to invoking the :meth:`_orm.registry.configure` method 

4127 on a particular :class:`_orm.registry`. 

4128 

4129 By contrast, the :func:`_orm.configure_mappers` function will invoke the 

4130 configuration process on all :class:`_orm.registry` objects that 

4131 exist in memory, and may be useful for scenarios where many individual 

4132 :class:`_orm.registry` objects that are nonetheless interrelated are 

4133 in use. 

4134 

4135 .. versionchanged:: 1.4 

4136 

4137 As of SQLAlchemy 1.4.0b2, this function works on a 

4138 per-:class:`_orm.registry` basis, locating all :class:`_orm.registry` 

4139 objects present and invoking the :meth:`_orm.registry.configure` method 

4140 on each. The :meth:`_orm.registry.configure` method may be preferred to 

4141 limit the configuration of mappers to those local to a particular 

4142 :class:`_orm.registry` and/or declarative base class. 

4143 

4144 Points at which automatic configuration is invoked include when a mapped 

4145 class is instantiated into an instance, as well as when ORM queries 

4146 are emitted using :meth:`.Session.query` or :meth:`_orm.Session.execute` 

4147 with an ORM-enabled statement. 

4148 

4149 The mapper configure process, whether invoked by 

4150 :func:`_orm.configure_mappers` or from :meth:`_orm.registry.configure`, 

4151 provides several event hooks that can be used to augment the mapper 

4152 configuration step. These hooks include: 

4153 

4154 * :meth:`.MapperEvents.before_configured` - called once before 

4155 :func:`.configure_mappers` or :meth:`_orm.registry.configure` does any 

4156 work; this can be used to establish additional options, properties, or 

4157 related mappings before the operation proceeds. 

4158 

4159 * :meth:`.RegistryEvents.before_configured` - Like 

4160 :meth:`.MapperEvents.before_configured`, but local to a specific 

4161 :class:`_orm.registry`. 

4162 

4163 .. versionadded:: 2.1 - added :meth:`.RegistryEvents.before_configured` 

4164 

4165 * :meth:`.MapperEvents.mapper_configured` - called as each individual 

4166 :class:`_orm.Mapper` is configured within the process; will include all 

4167 mapper state except for backrefs set up by other mappers that are still 

4168 to be configured. 

4169 

4170 * :meth:`.MapperEvents.after_configured` - called once after 

4171 :func:`.configure_mappers` or :meth:`_orm.registry.configure` is 

4172 complete; at this stage, all :class:`_orm.Mapper` objects that fall 

4173 within the scope of the configuration operation will be fully configured. 

4174 Note that the calling application may still have other mappings that 

4175 haven't been produced yet, such as if they are in modules as yet 

4176 unimported, and may also have mappings that are still to be configured, 

4177 if they are in other :class:`_orm.registry` collections not part of the 

4178 current scope of configuration. 

4179 

4180 * :meth:`.RegistryEvents.after_configured` - Like 

4181 :meth:`.MapperEvents.after_configured`, but local to a specific 

4182 :class:`_orm.registry`. 

4183 

4184 .. versionadded:: 2.1 - added :meth:`.RegistryEvents.after_configured` 

4185 

4186 """ 

4187 

4188 _configure_registries(_all_registries(), cascade=True) 

4189 

4190 

4191def _configure_registries( 

4192 registries: Set[_RegistryType], cascade: bool 

4193) -> None: 

4194 for reg in registries: 

4195 if reg._new_mappers: 

4196 break 

4197 else: 

4198 return 

4199 

4200 with _CONFIGURE_MUTEX: 

4201 global _already_compiling 

4202 if _already_compiling: 

4203 return 

4204 _already_compiling = True 

4205 try: 

4206 # double-check inside mutex 

4207 for reg in registries: 

4208 if reg._new_mappers: 

4209 break 

4210 else: 

4211 return 

4212 

4213 Mapper.dispatch._for_class(Mapper).before_configured() # type: ignore[arg-type, call-arg, misc] # noqa: E501 

4214 

4215 # initialize properties on all mappers 

4216 # note that _mapper_registry is unordered, which 

4217 # may randomly conceal/reveal issues related to 

4218 # the order of mapper compilation 

4219 

4220 registries_configured = list( 

4221 _do_configure_registries(registries, cascade) 

4222 ) 

4223 

4224 finally: 

4225 _already_compiling = False 

4226 for reg in registries_configured: 

4227 reg.dispatch.after_configured(reg) 

4228 Mapper.dispatch._for_class(Mapper).after_configured() # type: ignore[arg-type, call-arg, misc] # noqa: E501 

4229 

4230 

4231@util.preload_module("sqlalchemy.orm.decl_api") 

4232def _do_configure_registries( 

4233 registries: Set[_RegistryType], cascade: bool 

4234) -> Iterator[registry]: 

4235 registry = util.preloaded.orm_decl_api.registry 

4236 

4237 orig = set(registries) 

4238 

4239 for reg in registry._recurse_with_dependencies(registries): 

4240 if reg._new_mappers: 

4241 reg.dispatch.before_configured(reg) 

4242 

4243 has_skip = False 

4244 

4245 for mapper in reg._mappers_to_configure(): 

4246 run_configure = None 

4247 

4248 for fn in mapper.dispatch.before_mapper_configured: 

4249 run_configure = fn(mapper, mapper.class_) 

4250 if run_configure is EXT_SKIP: 

4251 has_skip = True 

4252 break 

4253 if run_configure is EXT_SKIP: 

4254 continue 

4255 

4256 if getattr(mapper, "_configure_failed", False): 

4257 e = sa_exc.InvalidRequestError( 

4258 "One or more mappers failed to initialize - " 

4259 "can't proceed with initialization of other " 

4260 "mappers. Triggering mapper: '%s'. " 

4261 "Original exception was: %s" 

4262 % (mapper, mapper._configure_failed) 

4263 ) 

4264 e._configure_failed = mapper._configure_failed # type: ignore[attr-defined] # noqa: E501 

4265 raise e 

4266 

4267 if not mapper.configured: 

4268 try: 

4269 mapper._post_configure_properties() 

4270 mapper._expire_memoizations() 

4271 mapper.dispatch.mapper_configured(mapper, mapper.class_) 

4272 except Exception: 

4273 exc = sys.exc_info()[1] 

4274 if not hasattr(exc, "_configure_failed"): 

4275 mapper._configure_failed = exc 

4276 raise 

4277 

4278 if reg._new_mappers: 

4279 yield reg 

4280 if not has_skip: 

4281 reg._new_mappers = False 

4282 

4283 if not cascade and reg._dependencies.difference(orig): 

4284 raise sa_exc.InvalidRequestError( 

4285 "configure was called with cascade=False but " 

4286 "additional registries remain" 

4287 ) 

4288 

4289 

4290@util.preload_module("sqlalchemy.orm.decl_api") 

4291def _dispose_registries(registries: Set[_RegistryType], cascade: bool) -> None: 

4292 registry = util.preloaded.orm_decl_api.registry 

4293 

4294 orig = set(registries) 

4295 

4296 for reg in registry._recurse_with_dependents(registries): 

4297 if not cascade and reg._dependents.difference(orig): 

4298 raise sa_exc.InvalidRequestError( 

4299 "Registry has dependent registries that are not disposed; " 

4300 "pass cascade=True to clear these also" 

4301 ) 

4302 

4303 while reg._managers: 

4304 try: 

4305 manager, _ = reg._managers.popitem() 

4306 except KeyError: 

4307 # guard against race between while and popitem 

4308 pass 

4309 else: 

4310 reg._dispose_manager_and_mapper(manager) 

4311 

4312 reg._dependents.clear() 

4313 for dep in reg._dependencies: 

4314 dep._dependents.discard(reg) 

4315 reg._dependencies.clear() 

4316 # this wasn't done in the 1.3 clear_mappers() and in fact it 

4317 # was a bug, as it could cause configure_mappers() to invoke 

4318 # the "before_configured" event even though mappers had all been 

4319 # disposed. 

4320 reg._new_mappers = False 

4321 

4322 

4323def reconstructor(fn: _Fn) -> _Fn: 

4324 """Decorate a method as the 'reconstructor' hook. 

4325 

4326 Designates a single method as the "reconstructor", an ``__init__``-like 

4327 method that will be called by the ORM after the instance has been 

4328 loaded from the database or otherwise reconstituted. 

4329 

4330 .. tip:: 

4331 

4332 The :func:`_orm.reconstructor` decorator makes use of the 

4333 :meth:`_orm.InstanceEvents.load` event hook, which can be 

4334 used directly. 

4335 

4336 The reconstructor will be invoked with no arguments. Scalar 

4337 (non-collection) database-mapped attributes of the instance will 

4338 be available for use within the function. Eagerly-loaded 

4339 collections are generally not yet available and will usually only 

4340 contain the first element. ORM state changes made to objects at 

4341 this stage will not be recorded for the next flush() operation, so 

4342 the activity within a reconstructor should be conservative. 

4343 

4344 .. seealso:: 

4345 

4346 :meth:`.InstanceEvents.load` 

4347 

4348 """ 

4349 fn.__sa_reconstructor__ = True # type: ignore[attr-defined] 

4350 return fn 

4351 

4352 

4353def validates( 

4354 *names: str, include_removes: bool = False, include_backrefs: bool = True 

4355) -> Callable[[_Fn], _Fn]: 

4356 r"""Decorate a method as a 'validator' for one or more named properties. 

4357 

4358 Designates a method as a validator, a method which receives the 

4359 name of the attribute as well as a value to be assigned, or in the 

4360 case of a collection, the value to be added to the collection. 

4361 The function can then raise validation exceptions to halt the 

4362 process from continuing (where Python's built-in ``ValueError`` 

4363 and ``AssertionError`` exceptions are reasonable choices), or can 

4364 modify or replace the value before proceeding. The function should 

4365 otherwise return the given value. 

4366 

4367 When a subclass overrides a validator for the same attribute using 

4368 the same method name, only the subclass validator is invoked. The 

4369 subclass validator may call ``super()`` to also invoke the parent 

4370 class validator. 

4371 

4372 A subclass that overrides a validator using the same method name 

4373 as the parent now replaces the parent validator entirely, rather 

4374 than the parent validator being invoked unconditionally. The 

4375 subclass validator may opt in to the parent's behavior by calling 

4376 ``super()``. See :ref:`validators_subclass_override` for 

4377 background on this change. 

4378 

4379 .. versionchanged:: 2.1 Added support for overriding of validators 

4380 on subclasses. 

4381 

4382 Note that a validator for a collection **cannot** issue a load of that 

4383 collection within the validation routine - this usage raises 

4384 an assertion to avoid recursion overflows. This is a reentrant 

4385 condition which is not supported. 

4386 

4387 :param \*names: list of attribute names to be validated. 

4388 :param include_removes: if True, "remove" events will be 

4389 sent as well - the validation function must accept an additional 

4390 argument "is_remove" which will be a boolean. 

4391 

4392 :param include_backrefs: defaults to ``True``; if ``False``, the 

4393 validation function will not emit if the originator is an attribute 

4394 event related via a backref. This can be used for bi-directional 

4395 :func:`.validates` usage where only one validator should emit per 

4396 attribute operation. 

4397 

4398 .. versionchanged:: 2.0.16 This parameter inadvertently defaulted to 

4399 ``False`` for releases 2.0.0 through 2.0.15. Its correct default 

4400 of ``True`` is restored in 2.0.16. 

4401 

4402 .. seealso:: 

4403 

4404 :ref:`simple_validators` - usage examples for :func:`.validates` 

4405 

4406 :ref:`validators_subclass_override` - overriding validators in 

4407 subclasses 

4408 

4409 """ 

4410 

4411 def wrap(fn: _Fn) -> _Fn: 

4412 fn.__sa_validators__ = names # type: ignore[attr-defined] 

4413 fn.__sa_validation_opts__ = { # type: ignore[attr-defined] 

4414 "include_removes": include_removes, 

4415 "include_backrefs": include_backrefs, 

4416 } 

4417 return fn 

4418 

4419 return wrap 

4420 

4421 

4422def _event_on_load(state, ctx): 

4423 instrumenting_mapper = state.manager.mapper 

4424 

4425 if instrumenting_mapper._reconstructor: 

4426 instrumenting_mapper._reconstructor(state.obj()) 

4427 

4428 

4429def _event_on_init(state, args, kwargs): 

4430 """Run init_instance hooks. 

4431 

4432 This also includes mapper compilation, normally not needed 

4433 here but helps with some piecemeal configuration 

4434 scenarios (such as in the ORM tutorial). 

4435 

4436 """ 

4437 

4438 instrumenting_mapper = state.manager.mapper 

4439 if instrumenting_mapper: 

4440 instrumenting_mapper._check_configure() 

4441 if instrumenting_mapper._set_polymorphic_identity: 

4442 instrumenting_mapper._set_polymorphic_identity(state) 

4443 

4444 

4445class _ColumnMapping(Dict["ColumnElement[Any]", "MapperProperty[Any]"]): 

4446 """Error reporting helper for mapper._columntoproperty.""" 

4447 

4448 __slots__ = ("mapper",) 

4449 

4450 def __init__(self, mapper): 

4451 # TODO: weakref would be a good idea here 

4452 self.mapper = mapper 

4453 

4454 def __missing__(self, column): 

4455 prop = self.mapper._props.get(column) 

4456 if prop: 

4457 raise orm_exc.UnmappedColumnError( 

4458 "Column '%s.%s' is not available, due to " 

4459 "conflicting property '%s':%r" 

4460 % (column.table.name, column.name, column.key, prop) 

4461 ) 

4462 raise orm_exc.UnmappedColumnError( 

4463 "No column %s is configured on mapper %s..." 

4464 % (column, self.mapper) 

4465 )