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

1460 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 .decl_base import _DeclarativeMapperConfig 

103 from .dependency import _DependencyProcessor 

104 from .descriptor_props import CompositeProperty 

105 from .descriptor_props import SynonymProperty 

106 from .events import MapperEvents 

107 from .instrumentation import ClassManager 

108 from .path_registry import _CachingEntityRegistry 

109 from .properties import ColumnProperty 

110 from .relationships import RelationshipProperty 

111 from .state import InstanceState 

112 from .util import ORMAdapter 

113 from ..engine import Row 

114 from ..engine import RowMapping 

115 from ..sql._typing import _ColumnExpressionArgument 

116 from ..sql._typing import _EquivalentColumnMap 

117 from ..sql.base import _EntityNamespace 

118 from ..sql.base import ReadOnlyColumnCollection 

119 from ..sql.elements import ColumnClause 

120 from ..sql.elements import ColumnElement 

121 from ..sql.selectable import FromClause 

122 from ..util import OrderedSet 

123 

124 

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

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

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

128 

129 

130_WithPolymorphicArg = Union[ 

131 Literal["*"], 

132 Tuple[ 

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

134 Optional["FromClause"], 

135 ], 

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

137] 

138 

139 

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

141 weakref.WeakKeyDictionary() 

142) 

143 

144 

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

146 with _CONFIGURE_MUTEX: 

147 return set(_mapper_registries) 

148 

149 

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

151 for reg in _all_registries(): 

152 yield from reg._mappers_to_configure() 

153 

154 

155_already_compiling = False 

156 

157 

158# a constant returned by _get_attr_by_column to indicate 

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

160# column 

161NO_ATTRIBUTE = util.symbol("NO_ATTRIBUTE") 

162 

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

164_CONFIGURE_MUTEX = threading.RLock() 

165 

166 

167@inspection._self_inspects 

168@log.class_logger 

169class Mapper( 

170 ORMFromClauseRole, 

171 ORMEntityColumnsClauseRole[_O], 

172 MemoizedHasCacheKey, 

173 InspectionAttr, 

174 log.Identified, 

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

176 EventTarget, 

177 Generic[_O], 

178): 

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

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

181 proceed. 

182 

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

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

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

186 :ref:`orm_mapping_classes_toplevel`. 

187 

188 """ 

189 

190 dispatch: dispatcher[Mapper[_O]] 

191 

192 _dispose_called = False 

193 _configure_failed: Any = False 

194 _ready_for_configure = False 

195 

196 def __init__( 

197 self, 

198 class_: Type[_O], 

199 local_table: Optional[FromClause] = None, 

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

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

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

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

204 inherit_foreign_keys: Optional[ 

205 Sequence[_ORMColumnExprArgument[Any]] 

206 ] = None, 

207 always_refresh: bool = False, 

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

209 version_id_generator: Optional[ 

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

211 ] = None, 

212 polymorphic_on: Optional[ 

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

214 ] = None, 

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

216 polymorphic_identity: Optional[Any] = None, 

217 concrete: bool = False, 

218 with_polymorphic: Optional[_WithPolymorphicArg] = None, 

219 polymorphic_abstract: bool = False, 

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

221 allow_partial_pks: bool = True, 

222 batch: bool = True, 

223 column_prefix: Optional[str] = None, 

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

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

226 passive_updates: bool = True, 

227 passive_deletes: bool = False, 

228 confirm_deleted_rows: bool = True, 

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

230 legacy_is_orphan: bool = False, 

231 _compiled_cache_size: int = 100, 

232 ): 

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

234 

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

236 is normally invoked through the 

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

238 :ref:`Declarative <orm_declarative_mapping>` or 

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

240 

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

242 removed; for a classical mapping configuration, use the 

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

244 

245 Parameters documented below may be passed to either the 

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

247 ``__mapper_args__`` declarative class attribute described at 

248 :ref:`orm_declarative_mapper_options`. 

249 

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

251 this argument is automatically passed as the declared class 

252 itself. 

253 

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

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

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

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

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

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

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

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

262 present. 

263 

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

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

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

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

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

269 inheritance scheme which uses 

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

271 

272 .. versionadded:: 2.0 

273 

274 .. seealso:: 

275 

276 :ref:`orm_inheritance_abstract_poly` 

277 

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

279 class will overwrite all data within object instances that already 

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

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

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

283 :meth:`_query.Query.populate_existing`. 

284 

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

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

287 possibly existing within the database. This affects whether a 

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

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

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

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

292 

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

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

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

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

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

298 

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

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

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

302 has partial NULL values will not be emitted. 

303 

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

305 of multiple entities can be batched together for efficiency. 

306 Setting to False indicates 

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

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

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

310 in between individual row persistence operations. 

311 

312 :param column_prefix: A string which will be prepended 

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

314 objects are automatically assigned as attributes to the 

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

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

317 dictionary. 

318 

319 This parameter is typically useful with imperative mappings 

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

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

322 ``user_id``, ``user_name``, and ``password``:: 

323 

324 class User(Base): 

325 __table__ = user_table 

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

327 

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

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

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

331 

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

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

334 approach to automating a naming scheme is to intercept the 

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

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

337 pattern. 

338 

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

340 table inheritance with its parent mapper. 

341 

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

343 

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

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

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

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

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

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

350 exception in a future release. 

351 

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

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

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

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

356 are needed immediately before the flush completes. 

357 

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

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

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

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

362 

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

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

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

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

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

368 attributes are not to be accessed in any case. 

369 

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

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

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

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

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

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

376 defaults will not be fetched. 

377 

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

379 :paramref:`.Mapper.eager_defaults` 

380 

381 .. seealso:: 

382 

383 :ref:`orm_server_defaults` 

384 

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

386 INSERTed at once using the 

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

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

389 feature to be very performant on supporting backends. 

390 

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

392 be excluded from mapping. 

393 

394 .. seealso:: 

395 

396 :ref:`include_exclude_cols` 

397 

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

399 names to map. 

400 

401 .. seealso:: 

402 

403 :ref:`include_exclude_cols` 

404 

405 :param inherits: A mapped class or the corresponding 

406 :class:`_orm.Mapper` 

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

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

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

410 is passed automatically as a result of the natural class 

411 hierarchy of the declared classes. 

412 

413 .. seealso:: 

414 

415 :ref:`inheritance_toplevel` 

416 

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

418 expression which will 

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

420 between the two tables. 

421 

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

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

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

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

426 

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

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

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

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

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

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

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

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

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

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

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

438 orphan object has been flushed yet or not. 

439 

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

441 for more detail on this change. 

442 

443 :param passive_deletes: Indicates DELETE behavior of foreign key 

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

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

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

447 on the superclass mapper. 

448 

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

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

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

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

453 superclass table, and not this table. 

454 

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

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

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

458 validate these attributes; note that the primary key columns 

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

460 the object as a whole. 

461 

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

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

464 to specify passive_deletes without this taking effect for 

465 all subclass mappers. 

466 

467 .. seealso:: 

468 

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

470 used with :func:`_orm.relationship` 

471 

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

473 CASCADE for joined-table inheritance mappers 

474 

475 :param passive_updates: Indicates UPDATE behavior of foreign key 

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

477 inheritance mapping. Defaults to ``True``. 

478 

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

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

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

482 on joined-table rows. 

483 

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

485 referential integrity and will not be issuing its own CASCADE 

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

487 emit an UPDATE statement for the dependent columns during a 

488 primary key change. 

489 

490 .. seealso:: 

491 

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

493 used with :func:`_orm.relationship` 

494 

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

496 CASCADE for joined-table inheritance mappers 

497 

498 :param polymorphic_load: Specifies "polymorphic loading" behavior 

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

500 table inheritance only). Valid values are: 

501 

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

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

504 in a SELECT query against the base. 

505 

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

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

508 the columns specific to this subclass. The SELECT uses 

509 IN to fetch multiple subclasses at once. 

510 

511 .. seealso:: 

512 

513 :ref:`with_polymorphic_mapper_config` 

514 

515 :ref:`polymorphic_selectin` 

516 

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

518 SQL expression used to determine the target class for an 

519 incoming row, when inheriting classes are present. 

520 

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

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

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

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

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

526 

527 class Employee(Base): 

528 __tablename__ = "employee" 

529 

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

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

532 

533 __mapper_args__ = { 

534 "polymorphic_on": discriminator, 

535 "polymorphic_identity": "employee", 

536 } 

537 

538 It may also be specified 

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

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

541 approach:: 

542 

543 class Employee(Base): 

544 __tablename__ = "employee" 

545 

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

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

548 

549 __mapper_args__ = { 

550 "polymorphic_on": case( 

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

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

553 else_="employee", 

554 ), 

555 "polymorphic_identity": "employee", 

556 } 

557 

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

559 which is of particular use when using annotated column 

560 configurations:: 

561 

562 class Employee(Base): 

563 __tablename__ = "employee" 

564 

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

566 discriminator: Mapped[str] 

567 

568 __mapper_args__ = { 

569 "polymorphic_on": "discriminator", 

570 "polymorphic_identity": "employee", 

571 } 

572 

573 When setting ``polymorphic_on`` to reference an 

574 attribute or expression that's not present in the 

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

576 of the discriminator should be persisted to the database, 

577 the value of the 

578 discriminator is not automatically set on new 

579 instances; this must be handled by the user, 

580 either through manual means or via event listeners. 

581 A typical approach to establishing such a listener 

582 looks like:: 

583 

584 from sqlalchemy import event 

585 from sqlalchemy.orm import object_mapper 

586 

587 

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

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

590 mapper = object_mapper(instance) 

591 instance.discriminator = mapper.polymorphic_identity 

592 

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

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

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

596 in the database. 

597 

598 .. warning:: 

599 

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

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

602 columns are not yet supported. 

603 

604 .. seealso:: 

605 

606 :ref:`inheritance_toplevel` 

607 

608 :param polymorphic_identity: Specifies the value which 

609 identifies this particular class as returned by the column expression 

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

611 rows are received, the value corresponding to the 

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

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

614 reconstructed object. 

615 

616 .. seealso:: 

617 

618 :ref:`inheritance_toplevel` 

619 

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

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

622 persistence behavior of that attribute. Note that 

623 :class:`_schema.Column` 

624 objects present in 

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

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

627 When using Declarative, this argument is passed automatically, 

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

629 in the declared class body. 

630 

631 .. seealso:: 

632 

633 :ref:`orm_mapping_properties` - in the 

634 :ref:`orm_mapping_classes_toplevel` 

635 

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

637 objects, or alternatively string names of attribute names which 

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

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

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

641 can be overridden here. 

642 

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

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

645 

646 .. seealso:: 

647 

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

649 

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

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

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

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

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

655 version id, a 

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

657 thrown. 

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

659 unless ``version_id_generator`` specifies an alternative version 

660 generator. 

661 

662 .. seealso:: 

663 

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

665 and rationale. 

666 

667 :param version_id_generator: Define how new version ids should 

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

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

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

671 

672 def generate_version(version): 

673 return next_version 

674 

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

676 or programmatic versioning schemes outside of the version id 

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

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

679 of important points when using this option. 

680 

681 .. seealso:: 

682 

683 :ref:`custom_version_counter` 

684 

685 :ref:`server_side_version_counter` 

686 

687 

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

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

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

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

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

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

694 loaded immediately. The second tuple argument <selectable> 

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

696 classes. 

697 

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

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

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

701 indicating polymorphic loading styles. 

702 

703 .. seealso:: 

704 

705 :ref:`with_polymorphic_mapper_config` 

706 

707 """ 

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

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

710 self.class_.__module__, 

711 self.class_.__name__, 

712 ) 

713 

714 self._primary_key_argument = util.to_list(primary_key) 

715 

716 self.always_refresh = always_refresh 

717 

718 if isinstance(version_id_col, MapperProperty): 

719 self.version_id_prop = version_id_col 

720 self.version_id_col = None 

721 else: 

722 self.version_id_col = ( 

723 coercions.expect( 

724 roles.ColumnArgumentOrKeyRole, 

725 version_id_col, 

726 argname="version_id_col", 

727 ) 

728 if version_id_col is not None 

729 else None 

730 ) 

731 

732 if version_id_generator is False: 

733 self.version_id_generator = False 

734 elif version_id_generator is None: 

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

736 else: 

737 self.version_id_generator = version_id_generator 

738 

739 self.concrete = concrete 

740 self.single = False 

741 

742 if inherits is not None: 

743 self.inherits = _parse_mapper_argument(inherits) 

744 else: 

745 self.inherits = None 

746 

747 if local_table is not None: 

748 self.local_table = coercions.expect( 

749 roles.FromClauseRole, 

750 local_table, 

751 disable_inspection=True, 

752 argname="local_table", 

753 ) 

754 elif self.inherits: 

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

756 # .local_table need not be Optional 

757 self.local_table = self.inherits.local_table 

758 self.single = True 

759 else: 

760 raise sa_exc.ArgumentError( 

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

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

763 ) 

764 

765 if inherit_condition is not None: 

766 self.inherit_condition = coercions.expect( 

767 roles.OnClauseRole, inherit_condition 

768 ) 

769 else: 

770 self.inherit_condition = None 

771 

772 self.inherit_foreign_keys = inherit_foreign_keys 

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

774 self._delete_orphans = [] 

775 self.batch = batch 

776 self.eager_defaults = eager_defaults 

777 self.column_prefix = column_prefix 

778 

779 # interim - polymorphic_on is further refined in 

780 # _configure_polymorphic_setter 

781 self.polymorphic_on = ( 

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

783 roles.ColumnArgumentOrKeyRole, 

784 polymorphic_on, 

785 argname="polymorphic_on", 

786 ) 

787 if polymorphic_on is not None 

788 else None 

789 ) 

790 self.polymorphic_abstract = polymorphic_abstract 

791 self._dependency_processors = [] 

792 self.validators = util.EMPTY_DICT 

793 self.passive_updates = passive_updates 

794 self.passive_deletes = passive_deletes 

795 self.legacy_is_orphan = legacy_is_orphan 

796 self._clause_adapter = None 

797 self._requires_row_aliasing = False 

798 self._inherits_equated_pairs = None 

799 self._memoized_values = {} 

800 self._compiled_cache_size = _compiled_cache_size 

801 self._reconstructor = None 

802 self.allow_partial_pks = allow_partial_pks 

803 

804 if self.inherits and not self.concrete: 

805 self.confirm_deleted_rows = False 

806 else: 

807 self.confirm_deleted_rows = confirm_deleted_rows 

808 

809 self._set_with_polymorphic(with_polymorphic) 

810 self.polymorphic_load = polymorphic_load 

811 

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

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

814 # the object instance for that row. 

815 self.polymorphic_identity = polymorphic_identity 

816 

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

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

819 # upon a select operation. 

820 if _polymorphic_map is None: 

821 self.polymorphic_map = {} 

822 else: 

823 self.polymorphic_map = _polymorphic_map 

824 

825 if include_properties is not None: 

826 self.include_properties = util.to_set(include_properties) 

827 else: 

828 self.include_properties = None 

829 if exclude_properties: 

830 self.exclude_properties = util.to_set(exclude_properties) 

831 else: 

832 self.exclude_properties = None 

833 

834 # prevent this mapper from being constructed 

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

836 # configure_mappers() until construction succeeds) 

837 with _CONFIGURE_MUTEX: 

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

839 class_, self 

840 ) 

841 self._configure_inheritance() 

842 self._configure_class_instrumentation() 

843 self._configure_properties() 

844 self._configure_polymorphic_setter() 

845 self._configure_pks() 

846 self.registry._flag_new_mapper(self) 

847 self._log("constructed") 

848 self._expire_memoizations() 

849 

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

851 

852 def _prefer_eager_defaults(self, dialect, table): 

853 if self.eager_defaults == "auto": 

854 if not table.implicit_returning: 

855 return False 

856 

857 return ( 

858 table in self._server_default_col_keys 

859 and dialect.insert_executemany_returning 

860 ) 

861 else: 

862 return self.eager_defaults 

863 

864 def _gen_cache_key(self, anon_map, bindparams): 

865 return (self,) 

866 

867 # ### BEGIN 

868 # ATTRIBUTE DECLARATIONS START HERE 

869 

870 is_mapper = True 

871 """Part of the inspection API.""" 

872 

873 represents_outer_join = False 

874 

875 registry: _RegistryType 

876 

877 @property 

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

879 """Part of the inspection API. 

880 

881 Returns self. 

882 

883 """ 

884 return self 

885 

886 @property 

887 def entity(self): 

888 r"""Part of the inspection API. 

889 

890 Returns self.class\_. 

891 

892 """ 

893 return self.class_ 

894 

895 class_: Type[_O] 

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

897 

898 _identity_class: Type[_O] 

899 

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

901 _dependency_processors: List[_DependencyProcessor] 

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

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

904 _all_tables: Set[TableClause] 

905 _polymorphic_attr_key: Optional[str] 

906 

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

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

909 

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

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

912 

913 _columntoproperty: _ColumnMapping 

914 

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

916 _validate_polymorphic_identity: Optional[ 

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

918 ] 

919 

920 tables: Sequence[TableClause] 

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

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

923 is aware of. 

924 

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

926 :class:`_expression.Alias` 

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

928 :class:`_schema.Table` 

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

930 

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

932 Behavior is undefined if directly modified. 

933 

934 """ 

935 

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

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

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

939 

940 The dictionary contains string attribute names as keys 

941 mapped to the actual validation method. 

942 

943 """ 

944 

945 always_refresh: bool 

946 allow_partial_pks: bool 

947 version_id_col: Optional[ColumnElement[Any]] 

948 

949 with_polymorphic: Optional[ 

950 Tuple[ 

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

952 Optional[FromClause], 

953 ] 

954 ] 

955 

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

957 

958 local_table: FromClause 

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

960 :class:`_orm.Mapper` refers. 

961 

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

963 :class:`.FromClause`. 

964 

965 The "local" table is the 

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

967 managing from an attribute access and flush perspective. For 

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

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

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

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

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

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

974 

975 .. seealso:: 

976 

977 :attr:`_orm.Mapper.persist_selectable`. 

978 

979 :attr:`_orm.Mapper.selectable`. 

980 

981 """ 

982 

983 persist_selectable: FromClause 

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

985 is mapped. 

986 

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

988 :class:`.FromClause`. 

989 

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

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

992 represents the inheriting class hierarchy overall in an inheritance 

993 scenario. 

994 

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

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

997 alternate subquery used for selecting columns. 

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

999 will be written on a persist operation. 

1000 

1001 .. seealso:: 

1002 

1003 :attr:`_orm.Mapper.selectable`. 

1004 

1005 :attr:`_orm.Mapper.local_table`. 

1006 

1007 """ 

1008 

1009 inherits: Optional[Mapper[Any]] 

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

1011 inherits from, if any. 

1012 

1013 """ 

1014 

1015 inherit_condition: Optional[ColumnElement[bool]] 

1016 

1017 configured: bool = False 

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

1019 

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

1021 Behavior is undefined if directly modified. 

1022 

1023 .. seealso:: 

1024 

1025 :func:`.configure_mappers`. 

1026 

1027 """ 

1028 

1029 concrete: bool 

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

1031 inheritance mapper. 

1032 

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

1034 Behavior is undefined if directly modified. 

1035 

1036 """ 

1037 

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

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

1040 objects 

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

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

1043 

1044 This list is against the selectable in 

1045 :attr:`_orm.Mapper.persist_selectable`. 

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

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

1048 :class:`_expression.Join`, the 

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

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

1051 

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

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

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

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

1056 

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

1058 Behavior is undefined if directly modified. 

1059 

1060 """ 

1061 

1062 class_manager: ClassManager[_O] 

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

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

1065 

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

1067 Behavior is undefined if directly modified. 

1068 

1069 """ 

1070 

1071 single: bool 

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

1073 inheritance mapper. 

1074 

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

1076 

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

1078 Behavior is undefined if directly modified. 

1079 

1080 """ 

1081 

1082 polymorphic_on: Optional[KeyedColumnElement[Any]] 

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

1084 ``polymorphic_on`` argument 

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

1086 

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

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

1089 :func:`.cast`. 

1090 

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

1092 Behavior is undefined if directly modified. 

1093 

1094 """ 

1095 

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

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

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

1099 

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

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

1102 

1103 An inheritance chain of mappers will all reference the same 

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

1105 result rows to target mappers. 

1106 

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

1108 Behavior is undefined if directly modified. 

1109 

1110 """ 

1111 

1112 polymorphic_identity: Optional[Any] 

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

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

1115 

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

1117 comparable to the type of column represented by 

1118 :attr:`_orm.Mapper.polymorphic_on`. 

1119 

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

1121 Behavior is undefined if directly modified. 

1122 

1123 """ 

1124 

1125 base_mapper: Mapper[Any] 

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

1127 

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

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

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

1131 objects in the inheritance chain. 

1132 

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

1134 Behavior is undefined if directly modified. 

1135 

1136 """ 

1137 

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

1139 

1140 @util.memoized_property 

1141 def _path_registry(self) -> _CachingEntityRegistry: 

1142 return PathRegistry.per_mapper(self) 

1143 

1144 def _configure_inheritance(self): 

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

1146 being present.""" 

1147 

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

1149 self._inheriting_mappers = util.WeakSequence() 

1150 

1151 if self.inherits: 

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

1153 raise sa_exc.ArgumentError( 

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

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

1156 ) 

1157 

1158 self.dispatch._update(self.inherits.dispatch) 

1159 

1160 if self.single: 

1161 self.persist_selectable = self.inherits.persist_selectable 

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

1163 if self.concrete: 

1164 self.persist_selectable = self.local_table 

1165 for mapper in self.iterate_to_root(): 

1166 if mapper.polymorphic_on is not None: 

1167 mapper._requires_row_aliasing = True 

1168 else: 

1169 if self.inherit_condition is None: 

1170 # figure out inherit condition from our table to the 

1171 # immediate table of the inherited mapper, not its 

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

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

1174 try: 

1175 self.inherit_condition = sql_util.join_condition( 

1176 self.inherits.local_table, self.local_table 

1177 ) 

1178 except sa_exc.NoForeignKeysError as nfe: 

1179 assert self.inherits.local_table is not None 

1180 assert self.local_table is not None 

1181 raise sa_exc.NoForeignKeysError( 

1182 "Can't determine the inherit condition " 

1183 "between inherited table '%s' and " 

1184 "inheriting " 

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

1186 "foreign key relationships established. " 

1187 "Please ensure the inheriting table has " 

1188 "a foreign key relationship to the " 

1189 "inherited " 

1190 "table, or provide an " 

1191 "'on clause' using " 

1192 "the 'inherit_condition' mapper argument." 

1193 % ( 

1194 self.inherits.local_table.description, 

1195 self.local_table.description, 

1196 ) 

1197 ) from nfe 

1198 except sa_exc.AmbiguousForeignKeysError as afe: 

1199 assert self.inherits.local_table is not None 

1200 assert self.local_table is not None 

1201 raise sa_exc.AmbiguousForeignKeysError( 

1202 "Can't determine the inherit condition " 

1203 "between inherited table '%s' and " 

1204 "inheriting " 

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

1206 "foreign key relationship established. " 

1207 "Please specify the 'on clause' using " 

1208 "the 'inherit_condition' mapper argument." 

1209 % ( 

1210 self.inherits.local_table.description, 

1211 self.local_table.description, 

1212 ) 

1213 ) from afe 

1214 assert self.inherits.persist_selectable is not None 

1215 self.persist_selectable = sql.join( 

1216 self.inherits.persist_selectable, 

1217 self.local_table, 

1218 self.inherit_condition, 

1219 ) 

1220 

1221 fks = util.to_set(self.inherit_foreign_keys) 

1222 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 

1223 self.persist_selectable.onclause, 

1224 consider_as_foreign_keys=fks, 

1225 ) 

1226 else: 

1227 self.persist_selectable = self.local_table 

1228 

1229 if self.polymorphic_identity is None: 

1230 self._identity_class = self.class_ 

1231 

1232 if ( 

1233 not self.polymorphic_abstract 

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

1235 ): 

1236 util.warn( 

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

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

1239 f"'polymorphic_on' column of " 

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

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

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

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

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

1245 "class unmapped when using Declarative, set the " 

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

1247 ) 

1248 elif self.concrete: 

1249 self._identity_class = self.class_ 

1250 else: 

1251 self._identity_class = self.inherits._identity_class 

1252 

1253 if self.version_id_col is None: 

1254 self.version_id_col = self.inherits.version_id_col 

1255 self.version_id_generator = self.inherits.version_id_generator 

1256 elif ( 

1257 self.inherits.version_id_col is not None 

1258 and self.version_id_col is not self.inherits.version_id_col 

1259 ): 

1260 util.warn( 

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

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

1263 "the inherited versioning column. " 

1264 "version_id_col should only be specified on " 

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

1266 % ( 

1267 self.version_id_col.description, 

1268 self.inherits.version_id_col.description, 

1269 ) 

1270 ) 

1271 

1272 self.polymorphic_map = self.inherits.polymorphic_map 

1273 self.batch = self.inherits.batch 

1274 self.inherits._inheriting_mappers.append(self) 

1275 self.base_mapper = self.inherits.base_mapper 

1276 self.passive_updates = self.inherits.passive_updates 

1277 self.passive_deletes = ( 

1278 self.inherits.passive_deletes or self.passive_deletes 

1279 ) 

1280 self._all_tables = self.inherits._all_tables 

1281 

1282 if self.polymorphic_identity is not None: 

1283 if self.polymorphic_identity in self.polymorphic_map: 

1284 util.warn( 

1285 "Reassigning polymorphic association for identity %r " 

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

1287 "value for polymorphic_identity." 

1288 % ( 

1289 self.polymorphic_identity, 

1290 self.polymorphic_map[self.polymorphic_identity], 

1291 self, 

1292 self.polymorphic_identity, 

1293 ) 

1294 ) 

1295 self.polymorphic_map[self.polymorphic_identity] = self 

1296 

1297 if self.polymorphic_load and self.concrete: 

1298 raise sa_exc.ArgumentError( 

1299 "polymorphic_load is not currently supported " 

1300 "with concrete table inheritance" 

1301 ) 

1302 if self.polymorphic_load == "inline": 

1303 self.inherits._add_with_polymorphic_subclass(self) 

1304 elif self.polymorphic_load == "selectin": 

1305 pass 

1306 elif self.polymorphic_load is not None: 

1307 raise sa_exc.ArgumentError( 

1308 "unknown argument for polymorphic_load: %r" 

1309 % self.polymorphic_load 

1310 ) 

1311 

1312 else: 

1313 self._all_tables = set() 

1314 self.base_mapper = self 

1315 assert self.local_table is not None 

1316 self.persist_selectable = self.local_table 

1317 if self.polymorphic_identity is not None: 

1318 self.polymorphic_map[self.polymorphic_identity] = self 

1319 self._identity_class = self.class_ 

1320 

1321 if self.persist_selectable is None: 

1322 raise sa_exc.ArgumentError( 

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

1324 % self 

1325 ) 

1326 

1327 def _set_with_polymorphic( 

1328 self, with_polymorphic: Optional[_WithPolymorphicArg] 

1329 ) -> None: 

1330 if with_polymorphic == "*": 

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

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

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

1334 self.with_polymorphic = cast( 

1335 """Tuple[ 

1336 Union[ 

1337 Literal["*"], 

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

1339 ], 

1340 Optional["FromClause"], 

1341 ]""", 

1342 with_polymorphic, 

1343 ) 

1344 else: 

1345 self.with_polymorphic = (with_polymorphic, None) 

1346 elif with_polymorphic is not None: 

1347 raise sa_exc.ArgumentError( 

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

1349 ) 

1350 else: 

1351 self.with_polymorphic = None 

1352 

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

1354 self.with_polymorphic = ( 

1355 self.with_polymorphic[0], 

1356 coercions.expect( 

1357 roles.FromClauseRole, 

1358 self.with_polymorphic[1], 

1359 ), 

1360 ) 

1361 

1362 if self.configured: 

1363 self._expire_memoizations() 

1364 

1365 def _add_with_polymorphic_subclass(self, mapper): 

1366 subcl = mapper.class_ 

1367 if self.with_polymorphic is None: 

1368 self._set_with_polymorphic((subcl,)) 

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

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

1371 self._set_with_polymorphic( 

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

1373 ) 

1374 

1375 def _set_concrete_base(self, mapper): 

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

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

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

1379 

1380 assert self.concrete 

1381 assert not self.inherits 

1382 assert isinstance(mapper, Mapper) 

1383 self.inherits = mapper 

1384 self.inherits.polymorphic_map.update(self.polymorphic_map) 

1385 self.polymorphic_map = self.inherits.polymorphic_map 

1386 for mapper in self.iterate_to_root(): 

1387 if mapper.polymorphic_on is not None: 

1388 mapper._requires_row_aliasing = True 

1389 self.batch = self.inherits.batch 

1390 for mp in self.self_and_descendants: 

1391 mp.base_mapper = self.inherits.base_mapper 

1392 self.inherits._inheriting_mappers.append(self) 

1393 self.passive_updates = self.inherits.passive_updates 

1394 self._all_tables = self.inherits._all_tables 

1395 

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

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

1398 key, key, local=False, column=None 

1399 ): 

1400 self._adapt_inherited_property(key, prop, False) 

1401 

1402 def _set_polymorphic_on(self, polymorphic_on): 

1403 self.polymorphic_on = polymorphic_on 

1404 self._configure_polymorphic_setter(True) 

1405 

1406 def _configure_class_instrumentation(self): 

1407 """Associate this Mapper with the 

1408 given class and entity name. 

1409 

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

1411 name combination will return this mapper. Also decorate the 

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

1413 auto-session attachment logic. 

1414 

1415 """ 

1416 

1417 # we expect that declarative has applied the class manager 

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

1419 # this raises as of 2.0. 

1420 manager = attributes.opt_manager_of_class(self.class_) 

1421 

1422 if manager is None or not manager.registry: 

1423 raise sa_exc.InvalidRequestError( 

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

1425 "invoked directly outside of a declarative registry." 

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

1427 "function for a classical mapping." 

1428 ) 

1429 

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

1431 

1432 # this invokes the class_instrument event and sets up 

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

1434 # occur after the instrument_class event above. 

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

1436 # :( 

1437 

1438 manager = instrumentation.register_class( 

1439 self.class_, 

1440 mapper=self, 

1441 expired_attribute_loader=util.partial( 

1442 loading._load_scalar_attributes, self 

1443 ), 

1444 # finalize flag means instrument the __init__ method 

1445 # and call the class_instrument event 

1446 finalize=True, 

1447 ) 

1448 

1449 self.class_manager = manager 

1450 

1451 assert manager.registry is not None 

1452 self.registry = manager.registry 

1453 

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

1455 # e_name None or not. 

1456 if manager.mapper is None: 

1457 return 

1458 

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

1460 

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

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

1463 method = method._sa_original_init 

1464 if hasattr(method, "__func__"): 

1465 method = method.__func__ 

1466 if callable(method): 

1467 if hasattr(method, "__sa_reconstructor__"): 

1468 self._reconstructor = method 

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

1470 elif hasattr(method, "__sa_validators__"): 

1471 validation_opts = method.__sa_validation_opts__ 

1472 for name in method.__sa_validators__: 

1473 if name in self.validators: 

1474 raise sa_exc.InvalidRequestError( 

1475 "A validation function for mapped " 

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

1477 % (name, self) 

1478 ) 

1479 self.validators = self.validators.union( 

1480 {name: (method, validation_opts)} 

1481 ) 

1482 

1483 def _set_dispose_flags(self) -> None: 

1484 self.configured = True 

1485 self._ready_for_configure = True 

1486 self._dispose_called = True 

1487 

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

1489 

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

1491 try: 

1492 prop = self._props[key] 

1493 except KeyError as err: 

1494 raise sa_exc.ArgumentError( 

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

1496 "no attribute is mapped to this name." 

1497 ) from err 

1498 try: 

1499 expr = prop.expression 

1500 except AttributeError as ae: 

1501 raise sa_exc.ArgumentError( 

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

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

1504 ) from ae 

1505 if not isinstance(expr, Column): 

1506 raise sa_exc.ArgumentError( 

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

1508 "property does not refer to a single " 

1509 "mapped Column" 

1510 ) 

1511 return expr 

1512 

1513 def _configure_pks(self) -> None: 

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

1515 

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

1517 

1518 self._pks_by_table = {} 

1519 self._cols_by_table = {} 

1520 

1521 all_cols = util.column_set( 

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

1523 ) 

1524 

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

1526 

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

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

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

1530 # ordering is important since it determines the ordering of 

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

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

1533 fc.primary_key 

1534 ).intersection( 

1535 pk_cols 

1536 ) 

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

1538 all_cols 

1539 ) 

1540 

1541 if self._primary_key_argument: 

1542 coerced_pk_arg = [ 

1543 ( 

1544 self._str_arg_to_mapped_col("primary_key", c) 

1545 if isinstance(c, str) 

1546 else c 

1547 ) 

1548 for c in ( 

1549 coercions.expect( 

1550 roles.DDLConstraintColumnRole, 

1551 coerce_pk, 

1552 argname="primary_key", 

1553 ) 

1554 for coerce_pk in self._primary_key_argument 

1555 ) 

1556 ] 

1557 else: 

1558 coerced_pk_arg = None 

1559 

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

1561 # primary key mappings 

1562 if coerced_pk_arg: 

1563 for k in coerced_pk_arg: 

1564 if k.table not in self._pks_by_table: 

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

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

1567 

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

1569 elif ( 

1570 self.persist_selectable not in self._pks_by_table 

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

1572 ): 

1573 raise sa_exc.ArgumentError( 

1574 "Mapper %s could not assemble any primary " 

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

1576 % (self, self.persist_selectable.description) 

1577 ) 

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

1579 self.local_table, schema.Table 

1580 ): 

1581 util.warn( 

1582 "Could not assemble any primary " 

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

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

1585 % self.local_table.description 

1586 ) 

1587 

1588 if ( 

1589 self.inherits 

1590 and not self.concrete 

1591 and not self._primary_key_argument 

1592 ): 

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

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

1595 self.primary_key = self.inherits.primary_key 

1596 else: 

1597 # determine primary key from argument or persist_selectable pks 

1598 primary_key: Collection[ColumnElement[Any]] 

1599 

1600 if coerced_pk_arg: 

1601 primary_key = [ 

1602 cc if cc is not None else c 

1603 for cc, c in ( 

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

1605 for c in coerced_pk_arg 

1606 ) 

1607 ] 

1608 else: 

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

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

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

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

1613 primary_key = sql_util.reduce_columns( 

1614 self._pks_by_table[self.persist_selectable], 

1615 ignore_nonexistent_tables=True, 

1616 ) 

1617 

1618 if len(primary_key) == 0: 

1619 raise sa_exc.ArgumentError( 

1620 "Mapper %s could not assemble any primary " 

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

1622 % (self, self.persist_selectable.description) 

1623 ) 

1624 

1625 self.primary_key = tuple(primary_key) 

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

1627 

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

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

1630 self._readonly_props = { 

1631 self._columntoproperty[col] 

1632 for col in self._columntoproperty 

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

1634 and ( 

1635 not hasattr(col, "table") 

1636 or col.table not in self._cols_by_table 

1637 ) 

1638 } 

1639 

1640 def _configure_properties(self) -> None: 

1641 self._columns = sql_base.WriteableColumnCollection() 

1642 

1643 # object attribute names mapped to MapperProperty objects 

1644 self._props = util.OrderedDict() 

1645 

1646 # table columns mapped to MapperProperty 

1647 self._columntoproperty = _ColumnMapping(self) 

1648 

1649 explicit_col_props_by_column: Dict[ 

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

1651 ] = {} 

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

1653 

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

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

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

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

1658 if self._init_properties: 

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

1660 if not isinstance(prop_arg, MapperProperty): 

1661 possible_col_prop = self._make_prop_from_column( 

1662 key, prop_arg 

1663 ) 

1664 else: 

1665 possible_col_prop = prop_arg 

1666 

1667 if isinstance( 

1668 possible_col_prop, properties.ColumnProperty 

1669 ) and self._excluded_by_include_exclude( 

1670 key, possible_col_prop.columns 

1671 ): 

1672 # explicit properties are mapped regardless of 

1673 # include_properties / exclude_properties 

1674 self._warn_for_excluded_declared_attr( 

1675 key, possible_col_prop.columns 

1676 ) 

1677 

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

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

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

1681 # the Table. 

1682 

1683 _map_as_property_now = True 

1684 if isinstance(possible_col_prop, properties.ColumnProperty): 

1685 for given_col in possible_col_prop.columns: 

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

1687 _map_as_property_now = False 

1688 explicit_col_props_by_key[key] = possible_col_prop 

1689 explicit_col_props_by_column[given_col] = ( 

1690 key, 

1691 possible_col_prop, 

1692 ) 

1693 

1694 if _map_as_property_now: 

1695 self._configure_property( 

1696 key, 

1697 possible_col_prop, 

1698 init=False, 

1699 ) 

1700 

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

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

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

1704 if self.inherits: 

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

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

1707 continue 

1708 

1709 incoming_prop = explicit_col_props_by_key.get(key) 

1710 if incoming_prop: 

1711 new_prop = self._reconcile_prop_with_incoming_columns( 

1712 key, 

1713 inherited_prop, 

1714 warn_only=False, 

1715 incoming_prop=incoming_prop, 

1716 ) 

1717 explicit_col_props_by_key[key] = new_prop 

1718 

1719 for inc_col in incoming_prop.columns: 

1720 explicit_col_props_by_column[inc_col] = ( 

1721 key, 

1722 new_prop, 

1723 ) 

1724 elif key not in self._props: 

1725 self._adapt_inherited_property(key, inherited_prop, False) 

1726 

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

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

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

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

1731 # reconciliation against inherited columns occurs here also. 

1732 

1733 for column in self.persist_selectable.columns: 

1734 if column in explicit_col_props_by_column: 

1735 # column was explicitly passed to properties; configure 

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

1737 # Table / selectable 

1738 key, prop = explicit_col_props_by_column[column] 

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

1740 continue 

1741 

1742 elif column in self._columntoproperty: 

1743 continue 

1744 

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

1746 if self._should_exclude( 

1747 column.key, 

1748 column_key, 

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

1750 column=column, 

1751 ): 

1752 continue 

1753 

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

1755 # of the inheriting mapper 

1756 for mapper in self.iterate_to_root(): 

1757 if column in mapper._columntoproperty: 

1758 column_key = mapper._columntoproperty[column].key 

1759 

1760 self._configure_property( 

1761 column_key, 

1762 column, 

1763 init=False, 

1764 setparent=True, 

1765 ) 

1766 

1767 def _configure_polymorphic_setter(self, init=False): 

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

1769 'polymorphic_on' column, if applicable, and not 

1770 already generated by _configure_properties (which is typical). 

1771 

1772 Also create a setter function which will assign this 

1773 attribute to the value of the 'polymorphic_identity' 

1774 upon instance construction, also if applicable. This 

1775 routine will run when an instance is created. 

1776 

1777 """ 

1778 setter = False 

1779 polymorphic_key: Optional[str] = None 

1780 

1781 if self.polymorphic_on is not None: 

1782 setter = True 

1783 

1784 if isinstance(self.polymorphic_on, str): 

1785 # polymorphic_on specified as a string - link 

1786 # it to mapped ColumnProperty 

1787 try: 

1788 self.polymorphic_on = self._props[self.polymorphic_on] 

1789 except KeyError as err: 

1790 raise sa_exc.ArgumentError( 

1791 "Can't determine polymorphic_on " 

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

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

1794 ) from err 

1795 

1796 if self.polymorphic_on in self._columntoproperty: 

1797 # polymorphic_on is a column that is already mapped 

1798 # to a ColumnProperty 

1799 prop = self._columntoproperty[self.polymorphic_on] 

1800 elif isinstance(self.polymorphic_on, MapperProperty): 

1801 # polymorphic_on is directly a MapperProperty, 

1802 # ensure it's a ColumnProperty 

1803 if not isinstance( 

1804 self.polymorphic_on, properties.ColumnProperty 

1805 ): 

1806 raise sa_exc.ArgumentError( 

1807 "Only direct column-mapped " 

1808 "property or SQL expression " 

1809 "can be passed for polymorphic_on" 

1810 ) 

1811 prop = self.polymorphic_on 

1812 else: 

1813 # polymorphic_on is a Column or SQL expression and 

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

1815 # only present in the with_polymorphic selectable or 

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

1817 # hope is compatible with this mapper's persist_selectable 

1818 col = self.persist_selectable.corresponding_column( 

1819 self.polymorphic_on 

1820 ) 

1821 if col is None: 

1822 # polymorphic_on doesn't derive from any 

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

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

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

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

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

1828 # represented somehow in either persist_selectable or 

1829 # with_polymorphic. Otherwise as of 0.7.4 we 

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

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

1832 setter = False 

1833 instrument = False 

1834 col = self.polymorphic_on 

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

1836 self.with_polymorphic is None 

1837 or self.with_polymorphic[1] is None 

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

1839 is None 

1840 ): 

1841 raise sa_exc.InvalidRequestError( 

1842 "Could not map polymorphic_on column " 

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

1844 "loads will not function properly" 

1845 % col.description 

1846 ) 

1847 else: 

1848 # column/expression that polymorphic_on derives from 

1849 # is present in our mapped table 

1850 # and is probably mapped, but polymorphic_on itself 

1851 # is not. This happens when 

1852 # the polymorphic_on is only directly present in the 

1853 # with_polymorphic selectable, as when use 

1854 # polymorphic_union. 

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

1856 instrument = True 

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

1858 if key: 

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

1860 raise sa_exc.InvalidRequestError( 

1861 "Cannot exclude or override the " 

1862 "discriminator column %r" % key 

1863 ) 

1864 else: 

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

1866 key = col.key 

1867 

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

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

1870 

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

1872 # column in the property 

1873 self.polymorphic_on = prop.columns[0] 

1874 polymorphic_key = prop.key 

1875 else: 

1876 # no polymorphic_on was set. 

1877 # check inheriting mappers for one. 

1878 for mapper in self.iterate_to_root(): 

1879 # determine if polymorphic_on of the parent 

1880 # should be propagated here. If the col 

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

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

1883 # inheritance), we can use it 

1884 if mapper.polymorphic_on is not None: 

1885 if self.persist_selectable is mapper.persist_selectable: 

1886 self.polymorphic_on = mapper.polymorphic_on 

1887 else: 

1888 self.polymorphic_on = ( 

1889 self.persist_selectable 

1890 ).corresponding_column(mapper.polymorphic_on) 

1891 # we can use the parent mapper's _set_polymorphic_identity 

1892 # directly; it ensures the polymorphic_identity of the 

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

1894 if self.polymorphic_on is not None: 

1895 self._set_polymorphic_identity = ( 

1896 mapper._set_polymorphic_identity 

1897 ) 

1898 self._polymorphic_attr_key = ( 

1899 mapper._polymorphic_attr_key 

1900 ) 

1901 self._validate_polymorphic_identity = ( 

1902 mapper._validate_polymorphic_identity 

1903 ) 

1904 else: 

1905 self._set_polymorphic_identity = None 

1906 self._polymorphic_attr_key = None 

1907 return 

1908 

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

1910 raise sa_exc.InvalidRequestError( 

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

1912 "on a mapper hierarchy which includes the " 

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

1914 ) 

1915 

1916 if setter: 

1917 

1918 def _set_polymorphic_identity(state): 

1919 dict_ = state.dict 

1920 # TODO: what happens if polymorphic_on column attribute name 

1921 # does not match .key? 

1922 

1923 polymorphic_identity = ( 

1924 state.manager.mapper.polymorphic_identity 

1925 ) 

1926 if ( 

1927 polymorphic_identity is None 

1928 and state.manager.mapper.polymorphic_abstract 

1929 ): 

1930 raise sa_exc.InvalidRequestError( 

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

1932 "mapper is marked polymorphic_abstract=True" 

1933 ) 

1934 

1935 state.get_impl(polymorphic_key).set( 

1936 state, 

1937 dict_, 

1938 polymorphic_identity, 

1939 None, 

1940 ) 

1941 

1942 self._polymorphic_attr_key = polymorphic_key 

1943 

1944 def _validate_polymorphic_identity(mapper, state, dict_): 

1945 if ( 

1946 polymorphic_key in dict_ 

1947 and dict_[polymorphic_key] 

1948 not in mapper._acceptable_polymorphic_identities 

1949 ): 

1950 util.warn_limited( 

1951 "Flushing object %s with " 

1952 "incompatible polymorphic identity %r; the " 

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

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

1955 ) 

1956 

1957 self._set_polymorphic_identity = _set_polymorphic_identity 

1958 self._validate_polymorphic_identity = ( 

1959 _validate_polymorphic_identity 

1960 ) 

1961 else: 

1962 self._polymorphic_attr_key = None 

1963 self._set_polymorphic_identity = None 

1964 

1965 _validate_polymorphic_identity = None 

1966 

1967 @HasMemoized.memoized_attribute 

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

1969 pk_cols = util.column_set(self.primary_key) 

1970 equiv = self._equivalent_columns 

1971 for pk_col in pk_cols.intersection(equiv): 

1972 pk_cols.update(equiv[pk_col]) 

1973 return util.column_set( 

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

1975 ) 

1976 

1977 @HasMemoized.memoized_attribute 

1978 def _version_id_prop(self): 

1979 if self.version_id_col is not None: 

1980 return self._columntoproperty[self.version_id_col] 

1981 else: 

1982 return None 

1983 

1984 @HasMemoized.memoized_attribute 

1985 def _acceptable_polymorphic_identities(self): 

1986 identities = set() 

1987 

1988 stack = deque([self]) 

1989 while stack: 

1990 item = stack.popleft() 

1991 if item.persist_selectable is self.persist_selectable: 

1992 identities.add(item.polymorphic_identity) 

1993 stack.extend(item._inheriting_mappers) 

1994 

1995 return identities 

1996 

1997 @HasMemoized.memoized_attribute 

1998 def _prop_set(self): 

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

2000 

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

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

2003 descriptor_props = util.preloaded.orm_descriptor_props 

2004 

2005 if not self.concrete: 

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

2007 elif key not in self._props: 

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

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

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

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

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

2013 # descriptors that might have side effects when invoked. 

2014 implementing_attribute = self.class_manager._get_class_attr_mro( 

2015 key, prop 

2016 ) 

2017 if implementing_attribute is prop or ( 

2018 isinstance( 

2019 implementing_attribute, attributes.InstrumentedAttribute 

2020 ) 

2021 and implementing_attribute._parententity is prop.parent 

2022 ): 

2023 self._configure_property( 

2024 key, 

2025 descriptor_props.ConcreteInheritedProperty(), 

2026 init=init, 

2027 setparent=True, 

2028 ) 

2029 

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

2031 def _configure_property( 

2032 self, 

2033 key: str, 

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

2035 *, 

2036 init: bool = True, 

2037 setparent: bool = True, 

2038 warn_for_existing: bool = False, 

2039 ) -> MapperProperty[Any]: 

2040 descriptor_props = util.preloaded.orm_descriptor_props 

2041 self._log( 

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

2043 ) 

2044 

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

2046 # ensure a Column is turned into a ColumnProperty. 

2047 # see #12858 

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

2049 

2050 if not isinstance(prop_arg, MapperProperty): 

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

2052 key, prop_arg, early_setup 

2053 ) 

2054 else: 

2055 prop = prop_arg 

2056 

2057 if early_setup: 

2058 return prop 

2059 

2060 if isinstance(prop, properties.ColumnProperty): 

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

2062 

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

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

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

2066 if col is None and self.inherits: 

2067 path = [self] 

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

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

2070 if col is not None: 

2071 for m2 in path: 

2072 m2.persist_selectable._refresh_for_new_column(col) 

2073 col = self.persist_selectable.corresponding_column( 

2074 prop.columns[0] 

2075 ) 

2076 break 

2077 path.append(m) 

2078 

2079 # subquery expression, column not present in the mapped 

2080 # selectable. 

2081 if col is None: 

2082 col = prop.columns[0] 

2083 

2084 # column is coming in after _readonly_props was 

2085 # initialized; check for 'readonly' 

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

2087 not hasattr(col, "table") 

2088 or col.table not in self._cols_by_table 

2089 ): 

2090 self._readonly_props.add(prop) 

2091 

2092 else: 

2093 # if column is coming in after _cols_by_table was 

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

2095 if ( 

2096 hasattr(self, "_cols_by_table") 

2097 and col.table in self._cols_by_table 

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

2099 ): 

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

2101 

2102 # if this properties.ColumnProperty represents the "polymorphic 

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

2104 # columns in SELECT statements. 

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

2106 prop._is_polymorphic_discriminator = ( 

2107 col is self.polymorphic_on 

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

2109 ) 

2110 

2111 if isinstance(col, expression.Label): 

2112 # new in 1.4, get column property against expressions 

2113 # to be addressable in subqueries 

2114 col.key = col._tq_key_label = key 

2115 

2116 self._columns.add(col, key) 

2117 

2118 for col in prop.columns: 

2119 for proxy_col in col.proxy_set: 

2120 self._columntoproperty[proxy_col] = prop 

2121 

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

2123 util.warn( 

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

2125 "assigned to attribute " 

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

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

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

2129 ) 

2130 

2131 prop.key = key 

2132 

2133 if setparent: 

2134 prop.set_parent(self, init) 

2135 

2136 if key in self._props and getattr( 

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

2138 ): 

2139 syn = self._props[key]._mapped_by_synonym 

2140 raise sa_exc.ArgumentError( 

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

2142 "a ColumnProperty already exists keyed to the name " 

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

2144 ) 

2145 

2146 # replacement cases 

2147 

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

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

2150 if ( 

2151 key in self._props 

2152 and not isinstance( 

2153 self._props[key], descriptor_props.ConcreteInheritedProperty 

2154 ) 

2155 and not isinstance(prop, descriptor_props.SynonymProperty) 

2156 ): 

2157 if warn_for_existing: 

2158 util.warn_deprecated( 

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

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

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

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

2163 "release", 

2164 "2.0", 

2165 ) 

2166 oldprop = self._props[key] 

2167 self._path_registry.pop(oldprop, None) 

2168 

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

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

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

2172 # get replaced. 

2173 elif ( 

2174 warn_for_existing 

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

2176 and not isinstance(prop, descriptor_props.SynonymProperty) 

2177 and not isinstance( 

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

2179 descriptor_props.ConcreteInheritedProperty, 

2180 ) 

2181 ): 

2182 util.warn_deprecated( 

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

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

2185 "attribute of the same name. " 

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

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

2188 "release", 

2189 "2.0", 

2190 ) 

2191 

2192 self._props[key] = prop 

2193 

2194 prop.instrument_class(self) 

2195 

2196 for mapper in self._inheriting_mappers: 

2197 mapper._adapt_inherited_property(key, prop, init) 

2198 

2199 if init: 

2200 prop.init() 

2201 prop.post_instrument_class(self) 

2202 

2203 if self.configured: 

2204 self._expire_memoizations() 

2205 

2206 return prop 

2207 

2208 def _make_prop_from_column( 

2209 self, 

2210 key: str, 

2211 column: Union[ 

2212 Sequence[KeyedColumnElement[Any]], KeyedColumnElement[Any] 

2213 ], 

2214 ) -> ColumnProperty[Any]: 

2215 columns = util.to_list(column) 

2216 mapped_column = [] 

2217 for c in columns: 

2218 mc = self.persist_selectable.corresponding_column(c) 

2219 if mc is None: 

2220 mc = self.local_table.corresponding_column(c) 

2221 if mc is not None: 

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

2223 # mapped table, this corresponds to adding a 

2224 # column after the fact to the local table. 

2225 # [ticket:1523] 

2226 self.persist_selectable._refresh_for_new_column(mc) 

2227 mc = self.persist_selectable.corresponding_column(c) 

2228 if mc is None: 

2229 raise sa_exc.ArgumentError( 

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

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

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

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

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

2235 ) 

2236 mapped_column.append(mc) 

2237 return properties.ColumnProperty(*mapped_column) 

2238 

2239 def _reconcile_prop_with_incoming_columns( 

2240 self, 

2241 key: str, 

2242 existing_prop: MapperProperty[Any], 

2243 warn_only: bool, 

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

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

2246 ) -> ColumnProperty[Any]: 

2247 if incoming_prop and ( 

2248 self.concrete 

2249 or not isinstance(existing_prop, properties.ColumnProperty) 

2250 ): 

2251 return incoming_prop 

2252 

2253 existing_column = existing_prop.columns[0] 

2254 

2255 if incoming_prop and existing_column in incoming_prop.columns: 

2256 return incoming_prop 

2257 

2258 if incoming_prop is None: 

2259 assert single_column is not None 

2260 incoming_column = single_column 

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

2262 else: 

2263 assert single_column is None 

2264 incoming_column = incoming_prop.columns[0] 

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

2266 

2267 if ( 

2268 ( 

2269 not self._inherits_equated_pairs 

2270 or (equated_pair_key not in self._inherits_equated_pairs) 

2271 ) 

2272 and not existing_column.shares_lineage(incoming_column) 

2273 and existing_column is not self.version_id_col 

2274 and incoming_column is not self.version_id_col 

2275 ): 

2276 msg = ( 

2277 "Implicitly combining column %s with column " 

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

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

2280 "explicitly." 

2281 % ( 

2282 existing_prop.columns[-1], 

2283 incoming_column, 

2284 key, 

2285 ) 

2286 ) 

2287 if warn_only: 

2288 util.warn(msg) 

2289 else: 

2290 raise sa_exc.InvalidRequestError(msg) 

2291 

2292 # existing properties.ColumnProperty from an inheriting 

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

2294 new_prop = existing_prop.copy() 

2295 

2296 new_prop.columns.insert(0, incoming_column) 

2297 self._log( 

2298 "inserting column to existing list " 

2299 "in properties.ColumnProperty %s", 

2300 key, 

2301 ) 

2302 return new_prop # type: ignore[no-any-return] 

2303 

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

2305 def _property_from_column( 

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

2307 ) -> ColumnProperty[Any]: 

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

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

2310 

2311 descriptor_props = util.preloaded.orm_descriptor_props 

2312 

2313 if early_setup: 

2314 prop = None 

2315 else: 

2316 prop = self._props.get(key) 

2317 

2318 if isinstance(prop, properties.ColumnProperty): 

2319 return self._reconcile_prop_with_incoming_columns( 

2320 key, 

2321 prop, 

2322 single_column=column, 

2323 warn_only=prop.parent is not self, 

2324 ) 

2325 elif prop is None or isinstance( 

2326 prop, descriptor_props.ConcreteInheritedProperty 

2327 ): 

2328 return self._make_prop_from_column(key, column) 

2329 else: 

2330 raise sa_exc.ArgumentError( 

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

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

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

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

2335 "to remove all awareness of the column entirely " 

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

2337 "use the 'include_properties' or 'exclude_properties' " 

2338 "mapper arguments to control specifically which table " 

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

2340 ) 

2341 

2342 @util.langhelpers.tag_method_for_warnings( 

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

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

2345 "operation.", 

2346 sa_exc.SAWarning, 

2347 ) 

2348 def _check_configure(self) -> None: 

2349 if self.registry._new_mappers: 

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

2351 

2352 def _post_configure_properties(self) -> None: 

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

2354 attached to this mapper. 

2355 

2356 This is a deferred configuration step which is intended 

2357 to execute once all mappers have been constructed. 

2358 

2359 """ 

2360 

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

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

2363 for key, prop in l: 

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

2365 

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

2367 prop.init() 

2368 

2369 if prop._configure_finished: 

2370 prop.post_instrument_class(self) 

2371 

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

2373 self.configured = True 

2374 

2375 def add_properties(self, dict_of_properties): 

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

2377 using `add_property`. 

2378 

2379 """ 

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

2381 self.add_property(key, value) 

2382 

2383 def add_property( 

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

2385 ) -> None: 

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

2387 

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

2389 property to the initial properties dictionary sent to the 

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

2391 the given MapperProperty is configured immediately. 

2392 

2393 """ 

2394 prop = self._configure_property( 

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

2396 ) 

2397 assert isinstance(prop, MapperProperty) 

2398 self._init_properties[key] = prop 

2399 

2400 def _expire_memoizations(self) -> None: 

2401 for mapper in self.iterate_to_root(): 

2402 mapper._reset_memoizations() 

2403 

2404 @property 

2405 def _log_desc(self) -> str: 

2406 return ( 

2407 "(" 

2408 + self.class_.__name__ 

2409 + "|" 

2410 + ( 

2411 self.local_table is not None 

2412 and self.local_table.description 

2413 or str(self.local_table) 

2414 ) 

2415 + ")" 

2416 ) 

2417 

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

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

2420 

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

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

2423 

2424 def __repr__(self) -> str: 

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

2426 

2427 def __str__(self) -> str: 

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

2429 self.class_.__name__, 

2430 ( 

2431 self.local_table.description 

2432 if self.local_table is not None 

2433 else self.persist_selectable.description 

2434 ), 

2435 ) 

2436 

2437 def path_string(self) -> str: 

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

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

2440 

2441 """ 

2442 return self.class_.__name__ 

2443 

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

2445 orphan_possible = False 

2446 for mapper in self.iterate_to_root(): 

2447 for key, cls in mapper._delete_orphans: 

2448 orphan_possible = True 

2449 

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

2451 state, key, optimistic=state.has_identity 

2452 ) 

2453 

2454 if self.legacy_is_orphan and has_parent: 

2455 return False 

2456 elif not self.legacy_is_orphan and not has_parent: 

2457 return True 

2458 

2459 if self.legacy_is_orphan: 

2460 return orphan_possible 

2461 else: 

2462 return False 

2463 

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

2465 return key in self._props 

2466 

2467 def get_property( 

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

2469 ) -> MapperProperty[Any]: 

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

2471 

2472 if _configure_mappers: 

2473 self._check_configure() 

2474 

2475 try: 

2476 return self._props[key] 

2477 except KeyError as err: 

2478 raise sa_exc.InvalidRequestError( 

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

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

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

2482 ) from err 

2483 

2484 def get_property_by_column( 

2485 self, column: ColumnElement[_T] 

2486 ) -> MapperProperty[_T]: 

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

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

2489 

2490 return self._columntoproperty[column] 

2491 

2492 @HasMemoized.memoized_attribute 

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

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

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

2496 

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

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

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

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

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

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

2503 

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

2505 Behavior is undefined if directly modified. 

2506 

2507 """ 

2508 return self._columns.as_readonly() 

2509 

2510 @HasMemoized.memoized_attribute 

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

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

2513 return self._columns.as_readonly() 

2514 

2515 @property 

2516 def iterate_properties(self): 

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

2518 

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

2520 

2521 def _mappers_from_spec( 

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

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

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

2525 represents. 

2526 

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

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

2529 

2530 """ 

2531 if spec == "*": 

2532 mappers = list(self.self_and_descendants) 

2533 elif spec: 

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

2535 for m in util.to_list(spec): 

2536 m = _class_to_mapper(m) 

2537 if not m.isa(self): 

2538 raise sa_exc.InvalidRequestError( 

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

2540 ) 

2541 

2542 if selectable is None: 

2543 mapper_set.update(m.iterate_to_root()) 

2544 else: 

2545 mapper_set.add(m) 

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

2547 else: 

2548 mappers = [] 

2549 

2550 if selectable is not None: 

2551 tables = set( 

2552 sql_util.find_tables(selectable, include_aliases=True) 

2553 ) 

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

2555 return mappers 

2556 

2557 def _selectable_from_mappers( 

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

2559 ) -> FromClause: 

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

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

2562 mapped tables. 

2563 

2564 """ 

2565 from_obj = self.persist_selectable 

2566 for m in mappers: 

2567 if m is self: 

2568 continue 

2569 if m.concrete: 

2570 raise sa_exc.InvalidRequestError( 

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

2572 "when concrete-inheriting mappers are used." 

2573 ) 

2574 elif not m.single: 

2575 if innerjoin: 

2576 from_obj = from_obj.join( 

2577 m.local_table, m.inherit_condition 

2578 ) 

2579 else: 

2580 from_obj = from_obj.outerjoin( 

2581 m.local_table, m.inherit_condition 

2582 ) 

2583 

2584 return from_obj 

2585 

2586 @HasMemoized.memoized_attribute 

2587 def _version_id_has_server_side_value(self) -> bool: 

2588 vid_col = self.version_id_col 

2589 

2590 if vid_col is None: 

2591 return False 

2592 

2593 elif not isinstance(vid_col, Column): 

2594 return True 

2595 else: 

2596 return vid_col.server_default is not None or ( 

2597 vid_col.default is not None 

2598 and ( 

2599 not vid_col.default.is_scalar 

2600 and not vid_col.default.is_callable 

2601 ) 

2602 ) 

2603 

2604 @HasMemoized.memoized_attribute 

2605 def _single_table_criteria_component(self): 

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

2607 

2608 hierarchy = tuple( 

2609 m.polymorphic_identity 

2610 for m in self.self_and_descendants 

2611 if not m.polymorphic_abstract 

2612 ) 

2613 

2614 return ( 

2615 self.polymorphic_on._annotate( 

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

2617 ), 

2618 hierarchy, 

2619 ) 

2620 else: 

2621 return None 

2622 

2623 @HasMemoized.memoized_attribute 

2624 def _single_table_criterion(self): 

2625 component = self._single_table_criteria_component 

2626 if component is not None: 

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

2628 else: 

2629 return None 

2630 

2631 @HasMemoized.memoized_attribute 

2632 def _has_aliased_polymorphic_fromclause(self): 

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

2634 like a subquery. 

2635 

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

2637 if this is present. 

2638 

2639 """ 

2640 return self.with_polymorphic and isinstance( 

2641 self.with_polymorphic[1], 

2642 expression.AliasedReturnsRows, 

2643 ) 

2644 

2645 @HasMemoized.memoized_attribute 

2646 def _should_select_with_poly_adapter(self): 

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

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

2649 rows for mapped classes and subclasses against this Mapper. 

2650 

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

2652 for this condition. 

2653 

2654 """ 

2655 

2656 # this has been simplified as of #8456. 

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

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

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

2660 # 

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

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

2663 # flattened JOIN for with_polymorphic.) 

2664 # 

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

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

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

2668 # some kind or polymorphic_union. 

2669 # 

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

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

2672 # on it (such as test_join_from_polymorphic_explicit_aliased_three). 

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

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

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

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

2677 # legacy case we should probably disable. 

2678 # 

2679 # 

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

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

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

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

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

2685 # polymorphic base class. 

2686 # 

2687 return ( 

2688 self._has_aliased_polymorphic_fromclause 

2689 or self._requires_row_aliasing 

2690 or (self.base_mapper._has_aliased_polymorphic_fromclause) 

2691 or self.base_mapper._requires_row_aliasing 

2692 ) 

2693 

2694 @HasMemoized.memoized_attribute 

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

2696 self._check_configure() 

2697 

2698 if not self.with_polymorphic: 

2699 return [] 

2700 return self._mappers_from_spec(*self.with_polymorphic) 

2701 

2702 @HasMemoized.memoized_attribute 

2703 def _post_inspect(self): 

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

2705 

2706 E.g. when Query calls: 

2707 

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

2709 

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

2711 

2712 """ 

2713 self._check_configure() 

2714 

2715 @HasMemoized_ro_memoized_attribute 

2716 def _with_polymorphic_selectable(self) -> FromClause: 

2717 if not self.with_polymorphic: 

2718 return self.persist_selectable 

2719 

2720 spec, selectable = self.with_polymorphic 

2721 if selectable is not None: 

2722 return selectable 

2723 else: 

2724 return self._selectable_from_mappers( 

2725 self._mappers_from_spec(spec, selectable), False 

2726 ) 

2727 

2728 with_polymorphic_mappers = _with_polymorphic_mappers 

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

2730 default "polymorphic" query. 

2731 

2732 """ 

2733 

2734 @HasMemoized_ro_memoized_attribute 

2735 def _insert_cols_evaluating_none(self): 

2736 return { 

2737 table: frozenset( 

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

2739 ) 

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

2741 } 

2742 

2743 @HasMemoized.memoized_attribute 

2744 def _insert_cols_as_none(self): 

2745 return { 

2746 table: frozenset( 

2747 col.key 

2748 for col in columns 

2749 if not col.primary_key 

2750 and not col.server_default 

2751 and not col.default 

2752 and not col.type.should_evaluate_none 

2753 ) 

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

2755 } 

2756 

2757 @HasMemoized.memoized_attribute 

2758 def _insert_orm_defaults( 

2759 self, 

2760 ) -> Mapping[FromClause, Sequence[Tuple[str, ColumnElement[Any], Any]]]: 

2761 """per table, (propkey, column, value) for ORM-level defaults on 

2762 column properties, i.e. dataclass defaults, which the column's own 

2763 default would not deliver to an INSERT. 

2764 

2765 Normally the ORM-level default is the same as the column's default. 

2766 They differ when the column is shared with another mapper, as is the 

2767 case for use_existing_column on single inheritance subclasses, #13748 

2768 

2769 """ 

2770 return { 

2771 table: [ 

2772 (prop.key, col, prop._default_scalar_value) 

2773 for col in columns 

2774 # None indicates no ORM-level default; a None value would 

2775 # be omitted from the INSERT in any case 

2776 if (prop := self._columntoproperty[col])._default_scalar_value 

2777 is not None 

2778 # skip if the column's own default already delivers the 

2779 # same value 

2780 and not ( 

2781 col.default is not None 

2782 and col.default.compare_argument( 

2783 prop._default_scalar_value 

2784 ) 

2785 ) 

2786 ] 

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

2788 } 

2789 

2790 @HasMemoized.memoized_attribute 

2791 def _propkey_to_col(self): 

2792 return { 

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

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

2795 } 

2796 

2797 @HasMemoized.memoized_attribute 

2798 def _pk_keys_by_table(self): 

2799 return { 

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

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

2802 } 

2803 

2804 @HasMemoized.memoized_attribute 

2805 def _pk_attr_keys_by_table(self): 

2806 return { 

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

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

2809 } 

2810 

2811 @HasMemoized.memoized_attribute 

2812 def _server_default_cols( 

2813 self, 

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

2815 return { 

2816 table: frozenset( 

2817 [ 

2818 col 

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

2820 if col.server_default is not None 

2821 or ( 

2822 col.default is not None 

2823 and col.default.is_clause_element 

2824 ) 

2825 ] 

2826 ) 

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

2828 } 

2829 

2830 @HasMemoized.memoized_attribute 

2831 def _server_onupdate_default_cols( 

2832 self, 

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

2834 return { 

2835 table: frozenset( 

2836 [ 

2837 col 

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

2839 if col.server_onupdate is not None 

2840 or ( 

2841 col.onupdate is not None 

2842 and col.onupdate.is_clause_element 

2843 ) 

2844 ] 

2845 ) 

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

2847 } 

2848 

2849 @HasMemoized.memoized_attribute 

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

2851 return { 

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

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

2854 } 

2855 

2856 @HasMemoized.memoized_attribute 

2857 def _server_onupdate_default_col_keys( 

2858 self, 

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

2860 return { 

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

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

2863 } 

2864 

2865 @HasMemoized.memoized_attribute 

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

2867 result: Set[str] = set() 

2868 

2869 col_to_property = self._columntoproperty 

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

2871 result.update( 

2872 col_to_property[col].key 

2873 for col in columns.intersection(col_to_property) 

2874 ) 

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

2876 result.update( 

2877 col_to_property[col].key 

2878 for col in columns.intersection(col_to_property) 

2879 ) 

2880 return result 

2881 

2882 @HasMemoized.memoized_instancemethod 

2883 def __clause_element__(self): 

2884 annotations: Dict[str, Any] = { 

2885 "entity_namespace": self, 

2886 "parententity": self, 

2887 "parentmapper": self, 

2888 } 

2889 if self.persist_selectable is not self.local_table: 

2890 # joined table inheritance, with polymorphic selectable, 

2891 # etc. 

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

2893 { 

2894 "entity_namespace": self, 

2895 "parententity": self, 

2896 "parentmapper": self, 

2897 } 

2898 )._set_propagate_attrs( 

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

2900 ) 

2901 

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

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

2904 ) 

2905 

2906 @util.memoized_property 

2907 def select_identity_token(self): 

2908 return ( 

2909 expression.null() 

2910 ._annotate( 

2911 { 

2912 "entity_namespace": self, 

2913 "parententity": self, 

2914 "parentmapper": self, 

2915 "identity_token": True, 

2916 } 

2917 ) 

2918 ._set_propagate_attrs( 

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

2920 ) 

2921 ) 

2922 

2923 @property 

2924 def selectable(self) -> FromClause: 

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

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

2927 

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

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

2930 full "polymorphic" selectable is returned. 

2931 

2932 """ 

2933 return self._with_polymorphic_selectable 

2934 

2935 def _with_polymorphic_args( 

2936 self, 

2937 spec: Any = None, 

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

2939 innerjoin: bool = False, 

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

2941 if selectable not in (None, False): 

2942 selectable = coercions.expect( 

2943 roles.FromClauseRole, 

2944 selectable, 

2945 ) 

2946 

2947 if self.with_polymorphic: 

2948 if not spec: 

2949 spec = self.with_polymorphic[0] 

2950 if selectable is False: 

2951 selectable = self.with_polymorphic[1] 

2952 elif selectable is False: 

2953 selectable = None 

2954 mappers = self._mappers_from_spec(spec, selectable) 

2955 if selectable is not None: 

2956 return mappers, selectable 

2957 else: 

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

2959 

2960 @HasMemoized.memoized_attribute 

2961 def _polymorphic_properties(self): 

2962 return list( 

2963 self._iterate_polymorphic_properties( 

2964 self._with_polymorphic_mappers 

2965 ) 

2966 ) 

2967 

2968 @property 

2969 def _all_column_expressions(self): 

2970 poly_properties = self._polymorphic_properties 

2971 adapter = self._polymorphic_adapter 

2972 

2973 return [ 

2974 adapter.columns[c] if adapter else c 

2975 for prop in poly_properties 

2976 if isinstance(prop, properties.ColumnProperty) 

2977 and prop._renders_in_subqueries 

2978 for c in prop.columns 

2979 ] 

2980 

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

2982 if polymorphic_mappers: 

2983 poly_properties = self._iterate_polymorphic_properties( 

2984 polymorphic_mappers 

2985 ) 

2986 else: 

2987 poly_properties = self._polymorphic_properties 

2988 

2989 return [ 

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

2991 for prop in poly_properties 

2992 if isinstance(prop, properties.ColumnProperty) 

2993 ] 

2994 

2995 @HasMemoized.memoized_attribute 

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

2997 if self._has_aliased_polymorphic_fromclause: 

2998 return orm_util.ORMAdapter( 

2999 orm_util._TraceAdaptRole.MAPPER_POLYMORPHIC_ADAPTER, 

3000 self, 

3001 selectable=self.selectable, 

3002 equivalents=self._equivalent_columns, 

3003 limit_on_entity=False, 

3004 ) 

3005 else: 

3006 return None 

3007 

3008 def _iterate_polymorphic_properties(self, mappers=None): 

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

3010 a SELECT.""" 

3011 if mappers is None: 

3012 mappers = self._with_polymorphic_mappers 

3013 

3014 if not mappers: 

3015 for c in self.iterate_properties: 

3016 yield c 

3017 else: 

3018 # in the polymorphic case, filter out discriminator columns 

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

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

3021 for c in util.unique_list( 

3022 chain( 

3023 *[ 

3024 list(mapper.iterate_properties) 

3025 for mapper in [self] + mappers 

3026 ] 

3027 ) 

3028 ): 

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

3030 self.polymorphic_on is None 

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

3032 ): 

3033 continue 

3034 yield c 

3035 

3036 @HasMemoized.memoized_attribute 

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

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

3039 associated this mapper. 

3040 

3041 This is an object that provides each property based on 

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

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

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

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

3046 column. The namespace object can also be iterated, 

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

3048 

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

3050 of this attribute which limit the types of properties 

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

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

3053 

3054 .. warning:: 

3055 

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

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

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

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

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

3061 accessing attributes dynamically, favor using the dict-access 

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

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

3064 

3065 .. seealso:: 

3066 

3067 :attr:`_orm.Mapper.all_orm_descriptors` 

3068 

3069 """ 

3070 

3071 self._check_configure() 

3072 return util.ReadOnlyProperties(self._props) 

3073 

3074 @HasMemoized.memoized_attribute 

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

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

3077 with the mapped class. 

3078 

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

3080 associated with the mapped class or its superclasses. 

3081 

3082 This namespace includes attributes that are mapped to the class 

3083 as well as attributes declared by extension modules. 

3084 It includes any Python descriptor type that inherits from 

3085 :class:`.InspectionAttr`. This includes 

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

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

3088 :class:`.AssociationProxy`. 

3089 

3090 To distinguish between mapped attributes and extension attributes, 

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

3092 to a constant that distinguishes between different extension types. 

3093 

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

3095 

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

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

3098 

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

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

3101 3 below. The order will be the 

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

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

3104 or the mapper. 

3105 

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

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

3108 class in which it first appeared. 

3109 

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

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

3112 

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

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

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

3116 referring to the collection of mapped properties via 

3117 :attr:`_orm.Mapper.attrs`. 

3118 

3119 .. warning:: 

3120 

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

3122 accessor namespace is an 

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

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

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

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

3127 accessing attributes dynamically, favor using the dict-access 

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

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

3130 collisions. 

3131 

3132 .. seealso:: 

3133 

3134 :attr:`_orm.Mapper.attrs` 

3135 

3136 """ 

3137 return util.ReadOnlyProperties( 

3138 dict(self.class_manager._all_sqla_attributes()) 

3139 ) 

3140 

3141 @HasMemoized.memoized_attribute 

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

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

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

3145 all synonyms that refer to primary key columns 

3146 

3147 """ 

3148 descriptor_props = util.preloaded.orm_descriptor_props 

3149 

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

3151 

3152 return { 

3153 syn.key: syn.name 

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

3155 if isinstance(syn, descriptor_props.SynonymProperty) 

3156 and syn.name in pk_keys 

3157 } 

3158 

3159 @HasMemoized.memoized_attribute 

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

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

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

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

3164 

3165 .. seealso:: 

3166 

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

3168 :class:`.MapperProperty` 

3169 objects. 

3170 

3171 """ 

3172 descriptor_props = util.preloaded.orm_descriptor_props 

3173 

3174 return self._filter_properties(descriptor_props.SynonymProperty) 

3175 

3176 @util.ro_non_memoized_property 

3177 def entity_namespace(self) -> _EntityNamespace: 

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

3179 

3180 @HasMemoized.memoized_attribute 

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

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

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

3184 

3185 .. seealso:: 

3186 

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

3188 :class:`.MapperProperty` 

3189 objects. 

3190 

3191 """ 

3192 return self._filter_properties(properties.ColumnProperty) 

3193 

3194 @HasMemoized.memoized_attribute 

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

3196 def relationships( 

3197 self, 

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

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

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

3201 

3202 .. warning:: 

3203 

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

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

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

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

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

3209 accessing attributes dynamically, favor using the dict-access 

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

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

3212 collisions. 

3213 

3214 .. seealso:: 

3215 

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

3217 :class:`.MapperProperty` 

3218 objects. 

3219 

3220 """ 

3221 return self._filter_properties( 

3222 util.preloaded.orm_relationships.RelationshipProperty 

3223 ) 

3224 

3225 @HasMemoized.memoized_attribute 

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

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

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

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

3230 

3231 .. seealso:: 

3232 

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

3234 :class:`.MapperProperty` 

3235 objects. 

3236 

3237 """ 

3238 return self._filter_properties( 

3239 util.preloaded.orm_descriptor_props.CompositeProperty 

3240 ) 

3241 

3242 def _filter_properties( 

3243 self, type_: Type[_MP] 

3244 ) -> util.ReadOnlyProperties[_MP]: 

3245 self._check_configure() 

3246 return util.ReadOnlyProperties( 

3247 util.OrderedDict( 

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

3249 ) 

3250 ) 

3251 

3252 @HasMemoized.memoized_attribute 

3253 def _get_clause(self): 

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

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

3256 by primary key. 

3257 

3258 """ 

3259 params = [ 

3260 ( 

3261 primary_key, 

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

3263 ) 

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

3265 ] 

3266 return ( 

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

3268 util.column_dict(params), 

3269 ) 

3270 

3271 @HasMemoized.memoized_attribute 

3272 def _equivalent_columns(self) -> _EquivalentColumnMap: 

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

3274 the determination of column pairs that are equated to 

3275 one another based on inherit condition. This is designed 

3276 to work with the queries that util.polymorphic_union 

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

3278 the base table directly (including the subclass table columns 

3279 only). 

3280 

3281 The resulting structure is a dictionary of columns mapped 

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

3283 

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

3285 

3286 """ # noqa: E501 

3287 result: _EquivalentColumnMap = {} 

3288 

3289 def visit_binary(binary): 

3290 if binary.operator == operators.eq: 

3291 if binary.left in result: 

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

3293 else: 

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

3295 if binary.right in result: 

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

3297 else: 

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

3299 

3300 for mapper in self.base_mapper.self_and_descendants: 

3301 if mapper.inherit_condition is not None: 

3302 visitors.traverse( 

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

3304 ) 

3305 

3306 return result 

3307 

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

3309 if isinstance( 

3310 obj, 

3311 ( 

3312 _MappedAttribute, 

3313 instrumentation.ClassManager, 

3314 expression.ColumnElement, 

3315 ), 

3316 ): 

3317 return False 

3318 else: 

3319 return assigned_name not in self._dataclass_fields 

3320 

3321 @HasMemoized.memoized_attribute 

3322 def _dataclass_fields(self): 

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

3324 

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

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

3327 present on the class. 

3328 

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

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

3331 

3332 """ 

3333 

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

3335 return True 

3336 

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

3338 # either local or from an inherited class 

3339 # ignore dataclass field default values 

3340 if local: 

3341 if self.class_.__dict__.get( 

3342 assigned_name, None 

3343 ) is not None and self._is_userland_descriptor( 

3344 assigned_name, self.class_.__dict__[assigned_name] 

3345 ): 

3346 return True 

3347 else: 

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

3349 if attr is not None and self._is_userland_descriptor( 

3350 assigned_name, attr 

3351 ): 

3352 return True 

3353 

3354 columns = [column] if column is not None else [] 

3355 if self._excluded_by_include_exclude(name, columns): 

3356 self._warn_for_excluded_declared_attr(name, columns) 

3357 return True 

3358 

3359 return False 

3360 

3361 def _excluded_by_include_exclude( 

3362 self, name: str, columns: Sequence[KeyedColumnElement[Any]] 

3363 ) -> bool: 

3364 if ( 

3365 self.include_properties is not None 

3366 and name not in self.include_properties 

3367 and self.include_properties.isdisjoint(columns) 

3368 ): 

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

3370 return True 

3371 

3372 if self.exclude_properties is not None and ( 

3373 name in self.exclude_properties 

3374 or not self.exclude_properties.isdisjoint(columns) 

3375 ): 

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

3377 return True 

3378 

3379 return False 

3380 

3381 def _warn_for_excluded_declared_attr( 

3382 self, name: str, columns: Sequence[KeyedColumnElement[Any]] 

3383 ) -> None: 

3384 """warn for include_properties / exclude_properties that exclude 

3385 an attribute declared on a Declarative class, #13650. 

3386 

3387 Columns that are present only in the Table, e.g. from ``__table__``, 

3388 ``__table_args__`` or reflection, may be excluded without warning. 

3389 

3390 """ 

3391 decl_scan_ref = self.class_manager.declarative_scan 

3392 mapper_config = decl_scan_ref() if decl_scan_ref is not None else None 

3393 if mapper_config is None or not mapper_config.is_declarative: 

3394 return 

3395 decl_scan = cast("_DeclarativeMapperConfig", mapper_config) 

3396 

3397 if any( 

3398 col in decl_scan.declared_columns 

3399 and col not in decl_scan.unmapped_columns 

3400 for col in columns 

3401 ): 

3402 util.warn_deprecated( 

3403 f"Attribute {name!r} declared on class {self.class_} is " 

3404 "excluded from the mapping by the exclude_properties / " 

3405 "include_properties mapper arguments; excluding attributes " 

3406 "that are declared on the class is deprecated, and does not " 

3407 "reliably remove them from the mapping. To declare a column " 

3408 "that is part of the Table but is not mapped, use the " 

3409 "Unmapped[] annotation with unmapped_column().", 

3410 "2.1.5", 

3411 ) 

3412 

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

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

3415 common inherited parent as this mapper.""" 

3416 

3417 return self.base_mapper is other.base_mapper 

3418 

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

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

3421 one. common parent but different branch 

3422 

3423 """ 

3424 return ( 

3425 self.base_mapper is other.base_mapper 

3426 and not self.isa(other) 

3427 and not other.isa(self) 

3428 ) 

3429 

3430 def _canload( 

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

3432 ) -> bool: 

3433 s = self.primary_mapper() 

3434 if self.polymorphic_on is not None or allow_subtypes: 

3435 return _state_mapper(state).isa(s) 

3436 else: 

3437 return _state_mapper(state) is s 

3438 

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

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

3441 

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

3443 while m and m is not other: 

3444 m = m.inherits 

3445 return bool(m) 

3446 

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

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

3449 while m: 

3450 yield m 

3451 m = m.inherits 

3452 

3453 @HasMemoized.memoized_attribute 

3454 def self_and_descendants(self) -> Sequence[Mapper[Any]]: 

3455 """The collection including this mapper and all descendant mappers. 

3456 

3457 This includes not just the immediately inheriting mappers but 

3458 all their inheriting mappers as well. 

3459 

3460 """ 

3461 descendants = [] 

3462 stack = deque([self]) 

3463 while stack: 

3464 item = stack.popleft() 

3465 descendants.append(item) 

3466 stack.extend(item._inheriting_mappers) 

3467 return util.WeakSequence(descendants) 

3468 

3469 def polymorphic_iterator(self) -> Iterator[Mapper[Any]]: 

3470 """Iterate through the collection including this mapper and 

3471 all descendant mappers. 

3472 

3473 This includes not just the immediately inheriting mappers but 

3474 all their inheriting mappers as well. 

3475 

3476 To iterate through an entire hierarchy, use 

3477 ``mapper.base_mapper.polymorphic_iterator()``. 

3478 

3479 """ 

3480 return iter(self.self_and_descendants) 

3481 

3482 def primary_mapper(self) -> Mapper[Any]: 

3483 """Return the primary mapper corresponding to this mapper's class key 

3484 (class).""" 

3485 

3486 return self.class_manager.mapper 

3487 

3488 @property 

3489 def primary_base_mapper(self) -> Mapper[Any]: 

3490 return self.class_manager.mapper.base_mapper 

3491 

3492 def _result_has_identity_key(self, result, adapter=None): 

3493 pk_cols: Sequence[ColumnElement[Any]] 

3494 if adapter is not None: 

3495 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3496 else: 

3497 pk_cols = self.primary_key 

3498 rk = result.keys() 

3499 for col in pk_cols: 

3500 if col not in rk: 

3501 return False 

3502 else: 

3503 return True 

3504 

3505 def identity_key_from_row( 

3506 self, 

3507 row: Union[Row[Unpack[TupleAny]], RowMapping], 

3508 identity_token: Optional[Any] = None, 

3509 adapter: Optional[ORMAdapter] = None, 

3510 ) -> _IdentityKeyType[_O]: 

3511 """Return an identity-map key for use in storing/retrieving an 

3512 item from the identity map. 

3513 

3514 :param row: A :class:`.Row` or :class:`.RowMapping` produced from a 

3515 result set that selected from the ORM mapped primary key columns. 

3516 

3517 .. versionchanged:: 2.0 

3518 :class:`.Row` or :class:`.RowMapping` are accepted 

3519 for the "row" argument 

3520 

3521 """ 

3522 pk_cols: Sequence[ColumnElement[Any]] 

3523 if adapter is not None: 

3524 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3525 else: 

3526 pk_cols = self.primary_key 

3527 

3528 mapping: RowMapping 

3529 if hasattr(row, "_mapping"): 

3530 mapping = row._mapping 

3531 else: 

3532 mapping = row # type: ignore[assignment] 

3533 

3534 return ( 

3535 self._identity_class, 

3536 tuple(mapping[column] for column in pk_cols), 

3537 identity_token, 

3538 ) 

3539 

3540 def identity_key_from_primary_key( 

3541 self, 

3542 primary_key: Tuple[Any, ...], 

3543 identity_token: Optional[Any] = None, 

3544 ) -> _IdentityKeyType[_O]: 

3545 """Return an identity-map key for use in storing/retrieving an 

3546 item from an identity map. 

3547 

3548 :param primary_key: A list of values indicating the identifier. 

3549 

3550 """ 

3551 return ( 

3552 self._identity_class, 

3553 tuple(primary_key), 

3554 identity_token, 

3555 ) 

3556 

3557 def identity_key_from_instance(self, instance: _O) -> _IdentityKeyType[_O]: 

3558 """Return the identity key for the given instance, based on 

3559 its primary key attributes. 

3560 

3561 If the instance's state is expired, calling this method 

3562 will result in a database check to see if the object has been deleted. 

3563 If the row no longer exists, 

3564 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3565 

3566 This value is typically also found on the instance state under the 

3567 attribute name `key`. 

3568 

3569 """ 

3570 state = attributes.instance_state(instance) 

3571 return self._identity_key_from_state(state, PassiveFlag.PASSIVE_OFF) 

3572 

3573 def _identity_key_from_state( 

3574 self, 

3575 state: InstanceState[_O], 

3576 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3577 ) -> _IdentityKeyType[_O]: 

3578 dict_ = state.dict 

3579 manager = state.manager 

3580 return ( 

3581 self._identity_class, 

3582 tuple( 

3583 [ 

3584 manager[prop.key].impl.get(state, dict_, passive) 

3585 for prop in self._identity_key_props 

3586 ] 

3587 ), 

3588 state.identity_token, 

3589 ) 

3590 

3591 def primary_key_from_instance(self, instance: _O) -> Tuple[Any, ...]: 

3592 """Return the list of primary key values for the given 

3593 instance. 

3594 

3595 If the instance's state is expired, calling this method 

3596 will result in a database check to see if the object has been deleted. 

3597 If the row no longer exists, 

3598 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3599 

3600 """ 

3601 state = attributes.instance_state(instance) 

3602 identity_key = self._identity_key_from_state( 

3603 state, PassiveFlag.PASSIVE_OFF 

3604 ) 

3605 return identity_key[1] 

3606 

3607 @HasMemoized.memoized_attribute 

3608 def _persistent_sortkey_fn(self): 

3609 key_fns = [col.type.sort_key_function for col in self.primary_key] 

3610 

3611 if set(key_fns).difference([None]): 

3612 

3613 def key(state): 

3614 return tuple( 

3615 key_fn(val) if key_fn is not None else val 

3616 for key_fn, val in zip(key_fns, state.key[1]) 

3617 ) 

3618 

3619 else: 

3620 

3621 def key(state): 

3622 return state.key[1] 

3623 

3624 return key 

3625 

3626 @HasMemoized.memoized_attribute 

3627 def _identity_key_props(self): 

3628 return [self._columntoproperty[col] for col in self.primary_key] 

3629 

3630 @HasMemoized.memoized_attribute 

3631 def _all_pk_cols(self): 

3632 collection: Set[ColumnClause[Any]] = set() 

3633 for table in self.tables: 

3634 collection.update(self._pks_by_table[table]) 

3635 return collection 

3636 

3637 @HasMemoized.memoized_attribute 

3638 def _should_undefer_in_wildcard(self): 

3639 cols: Set[ColumnElement[Any]] = set(self.primary_key) 

3640 if self.polymorphic_on is not None: 

3641 cols.add(self.polymorphic_on) 

3642 return cols 

3643 

3644 @HasMemoized.memoized_attribute 

3645 def _primary_key_propkeys(self): 

3646 return {self._columntoproperty[col].key for col in self._all_pk_cols} 

3647 

3648 def _get_state_attr_by_column( 

3649 self, 

3650 state: InstanceState[_O], 

3651 dict_: _InstanceDict, 

3652 column: ColumnElement[Any], 

3653 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3654 ) -> Any: 

3655 prop = self._columntoproperty[column] 

3656 return state.manager[prop.key].impl.get(state, dict_, passive=passive) 

3657 

3658 def _state_ident_getter(self, columns, passive): 

3659 lookup_keys = [self._columntoproperty[col].key for col in columns] 

3660 missing = object() 

3661 

3662 def get_ident(state, state_dict): 

3663 return tuple( 

3664 ( 

3665 v 

3666 if (v := state_dict.get(lk, missing)) is not missing 

3667 else self._get_state_attr_by_column( 

3668 state, state_dict, col, passive=passive 

3669 ) 

3670 ) 

3671 for lk, col in zip(lookup_keys, columns) 

3672 ) 

3673 

3674 return get_ident 

3675 

3676 def _set_committed_state_attr_by_column(self, state, dict_, column, value): 

3677 prop = self._columntoproperty[column] 

3678 state.manager[prop.key].impl.set_committed_value(state, dict_, value) 

3679 

3680 def _set_state_attr_by_column(self, state, dict_, column, value): 

3681 prop = self._columntoproperty[column] 

3682 state.manager[prop.key].impl.set(state, dict_, value, None) 

3683 

3684 def _get_committed_attr_by_column(self, obj, column): 

3685 state = attributes.instance_state(obj) 

3686 dict_ = attributes.instance_dict(obj) 

3687 return self._get_committed_state_attr_by_column( 

3688 state, dict_, column, passive=PassiveFlag.PASSIVE_OFF 

3689 ) 

3690 

3691 def _get_committed_state_attr_by_column( 

3692 self, state, dict_, column, passive=PassiveFlag.PASSIVE_RETURN_NO_VALUE 

3693 ): 

3694 prop = self._columntoproperty[column] 

3695 return state.manager[prop.key].impl.get_committed_value( 

3696 state, dict_, passive=passive 

3697 ) 

3698 

3699 def _optimized_get_statement(self, state, attribute_names): 

3700 """assemble a WHERE clause which retrieves a given state by primary 

3701 key, using a minimized set of tables. 

3702 

3703 Applies to a joined-table inheritance mapper where the 

3704 requested attribute names are only present on joined tables, 

3705 not the base table. The WHERE clause attempts to include 

3706 only those tables to minimize joins. 

3707 

3708 """ 

3709 props = self._props 

3710 

3711 col_attribute_names = set(attribute_names).intersection( 

3712 state.mapper.column_attrs.keys() 

3713 ) 

3714 tables: Set[FromClause] = set( 

3715 chain( 

3716 *[ 

3717 sql_util.find_tables(c, check_columns=True) 

3718 for key in col_attribute_names 

3719 for c in props[key].columns 

3720 ] 

3721 ) 

3722 ) 

3723 

3724 if self.base_mapper.local_table in tables: 

3725 return None 

3726 

3727 def visit_binary(binary): 

3728 leftcol = binary.left 

3729 rightcol = binary.right 

3730 if leftcol is None or rightcol is None: 

3731 return 

3732 

3733 if leftcol.table not in tables: 

3734 leftval = self._get_committed_state_attr_by_column( 

3735 state, 

3736 state.dict, 

3737 leftcol, 

3738 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3739 ) 

3740 if leftval in orm_util._none_set: 

3741 raise _OptGetColumnsNotAvailable() 

3742 binary.left = sql.bindparam( 

3743 None, leftval, type_=binary.right.type 

3744 ) 

3745 elif rightcol.table not in tables: 

3746 rightval = self._get_committed_state_attr_by_column( 

3747 state, 

3748 state.dict, 

3749 rightcol, 

3750 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3751 ) 

3752 if rightval in orm_util._none_set: 

3753 raise _OptGetColumnsNotAvailable() 

3754 binary.right = sql.bindparam( 

3755 None, rightval, type_=binary.right.type 

3756 ) 

3757 

3758 allconds: List[ColumnElement[bool]] = [] 

3759 

3760 start = False 

3761 

3762 # as of #7507, from the lowest base table on upwards, 

3763 # we include all intermediary tables. 

3764 

3765 for mapper in reversed(list(self.iterate_to_root())): 

3766 if mapper.local_table in tables: 

3767 start = True 

3768 elif not isinstance(mapper.local_table, expression.TableClause): 

3769 return None 

3770 if start and not mapper.single: 

3771 assert mapper.inherits 

3772 assert not mapper.concrete 

3773 assert mapper.inherit_condition is not None 

3774 allconds.append(mapper.inherit_condition) 

3775 tables.add(mapper.local_table) 

3776 

3777 # only the bottom table needs its criteria to be altered to fit 

3778 # the primary key ident - the rest of the tables upwards to the 

3779 # descendant-most class should all be present and joined to each 

3780 # other. 

3781 try: 

3782 _traversed = visitors.cloned_traverse( 

3783 allconds[0], {}, {"binary": visit_binary} 

3784 ) 

3785 except _OptGetColumnsNotAvailable: 

3786 return None 

3787 else: 

3788 allconds[0] = _traversed 

3789 

3790 cond = sql.and_(*allconds) 

3791 

3792 cols = [] 

3793 for key in col_attribute_names: 

3794 cols.extend(props[key].columns) 

3795 return ( 

3796 sql.select(*cols) 

3797 .where(cond) 

3798 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 

3799 ) 

3800 

3801 def _iterate_to_target_viawpoly(self, mapper): 

3802 if self.isa(mapper): 

3803 prev = self 

3804 for m in self.iterate_to_root(): 

3805 yield m 

3806 

3807 if m is not prev and prev not in m._with_polymorphic_mappers: 

3808 break 

3809 

3810 prev = m 

3811 if m is mapper: 

3812 break 

3813 

3814 @HasMemoized.memoized_attribute 

3815 def _would_selectinload_combinations_cache(self): 

3816 return {} 

3817 

3818 def _would_selectin_load_only_from_given_mapper(self, super_mapper): 

3819 """return True if this mapper would "selectin" polymorphic load based 

3820 on the given super mapper, and not from a setting from a subclass. 

3821 

3822 given:: 

3823 

3824 class A: ... 

3825 

3826 

3827 class B(A): 

3828 __mapper_args__ = {"polymorphic_load": "selectin"} 

3829 

3830 

3831 class C(B): ... 

3832 

3833 

3834 class D(B): 

3835 __mapper_args__ = {"polymorphic_load": "selectin"} 

3836 

3837 ``inspect(C)._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3838 returns True, because C does selectin loading because of B's setting. 

3839 

3840 OTOH, ``inspect(D) 

3841 ._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3842 returns False, because D does selectin loading because of its own 

3843 setting; when we are doing a selectin poly load from B, we want to 

3844 filter out D because it would already have its own selectin poly load 

3845 set up separately. 

3846 

3847 Added as part of #9373. 

3848 

3849 """ 

3850 cache = self._would_selectinload_combinations_cache 

3851 

3852 try: 

3853 return cache[super_mapper] 

3854 except KeyError: 

3855 pass 

3856 

3857 # assert that given object is a supermapper, meaning we already 

3858 # strong reference it directly or indirectly. this allows us 

3859 # to not worry that we are creating new strongrefs to unrelated 

3860 # mappers or other objects. 

3861 assert self.isa(super_mapper) 

3862 

3863 mapper = super_mapper 

3864 for m in self._iterate_to_target_viawpoly(mapper): 

3865 if m.polymorphic_load == "selectin": 

3866 retval = m is super_mapper 

3867 break 

3868 else: 

3869 retval = False 

3870 

3871 cache[super_mapper] = retval 

3872 return retval 

3873 

3874 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 

3875 if not enabled_via_opt: 

3876 # common case, takes place for all polymorphic loads 

3877 mapper = polymorphic_from 

3878 for m in self._iterate_to_target_viawpoly(mapper): 

3879 if m.polymorphic_load == "selectin": 

3880 return m 

3881 else: 

3882 # uncommon case, selectin load options were used 

3883 enabled_via_opt = set(enabled_via_opt) 

3884 enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 

3885 for entity in enabled_via_opt.union([polymorphic_from]): 

3886 mapper = entity.mapper 

3887 for m in self._iterate_to_target_viawpoly(mapper): 

3888 if ( 

3889 m.polymorphic_load == "selectin" 

3890 or m in enabled_via_opt_mappers 

3891 ): 

3892 return enabled_via_opt_mappers.get(m, m) 

3893 

3894 return None 

3895 

3896 @util.preload_module("sqlalchemy.orm.strategy_options") 

3897 def _subclass_load_via_in(self, entity, polymorphic_from): 

3898 """Assemble a that can load the columns local to 

3899 this subclass as a SELECT with IN. 

3900 

3901 """ 

3902 

3903 strategy_options = util.preloaded.orm_strategy_options 

3904 

3905 assert self.inherits 

3906 

3907 if self.polymorphic_on is not None: 

3908 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 

3909 keep_props = set([polymorphic_prop] + self._identity_key_props) 

3910 else: 

3911 keep_props = set(self._identity_key_props) 

3912 

3913 disable_opt = strategy_options.Load(entity) 

3914 enable_opt = strategy_options.Load(entity) 

3915 

3916 classes_to_include = {self} 

3917 m: Optional[Mapper[Any]] = self.inherits 

3918 while ( 

3919 m is not None 

3920 and m is not polymorphic_from 

3921 and m.polymorphic_load == "selectin" 

3922 ): 

3923 classes_to_include.add(m) 

3924 m = m.inherits 

3925 

3926 for prop in self.column_attrs + self.relationships: 

3927 # skip prop keys that are not instrumented on the mapped class. 

3928 # this is primarily the "_sa_polymorphic_on" property that gets 

3929 # created for an ad-hoc polymorphic_on SQL expression, issue #8704 

3930 if prop.key not in self.class_manager: 

3931 continue 

3932 

3933 if prop.parent in classes_to_include or prop in keep_props: 

3934 # "enable" options, to turn on the properties that we want to 

3935 # load by default (subject to options from the query) 

3936 if not isinstance(prop, StrategizedProperty): 

3937 continue 

3938 

3939 enable_opt = enable_opt._set_generic_strategy( 

3940 # convert string name to an attribute before passing 

3941 # to loader strategy. note this must be in terms 

3942 # of given entity, such as AliasedClass, etc. 

3943 (getattr(entity.entity_namespace, prop.key),), 

3944 dict(prop.strategy_key), 

3945 _reconcile_to_other=True, 

3946 ) 

3947 else: 

3948 # "disable" options, to turn off the properties from the 

3949 # superclass that we *don't* want to load, applied after 

3950 # the options from the query to override them 

3951 disable_opt = disable_opt._set_generic_strategy( 

3952 # convert string name to an attribute before passing 

3953 # to loader strategy. note this must be in terms 

3954 # of given entity, such as AliasedClass, etc. 

3955 (getattr(entity.entity_namespace, prop.key),), 

3956 {"do_nothing": True}, 

3957 _reconcile_to_other=False, 

3958 ) 

3959 

3960 primary_key = list(self.primary_key) 

3961 

3962 in_expr: ColumnElement[Any] 

3963 

3964 if len(primary_key) > 1: 

3965 in_expr = sql.tuple_(*primary_key) 

3966 else: 

3967 in_expr = primary_key[0] 

3968 

3969 if entity.is_aliased_class: 

3970 assert entity.mapper is self 

3971 

3972 q = sql.select(entity).set_label_style( 

3973 LABEL_STYLE_TABLENAME_PLUS_COL 

3974 ) 

3975 

3976 in_expr = entity._adapter.traverse(in_expr) 

3977 q = q.where( 

3978 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3979 ) 

3980 else: 

3981 q = sql.select(self).set_label_style( 

3982 LABEL_STYLE_TABLENAME_PLUS_COL 

3983 ) 

3984 q = q.where( 

3985 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3986 ) 

3987 

3988 return q, enable_opt, disable_opt 

3989 

3990 @HasMemoized.memoized_attribute 

3991 def _subclass_load_via_in_mapper(self): 

3992 # the default is loading this mapper against the basemost mapper 

3993 return self._subclass_load_via_in(self, self.base_mapper) 

3994 

3995 def cascade_iterator( 

3996 self, 

3997 type_: str, 

3998 state: InstanceState[_O], 

3999 halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None, 

4000 ) -> Iterator[ 

4001 Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict] 

4002 ]: 

4003 r"""Iterate each element and its mapper in an object graph, 

4004 for all relationships that meet the given cascade rule. 

4005 

4006 :param type\_: 

4007 The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 

4008 etc.). 

4009 

4010 .. note:: the ``"all"`` cascade is not accepted here. For a generic 

4011 object traversal function, see :ref:`faq_walk_objects`. 

4012 

4013 :param state: 

4014 The lead InstanceState. child items will be processed per 

4015 the relationships defined for this object's mapper. 

4016 

4017 :return: the method yields individual object instances. 

4018 

4019 .. seealso:: 

4020 

4021 :ref:`unitofwork_cascades` 

4022 

4023 :ref:`faq_walk_objects` - illustrates a generic function to 

4024 traverse all objects without relying on cascades. 

4025 

4026 """ 

4027 visited_states: Set[InstanceState[Any]] = set() 

4028 prp, mpp = object(), object() 

4029 

4030 assert state.mapper.isa(self) 

4031 

4032 # this is actually a recursive structure, fully typing it seems 

4033 # a little too difficult for what it's worth here 

4034 visitables: Deque[ 

4035 Tuple[ 

4036 Deque[Any], 

4037 object, 

4038 Optional[InstanceState[Any]], 

4039 Optional[_InstanceDict], 

4040 ] 

4041 ] 

4042 

4043 visitables = deque( 

4044 [(deque(state.mapper._props.values()), prp, state, state.dict)] 

4045 ) 

4046 

4047 while visitables: 

4048 iterator, item_type, parent_state, parent_dict = visitables[-1] 

4049 if not iterator: 

4050 visitables.pop() 

4051 continue 

4052 

4053 if item_type is prp: 

4054 prop = iterator.popleft() 

4055 if not prop.cascade or type_ not in prop.cascade: 

4056 continue 

4057 assert parent_state is not None 

4058 assert parent_dict is not None 

4059 queue = deque( 

4060 prop.cascade_iterator( 

4061 type_, 

4062 parent_state, 

4063 parent_dict, 

4064 visited_states, 

4065 halt_on, 

4066 ) 

4067 ) 

4068 if queue: 

4069 visitables.append((queue, mpp, None, None)) 

4070 elif item_type is mpp: 

4071 ( 

4072 instance, 

4073 instance_mapper, 

4074 corresponding_state, 

4075 corresponding_dict, 

4076 ) = iterator.popleft() 

4077 yield ( 

4078 instance, 

4079 instance_mapper, 

4080 corresponding_state, 

4081 corresponding_dict, 

4082 ) 

4083 visitables.append( 

4084 ( 

4085 deque(instance_mapper._props.values()), 

4086 prp, 

4087 corresponding_state, 

4088 corresponding_dict, 

4089 ) 

4090 ) 

4091 

4092 @HasMemoized.memoized_attribute 

4093 def _compiled_cache(self): 

4094 return util.LRUCache(self._compiled_cache_size) 

4095 

4096 @HasMemoized.memoized_attribute 

4097 def _multiple_persistence_tables(self): 

4098 return len(self.tables) > 1 

4099 

4100 @HasMemoized.memoized_attribute 

4101 def _sorted_tables(self): 

4102 table_to_mapper: Dict[TableClause, Mapper[Any]] = {} 

4103 

4104 for mapper in self.base_mapper.self_and_descendants: 

4105 for t in mapper.tables: 

4106 table_to_mapper.setdefault(t, mapper) 

4107 

4108 extra_dependencies = [] 

4109 for table, mapper in table_to_mapper.items(): 

4110 super_ = mapper.inherits 

4111 if super_: 

4112 extra_dependencies.extend( 

4113 [(super_table, table) for super_table in super_.tables] 

4114 ) 

4115 

4116 def skip(fk): 

4117 # attempt to skip dependencies that are not 

4118 # significant to the inheritance chain 

4119 # for two tables that are related by inheritance. 

4120 # while that dependency may be important, it's technically 

4121 # not what we mean to sort on here. 

4122 parent = table_to_mapper.get(fk.parent.table) 

4123 dep = table_to_mapper.get(fk.column.table) 

4124 if ( 

4125 parent is not None 

4126 and dep is not None 

4127 and dep is not parent 

4128 and dep.inherit_condition is not None 

4129 ): 

4130 cols = set(sql_util._find_columns(dep.inherit_condition)) 

4131 if parent.inherit_condition is not None: 

4132 cols = cols.union( 

4133 sql_util._find_columns(parent.inherit_condition) 

4134 ) 

4135 return fk.parent not in cols and fk.column not in cols 

4136 else: 

4137 return fk.parent not in cols 

4138 return False 

4139 

4140 sorted_ = sql_util.sort_tables( 

4141 table_to_mapper, 

4142 skip_fn=skip, 

4143 extra_dependencies=extra_dependencies, 

4144 ) 

4145 

4146 ret = util.OrderedDict() 

4147 for t in sorted_: 

4148 ret[t] = table_to_mapper[t] 

4149 return ret 

4150 

4151 def _memo(self, key: Any, callable_: Callable[[], _T]) -> _T: 

4152 if key in self._memoized_values: 

4153 return cast(_T, self._memoized_values[key]) 

4154 else: 

4155 self._memoized_values[key] = value = callable_() 

4156 return value 

4157 

4158 @util.memoized_property 

4159 def _table_to_equated(self): 

4160 """memoized map of tables to collections of columns to be 

4161 synchronized upwards to the base mapper.""" 

4162 

4163 result: util.defaultdict[ 

4164 Table, 

4165 List[ 

4166 Tuple[ 

4167 Mapper[Any], 

4168 List[Tuple[ColumnElement[Any], ColumnElement[Any]]], 

4169 ] 

4170 ], 

4171 ] = util.defaultdict(list) 

4172 

4173 def set_union(x, y): 

4174 return x.union(y) 

4175 

4176 for table in self._sorted_tables: 

4177 cols = set(table.c) 

4178 

4179 for m in self.iterate_to_root(): 

4180 if m._inherits_equated_pairs and cols.intersection( 

4181 reduce( 

4182 set_union, 

4183 [l.proxy_set for l, r in m._inherits_equated_pairs], 

4184 ) 

4185 ): 

4186 result[table].append((m, m._inherits_equated_pairs)) 

4187 

4188 return result 

4189 

4190 

4191class _OptGetColumnsNotAvailable(Exception): 

4192 pass 

4193 

4194 

4195def configure_mappers() -> None: 

4196 """Initialize the inter-mapper relationships of all mappers that 

4197 have been constructed thus far across all :class:`_orm.registry` 

4198 collections. 

4199 

4200 The configure step is used to reconcile and initialize the 

4201 :func:`_orm.relationship` linkages between mapped classes, as well as to 

4202 invoke configuration events such as the 

4203 :meth:`_orm.MapperEvents.before_configured` and 

4204 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM 

4205 extensions or user-defined extension hooks. 

4206 

4207 Mapper configuration is normally invoked automatically, the first time 

4208 mappings from a particular :class:`_orm.registry` are used, as well as 

4209 whenever mappings are used and additional not-yet-configured mappers have 

4210 been constructed. The automatic configuration process however is local only 

4211 to the :class:`_orm.registry` involving the target mapper and any related 

4212 :class:`_orm.registry` objects which it may depend on; this is 

4213 equivalent to invoking the :meth:`_orm.registry.configure` method 

4214 on a particular :class:`_orm.registry`. 

4215 

4216 By contrast, the :func:`_orm.configure_mappers` function will invoke the 

4217 configuration process on all :class:`_orm.registry` objects that 

4218 exist in memory, and may be useful for scenarios where many individual 

4219 :class:`_orm.registry` objects that are nonetheless interrelated are 

4220 in use. 

4221 

4222 .. versionchanged:: 1.4 

4223 

4224 As of SQLAlchemy 1.4.0b2, this function works on a 

4225 per-:class:`_orm.registry` basis, locating all :class:`_orm.registry` 

4226 objects present and invoking the :meth:`_orm.registry.configure` method 

4227 on each. The :meth:`_orm.registry.configure` method may be preferred to 

4228 limit the configuration of mappers to those local to a particular 

4229 :class:`_orm.registry` and/or declarative base class. 

4230 

4231 Points at which automatic configuration is invoked include when a mapped 

4232 class is instantiated into an instance, as well as when ORM queries 

4233 are emitted using :meth:`.Session.query` or :meth:`_orm.Session.execute` 

4234 with an ORM-enabled statement. 

4235 

4236 The mapper configure process, whether invoked by 

4237 :func:`_orm.configure_mappers` or from :meth:`_orm.registry.configure`, 

4238 provides several event hooks that can be used to augment the mapper 

4239 configuration step. These hooks include: 

4240 

4241 * :meth:`.MapperEvents.before_configured` - called once before 

4242 :func:`.configure_mappers` or :meth:`_orm.registry.configure` does any 

4243 work; this can be used to establish additional options, properties, or 

4244 related mappings before the operation proceeds. 

4245 

4246 * :meth:`.RegistryEvents.before_configured` - Like 

4247 :meth:`.MapperEvents.before_configured`, but local to a specific 

4248 :class:`_orm.registry`. 

4249 

4250 .. versionadded:: 2.1 - added :meth:`.RegistryEvents.before_configured` 

4251 

4252 * :meth:`.MapperEvents.mapper_configured` - called as each individual 

4253 :class:`_orm.Mapper` is configured within the process; will include all 

4254 mapper state except for backrefs set up by other mappers that are still 

4255 to be configured. 

4256 

4257 * :meth:`.MapperEvents.after_configured` - called once after 

4258 :func:`.configure_mappers` or :meth:`_orm.registry.configure` is 

4259 complete; at this stage, all :class:`_orm.Mapper` objects that fall 

4260 within the scope of the configuration operation will be fully configured. 

4261 Note that the calling application may still have other mappings that 

4262 haven't been produced yet, such as if they are in modules as yet 

4263 unimported, and may also have mappings that are still to be configured, 

4264 if they are in other :class:`_orm.registry` collections not part of the 

4265 current scope of configuration. 

4266 

4267 * :meth:`.RegistryEvents.after_configured` - Like 

4268 :meth:`.MapperEvents.after_configured`, but local to a specific 

4269 :class:`_orm.registry`. 

4270 

4271 .. versionadded:: 2.1 - added :meth:`.RegistryEvents.after_configured` 

4272 

4273 """ 

4274 

4275 _configure_registries(_all_registries(), cascade=True) 

4276 

4277 

4278def _configure_registries( 

4279 registries: Set[_RegistryType], cascade: bool 

4280) -> None: 

4281 for reg in registries: 

4282 if reg._new_mappers: 

4283 break 

4284 else: 

4285 return 

4286 

4287 with _CONFIGURE_MUTEX: 

4288 global _already_compiling 

4289 if _already_compiling: 

4290 return 

4291 _already_compiling = True 

4292 try: 

4293 # double-check inside mutex 

4294 for reg in registries: 

4295 if reg._new_mappers: 

4296 break 

4297 else: 

4298 return 

4299 

4300 Mapper.dispatch._for_class(Mapper).before_configured() # type: ignore[arg-type, call-arg, misc] # noqa: E501 

4301 

4302 # initialize properties on all mappers 

4303 # note that _mapper_registry is unordered, which 

4304 # may randomly conceal/reveal issues related to 

4305 # the order of mapper compilation 

4306 

4307 registries_configured = list( 

4308 _do_configure_registries(registries, cascade) 

4309 ) 

4310 

4311 finally: 

4312 _already_compiling = False 

4313 for reg in registries_configured: 

4314 reg.dispatch.after_configured(reg) 

4315 Mapper.dispatch._for_class(Mapper).after_configured() # type: ignore[arg-type, call-arg, misc] # noqa: E501 

4316 

4317 

4318@util.preload_module("sqlalchemy.orm.decl_api") 

4319def _do_configure_registries( 

4320 registries: Set[_RegistryType], cascade: bool 

4321) -> Iterator[registry]: 

4322 registry = util.preloaded.orm_decl_api.registry 

4323 

4324 orig = set(registries) 

4325 

4326 for reg in registry._recurse_with_dependencies(registries): 

4327 if reg._new_mappers: 

4328 reg.dispatch.before_configured(reg) 

4329 

4330 has_skip = False 

4331 

4332 for mapper in reg._mappers_to_configure(): 

4333 run_configure = None 

4334 

4335 for fn in mapper.dispatch.before_mapper_configured: 

4336 run_configure = fn(mapper, mapper.class_) 

4337 if run_configure is EXT_SKIP: 

4338 has_skip = True 

4339 break 

4340 if run_configure is EXT_SKIP: 

4341 continue 

4342 

4343 if getattr(mapper, "_configure_failed", False): 

4344 e = sa_exc.InvalidRequestError( 

4345 "One or more mappers failed to initialize - " 

4346 "can't proceed with initialization of other " 

4347 "mappers. Triggering mapper: '%s'. " 

4348 "Original exception was: %s" 

4349 % (mapper, mapper._configure_failed) 

4350 ) 

4351 e._configure_failed = mapper._configure_failed # type: ignore[attr-defined] # noqa: E501 

4352 raise e 

4353 

4354 if not mapper.configured: 

4355 try: 

4356 mapper._post_configure_properties() 

4357 mapper._expire_memoizations() 

4358 mapper.dispatch.mapper_configured(mapper, mapper.class_) 

4359 except Exception: 

4360 exc = sys.exc_info()[1] 

4361 if not hasattr(exc, "_configure_failed"): 

4362 mapper._configure_failed = exc 

4363 raise 

4364 

4365 if reg._new_mappers: 

4366 yield reg 

4367 if not has_skip: 

4368 reg._new_mappers = False 

4369 

4370 if not cascade and reg._dependencies.difference(orig): 

4371 raise sa_exc.InvalidRequestError( 

4372 "configure was called with cascade=False but " 

4373 "additional registries remain" 

4374 ) 

4375 

4376 

4377@util.preload_module("sqlalchemy.orm.decl_api") 

4378def _dispose_registries(registries: Set[_RegistryType], cascade: bool) -> None: 

4379 registry = util.preloaded.orm_decl_api.registry 

4380 

4381 orig = set(registries) 

4382 

4383 for reg in registry._recurse_with_dependents(registries): 

4384 if not cascade and reg._dependents.difference(orig): 

4385 raise sa_exc.InvalidRequestError( 

4386 "Registry has dependent registries that are not disposed; " 

4387 "pass cascade=True to clear these also" 

4388 ) 

4389 

4390 while reg._managers: 

4391 try: 

4392 manager, _ = reg._managers.popitem() 

4393 except KeyError: 

4394 # guard against race between while and popitem 

4395 pass 

4396 else: 

4397 reg._dispose_manager_and_mapper(manager) 

4398 

4399 reg._dependents.clear() 

4400 for dep in reg._dependencies: 

4401 dep._dependents.discard(reg) 

4402 reg._dependencies.clear() 

4403 # this wasn't done in the 1.3 clear_mappers() and in fact it 

4404 # was a bug, as it could cause configure_mappers() to invoke 

4405 # the "before_configured" event even though mappers had all been 

4406 # disposed. 

4407 reg._new_mappers = False 

4408 

4409 

4410def reconstructor(fn: _Fn) -> _Fn: 

4411 """Decorate a method as the 'reconstructor' hook. 

4412 

4413 Designates a single method as the "reconstructor", an ``__init__``-like 

4414 method that will be called by the ORM after the instance has been 

4415 loaded from the database or otherwise reconstituted. 

4416 

4417 .. tip:: 

4418 

4419 The :func:`_orm.reconstructor` decorator makes use of the 

4420 :meth:`_orm.InstanceEvents.load` event hook, which can be 

4421 used directly. 

4422 

4423 The reconstructor will be invoked with no arguments. Scalar 

4424 (non-collection) database-mapped attributes of the instance will 

4425 be available for use within the function. Eagerly-loaded 

4426 collections are generally not yet available and will usually only 

4427 contain the first element. ORM state changes made to objects at 

4428 this stage will not be recorded for the next flush() operation, so 

4429 the activity within a reconstructor should be conservative. 

4430 

4431 .. seealso:: 

4432 

4433 :meth:`.InstanceEvents.load` 

4434 

4435 """ 

4436 fn.__sa_reconstructor__ = True # type: ignore[attr-defined] 

4437 return fn 

4438 

4439 

4440def validates( 

4441 *names: str, include_removes: bool = False, include_backrefs: bool = True 

4442) -> Callable[[_Fn], _Fn]: 

4443 r"""Decorate a method as a 'validator' for one or more named properties. 

4444 

4445 Designates a method as a validator, a method which receives the 

4446 name of the attribute as well as a value to be assigned, or in the 

4447 case of a collection, the value to be added to the collection. 

4448 The function can then raise validation exceptions to halt the 

4449 process from continuing (where Python's built-in ``ValueError`` 

4450 and ``AssertionError`` exceptions are reasonable choices), or can 

4451 modify or replace the value before proceeding. The function should 

4452 otherwise return the given value. 

4453 

4454 When a subclass overrides a validator for the same attribute using 

4455 the same method name, only the subclass validator is invoked. The 

4456 subclass validator may call ``super()`` to also invoke the parent 

4457 class validator. 

4458 

4459 A subclass that overrides a validator using the same method name 

4460 as the parent now replaces the parent validator entirely, rather 

4461 than the parent validator being invoked unconditionally. The 

4462 subclass validator may opt in to the parent's behavior by calling 

4463 ``super()``. See :ref:`validators_subclass_override` for 

4464 background on this change. 

4465 

4466 .. versionchanged:: 2.1 Added support for overriding of validators 

4467 on subclasses. 

4468 

4469 Note that a validator for a collection **cannot** issue a load of that 

4470 collection within the validation routine - this usage raises 

4471 an assertion to avoid recursion overflows. This is a reentrant 

4472 condition which is not supported. 

4473 

4474 :param \*names: list of attribute names to be validated. 

4475 :param include_removes: if True, "remove" events will be 

4476 sent as well - the validation function must accept an additional 

4477 argument "is_remove" which will be a boolean. 

4478 

4479 :param include_backrefs: defaults to ``True``; if ``False``, the 

4480 validation function will not emit if the originator is an attribute 

4481 event related via a backref. This can be used for bi-directional 

4482 :func:`.validates` usage where only one validator should emit per 

4483 attribute operation. 

4484 

4485 .. versionchanged:: 2.0.16 This parameter inadvertently defaulted to 

4486 ``False`` for releases 2.0.0 through 2.0.15. Its correct default 

4487 of ``True`` is restored in 2.0.16. 

4488 

4489 .. seealso:: 

4490 

4491 :ref:`simple_validators` - usage examples for :func:`.validates` 

4492 

4493 :ref:`validators_subclass_override` - overriding validators in 

4494 subclasses 

4495 

4496 """ 

4497 

4498 def wrap(fn: _Fn) -> _Fn: 

4499 fn.__sa_validators__ = names # type: ignore[attr-defined] 

4500 fn.__sa_validation_opts__ = { # type: ignore[attr-defined] 

4501 "include_removes": include_removes, 

4502 "include_backrefs": include_backrefs, 

4503 } 

4504 return fn 

4505 

4506 return wrap 

4507 

4508 

4509def _event_on_load(state, ctx): 

4510 instrumenting_mapper = state.manager.mapper 

4511 

4512 if instrumenting_mapper._reconstructor: 

4513 instrumenting_mapper._reconstructor(state.obj()) 

4514 

4515 

4516def _event_on_init(state, args, kwargs): 

4517 """Run init_instance hooks. 

4518 

4519 This also includes mapper compilation, normally not needed 

4520 here but helps with some piecemeal configuration 

4521 scenarios (such as in the ORM tutorial). 

4522 

4523 """ 

4524 

4525 instrumenting_mapper = state.manager.mapper 

4526 if instrumenting_mapper: 

4527 instrumenting_mapper._check_configure() 

4528 if instrumenting_mapper._set_polymorphic_identity: 

4529 instrumenting_mapper._set_polymorphic_identity(state) 

4530 

4531 

4532class _ColumnMapping(Dict["ColumnElement[Any]", "MapperProperty[Any]"]): 

4533 """Error reporting helper for mapper._columntoproperty.""" 

4534 

4535 __slots__ = ("mapper",) 

4536 

4537 def __init__(self, mapper): 

4538 # TODO: weakref would be a good idea here 

4539 self.mapper = mapper 

4540 

4541 def __missing__(self, column): 

4542 prop = self.mapper._props.get(column) 

4543 if prop: 

4544 raise orm_exc.UnmappedColumnError( 

4545 "Column '%s.%s' is not available, due to " 

4546 "conflicting property '%s':%r" 

4547 % (column.table.name, column.name, column.key, prop) 

4548 ) 

4549 raise orm_exc.UnmappedColumnError( 

4550 "No column %s is configured on mapper %s..." 

4551 % (column, self.mapper) 

4552 )