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

1411 statements  

1# orm/mapper.py 

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

3# <see AUTHORS file> 

4# 

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

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

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

8 

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

10 

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

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

13 

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

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

16 

17""" 

18from __future__ import annotations 

19 

20from collections import deque 

21from functools import reduce 

22from itertools import chain 

23import sys 

24import threading 

25from typing import Any 

26from typing import Callable 

27from typing import cast 

28from typing import Collection 

29from typing import Deque 

30from typing import Dict 

31from typing import FrozenSet 

32from typing import Generic 

33from typing import Iterable 

34from typing import Iterator 

35from typing import List 

36from typing import Mapping 

37from typing import Optional 

38from typing import Sequence 

39from typing import Set 

40from typing import Tuple 

41from typing import Type 

42from typing import TYPE_CHECKING 

43from typing import TypeVar 

44from typing import Union 

45import weakref 

46 

47from . import attributes 

48from . import exc as orm_exc 

49from . import instrumentation 

50from . import loading 

51from . import properties 

52from . import util as orm_util 

53from ._typing import _O 

54from .base import _class_to_mapper 

55from .base import _parse_mapper_argument 

56from .base import _state_mapper 

57from .base import PassiveFlag 

58from .base import state_str 

59from .interfaces import _MappedAttribute 

60from .interfaces import EXT_SKIP 

61from .interfaces import InspectionAttr 

62from .interfaces import MapperProperty 

63from .interfaces import ORMEntityColumnsClauseRole 

64from .interfaces import ORMFromClauseRole 

65from .interfaces import StrategizedProperty 

66from .path_registry import PathRegistry 

67from .. import event 

68from .. import exc as sa_exc 

69from .. import inspection 

70from .. import log 

71from .. import schema 

72from .. import sql 

73from .. import util 

74from ..event import dispatcher 

75from ..event import EventTarget 

76from ..sql import base as sql_base 

77from ..sql import coercions 

78from ..sql import expression 

79from ..sql import operators 

80from ..sql import roles 

81from ..sql import TableClause 

82from ..sql import util as sql_util 

83from ..sql import visitors 

84from ..sql.cache_key import MemoizedHasCacheKey 

85from ..sql.elements import KeyedColumnElement 

86from ..sql.schema import Column 

87from ..sql.schema import Table 

88from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

89from ..util import HasMemoized 

90from ..util import HasMemoized_ro_memoized_attribute 

91from ..util.typing import Literal 

92from ..util.typing import TupleAny 

93from ..util.typing import Unpack 

94 

95if TYPE_CHECKING: 

96 from ._typing import _IdentityKeyType 

97 from ._typing import _InstanceDict 

98 from ._typing import _ORMColumnExprArgument 

99 from ._typing import _RegistryType 

100 from .decl_api import registry 

101 from .dependency import _DependencyProcessor 

102 from .descriptor_props import CompositeProperty 

103 from .descriptor_props import SynonymProperty 

104 from .events import MapperEvents 

105 from .instrumentation import ClassManager 

106 from .path_registry import _CachingEntityRegistry 

107 from .properties import ColumnProperty 

108 from .relationships import RelationshipProperty 

109 from .state import InstanceState 

110 from .util import ORMAdapter 

111 from ..engine import Row 

112 from ..engine import RowMapping 

113 from ..sql._typing import _ColumnExpressionArgument 

114 from ..sql._typing import _EquivalentColumnMap 

115 from ..sql.base import ReadOnlyColumnCollection 

116 from ..sql.elements import ColumnClause 

117 from ..sql.elements import ColumnElement 

118 from ..sql.selectable import FromClause 

119 from ..util import OrderedSet 

120 

121 

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

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

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

125 

126 

127_WithPolymorphicArg = Union[ 

128 Literal["*"], 

129 Tuple[ 

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

131 Optional["FromClause"], 

132 ], 

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

134] 

135 

136 

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

138 weakref.WeakKeyDictionary() 

139) 

140 

141 

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

143 with _CONFIGURE_MUTEX: 

144 return set(_mapper_registries) 

145 

146 

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

148 for reg in _all_registries(): 

149 yield from reg._mappers_to_configure() 

150 

151 

152_already_compiling = False 

153 

154 

155# a constant returned by _get_attr_by_column to indicate 

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

157# column 

158NO_ATTRIBUTE = util.symbol("NO_ATTRIBUTE") 

159 

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

161_CONFIGURE_MUTEX = threading.RLock() 

162 

163 

164@inspection._self_inspects 

165@log.class_logger 

166class Mapper( 

167 ORMFromClauseRole, 

168 ORMEntityColumnsClauseRole[_O], 

169 MemoizedHasCacheKey, 

170 InspectionAttr, 

171 log.Identified, 

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

173 EventTarget, 

174 Generic[_O], 

175): 

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

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

178 proceed. 

179 

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

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

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

183 :ref:`orm_mapping_classes_toplevel`. 

184 

185 """ 

186 

187 dispatch: dispatcher[Mapper[_O]] 

188 

189 _dispose_called = False 

190 _configure_failed: Any = False 

191 _ready_for_configure = False 

192 

193 def __init__( 

194 self, 

195 class_: Type[_O], 

196 local_table: Optional[FromClause] = None, 

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

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

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

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

201 inherit_foreign_keys: Optional[ 

202 Sequence[_ORMColumnExprArgument[Any]] 

203 ] = None, 

204 always_refresh: bool = False, 

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

206 version_id_generator: Optional[ 

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

208 ] = None, 

209 polymorphic_on: Optional[ 

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

211 ] = None, 

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

213 polymorphic_identity: Optional[Any] = None, 

214 concrete: bool = False, 

215 with_polymorphic: Optional[_WithPolymorphicArg] = None, 

216 polymorphic_abstract: bool = False, 

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

218 allow_partial_pks: bool = True, 

219 batch: bool = True, 

220 column_prefix: Optional[str] = None, 

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

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

223 passive_updates: bool = True, 

224 passive_deletes: bool = False, 

225 confirm_deleted_rows: bool = True, 

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

227 legacy_is_orphan: bool = False, 

228 _compiled_cache_size: int = 100, 

229 ): 

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

231 

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

233 is normally invoked through the 

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

235 :ref:`Declarative <orm_declarative_mapping>` or 

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

237 

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

239 removed; for a classical mapping configuration, use the 

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

241 

242 Parameters documented below may be passed to either the 

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

244 ``__mapper_args__`` declarative class attribute described at 

245 :ref:`orm_declarative_mapper_options`. 

246 

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

248 this argument is automatically passed as the declared class 

249 itself. 

250 

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

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

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

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

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

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

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

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

259 present. 

260 

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

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

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

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

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

266 inheritance scheme which uses 

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

268 

269 .. versionadded:: 2.0 

270 

271 .. seealso:: 

272 

273 :ref:`orm_inheritance_abstract_poly` 

274 

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

276 class will overwrite all data within object instances that already 

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

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

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

280 :meth:`_query.Query.populate_existing`. 

281 

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

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

284 possibly existing within the database. This affects whether a 

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

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

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

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

289 

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

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

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

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

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

295 

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

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

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

299 has partial NULL values will not be emitted. 

300 

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

302 of multiple entities can be batched together for efficiency. 

303 Setting to False indicates 

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

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

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

307 in between individual row persistence operations. 

308 

309 :param column_prefix: A string which will be prepended 

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

311 objects are automatically assigned as attributes to the 

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

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

314 dictionary. 

315 

316 This parameter is typically useful with imperative mappings 

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

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

319 ``user_id``, ``user_name``, and ``password``:: 

320 

321 class User(Base): 

322 __table__ = user_table 

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

324 

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

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

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

328 

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

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

331 approach to automating a naming scheme is to intercept the 

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

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

334 pattern. 

335 

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

337 table inheritance with its parent mapper. 

338 

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

340 

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

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

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

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

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

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

347 exception in a future release. 

348 

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

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

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

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

353 are needed immediately before the flush completes. 

354 

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

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

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

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

359 

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

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

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

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

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

365 attributes are not to be accessed in any case. 

366 

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

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

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

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

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

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

373 defaults will not be fetched. 

374 

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

376 :paramref:`.Mapper.eager_defaults` 

377 

378 .. seealso:: 

379 

380 :ref:`orm_server_defaults` 

381 

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

383 INSERTed at once using the 

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

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

386 feature to be very performant on supporting backends. 

387 

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

389 be excluded from mapping. 

390 

391 .. seealso:: 

392 

393 :ref:`include_exclude_cols` 

394 

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

396 names to map. 

397 

398 .. seealso:: 

399 

400 :ref:`include_exclude_cols` 

401 

402 :param inherits: A mapped class or the corresponding 

403 :class:`_orm.Mapper` 

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

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

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

407 is passed automatically as a result of the natural class 

408 hierarchy of the declared classes. 

409 

410 .. seealso:: 

411 

412 :ref:`inheritance_toplevel` 

413 

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

415 expression which will 

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

417 between the two tables. 

418 

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

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

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

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

423 

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

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

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

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

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

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

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

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

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

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

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

435 orphan object has been flushed yet or not. 

436 

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

438 for more detail on this change. 

439 

440 :param passive_deletes: Indicates DELETE behavior of foreign key 

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

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

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

444 on the superclass mapper. 

445 

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

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

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

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

450 superclass table, and not this table. 

451 

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

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

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

455 validate these attributes; note that the primary key columns 

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

457 the object as a whole. 

458 

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

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

461 to specify passive_deletes without this taking effect for 

462 all subclass mappers. 

463 

464 .. seealso:: 

465 

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

467 used with :func:`_orm.relationship` 

468 

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

470 CASCADE for joined-table inheritance mappers 

471 

472 :param passive_updates: Indicates UPDATE behavior of foreign key 

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

474 inheritance mapping. Defaults to ``True``. 

475 

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

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

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

479 on joined-table rows. 

480 

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

482 referential integrity and will not be issuing its own CASCADE 

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

484 emit an UPDATE statement for the dependent columns during a 

485 primary key change. 

486 

487 .. seealso:: 

488 

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

490 used with :func:`_orm.relationship` 

491 

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

493 CASCADE for joined-table inheritance mappers 

494 

495 :param polymorphic_load: Specifies "polymorphic loading" behavior 

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

497 table inheritance only). Valid values are: 

498 

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

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

501 in a SELECT query against the base. 

502 

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

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

505 the columns specific to this subclass. The SELECT uses 

506 IN to fetch multiple subclasses at once. 

507 

508 .. seealso:: 

509 

510 :ref:`with_polymorphic_mapper_config` 

511 

512 :ref:`polymorphic_selectin` 

513 

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

515 SQL expression used to determine the target class for an 

516 incoming row, when inheriting classes are present. 

517 

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

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

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

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

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

523 

524 class Employee(Base): 

525 __tablename__ = "employee" 

526 

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

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

529 

530 __mapper_args__ = { 

531 "polymorphic_on": discriminator, 

532 "polymorphic_identity": "employee", 

533 } 

534 

535 It may also be specified 

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

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

538 approach:: 

539 

540 class Employee(Base): 

541 __tablename__ = "employee" 

542 

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

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

545 

546 __mapper_args__ = { 

547 "polymorphic_on": case( 

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

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

550 else_="employee", 

551 ), 

552 "polymorphic_identity": "employee", 

553 } 

554 

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

556 which is of particular use when using annotated column 

557 configurations:: 

558 

559 class Employee(Base): 

560 __tablename__ = "employee" 

561 

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

563 discriminator: Mapped[str] 

564 

565 __mapper_args__ = { 

566 "polymorphic_on": "discriminator", 

567 "polymorphic_identity": "employee", 

568 } 

569 

570 When setting ``polymorphic_on`` to reference an 

571 attribute or expression that's not present in the 

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

573 of the discriminator should be persisted to the database, 

574 the value of the 

575 discriminator is not automatically set on new 

576 instances; this must be handled by the user, 

577 either through manual means or via event listeners. 

578 A typical approach to establishing such a listener 

579 looks like:: 

580 

581 from sqlalchemy import event 

582 from sqlalchemy.orm import object_mapper 

583 

584 

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

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

587 mapper = object_mapper(instance) 

588 instance.discriminator = mapper.polymorphic_identity 

589 

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

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

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

593 in the database. 

594 

595 .. warning:: 

596 

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

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

599 columns are not yet supported. 

600 

601 .. seealso:: 

602 

603 :ref:`inheritance_toplevel` 

604 

605 :param polymorphic_identity: Specifies the value which 

606 identifies this particular class as returned by the column expression 

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

608 rows are received, the value corresponding to the 

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

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

611 reconstructed object. 

612 

613 .. seealso:: 

614 

615 :ref:`inheritance_toplevel` 

616 

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

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

619 persistence behavior of that attribute. Note that 

620 :class:`_schema.Column` 

621 objects present in 

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

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

624 When using Declarative, this argument is passed automatically, 

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

626 in the declared class body. 

627 

628 .. seealso:: 

629 

630 :ref:`orm_mapping_properties` - in the 

631 :ref:`orm_mapping_classes_toplevel` 

632 

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

634 objects, or alternatively string names of attribute names which 

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

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

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

638 can be overridden here. 

639 

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

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

642 

643 .. seealso:: 

644 

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

646 

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

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

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

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

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

652 version id, a 

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

654 thrown. 

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

656 unless ``version_id_generator`` specifies an alternative version 

657 generator. 

658 

659 .. seealso:: 

660 

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

662 and rationale. 

663 

664 :param version_id_generator: Define how new version ids should 

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

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

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

668 

669 def generate_version(version): 

670 return next_version 

671 

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

673 or programmatic versioning schemes outside of the version id 

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

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

676 of important points when using this option. 

677 

678 .. seealso:: 

679 

680 :ref:`custom_version_counter` 

681 

682 :ref:`server_side_version_counter` 

683 

684 

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

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

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

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

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

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

691 loaded immediately. The second tuple argument <selectable> 

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

693 classes. 

694 

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

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

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

698 indicating polymorphic loading styles. 

699 

700 .. seealso:: 

701 

702 :ref:`with_polymorphic_mapper_config` 

703 

704 """ 

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

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

707 self.class_.__module__, 

708 self.class_.__name__, 

709 ) 

710 

711 self._primary_key_argument = util.to_list(primary_key) 

712 

713 self.always_refresh = always_refresh 

714 

715 if isinstance(version_id_col, MapperProperty): 

716 self.version_id_prop = version_id_col 

717 self.version_id_col = None 

718 else: 

719 self.version_id_col = ( 

720 coercions.expect( 

721 roles.ColumnArgumentOrKeyRole, 

722 version_id_col, 

723 argname="version_id_col", 

724 ) 

725 if version_id_col is not None 

726 else None 

727 ) 

728 

729 if version_id_generator is False: 

730 self.version_id_generator = False 

731 elif version_id_generator is None: 

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

733 else: 

734 self.version_id_generator = version_id_generator 

735 

736 self.concrete = concrete 

737 self.single = False 

738 

739 if inherits is not None: 

740 self.inherits = _parse_mapper_argument(inherits) 

741 else: 

742 self.inherits = None 

743 

744 if local_table is not None: 

745 self.local_table = coercions.expect( 

746 roles.FromClauseRole, 

747 local_table, 

748 disable_inspection=True, 

749 argname="local_table", 

750 ) 

751 elif self.inherits: 

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

753 # .local_table need not be Optional 

754 self.local_table = self.inherits.local_table 

755 self.single = True 

756 else: 

757 raise sa_exc.ArgumentError( 

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

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

760 ) 

761 

762 if inherit_condition is not None: 

763 self.inherit_condition = coercions.expect( 

764 roles.OnClauseRole, inherit_condition 

765 ) 

766 else: 

767 self.inherit_condition = None 

768 

769 self.inherit_foreign_keys = inherit_foreign_keys 

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

771 self._delete_orphans = [] 

772 self.batch = batch 

773 self.eager_defaults = eager_defaults 

774 self.column_prefix = column_prefix 

775 

776 # interim - polymorphic_on is further refined in 

777 # _configure_polymorphic_setter 

778 self.polymorphic_on = ( 

779 coercions.expect( # type: ignore 

780 roles.ColumnArgumentOrKeyRole, 

781 polymorphic_on, 

782 argname="polymorphic_on", 

783 ) 

784 if polymorphic_on is not None 

785 else None 

786 ) 

787 self.polymorphic_abstract = polymorphic_abstract 

788 self._dependency_processors = [] 

789 self.validators = util.EMPTY_DICT 

790 self.passive_updates = passive_updates 

791 self.passive_deletes = passive_deletes 

792 self.legacy_is_orphan = legacy_is_orphan 

793 self._clause_adapter = None 

794 self._requires_row_aliasing = False 

795 self._inherits_equated_pairs = None 

796 self._memoized_values = {} 

797 self._compiled_cache_size = _compiled_cache_size 

798 self._reconstructor = None 

799 self.allow_partial_pks = allow_partial_pks 

800 

801 if self.inherits and not self.concrete: 

802 self.confirm_deleted_rows = False 

803 else: 

804 self.confirm_deleted_rows = confirm_deleted_rows 

805 

806 self._set_with_polymorphic(with_polymorphic) 

807 self.polymorphic_load = polymorphic_load 

808 

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

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

811 # the object instance for that row. 

812 self.polymorphic_identity = polymorphic_identity 

813 

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

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

816 # upon a select operation. 

817 if _polymorphic_map is None: 

818 self.polymorphic_map = {} 

819 else: 

820 self.polymorphic_map = _polymorphic_map 

821 

822 if include_properties is not None: 

823 self.include_properties = util.to_set(include_properties) 

824 else: 

825 self.include_properties = None 

826 if exclude_properties: 

827 self.exclude_properties = util.to_set(exclude_properties) 

828 else: 

829 self.exclude_properties = None 

830 

831 # prevent this mapper from being constructed 

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

833 # configure_mappers() until construction succeeds) 

834 with _CONFIGURE_MUTEX: 

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

836 class_, self 

837 ) 

838 self._configure_inheritance() 

839 self._configure_class_instrumentation() 

840 self._configure_properties() 

841 self._configure_polymorphic_setter() 

842 self._configure_pks() 

843 self.registry._flag_new_mapper(self) 

844 self._log("constructed") 

845 self._expire_memoizations() 

846 

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

848 

849 def _prefer_eager_defaults(self, dialect, table): 

850 if self.eager_defaults == "auto": 

851 if not table.implicit_returning: 

852 return False 

853 

854 return ( 

855 table in self._server_default_col_keys 

856 and dialect.insert_executemany_returning 

857 ) 

858 else: 

859 return self.eager_defaults 

860 

861 def _gen_cache_key(self, anon_map, bindparams): 

862 return (self,) 

863 

864 # ### BEGIN 

865 # ATTRIBUTE DECLARATIONS START HERE 

866 

867 is_mapper = True 

868 """Part of the inspection API.""" 

869 

870 represents_outer_join = False 

871 

872 registry: _RegistryType 

873 

874 @property 

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

876 """Part of the inspection API. 

877 

878 Returns self. 

879 

880 """ 

881 return self 

882 

883 @property 

884 def entity(self): 

885 r"""Part of the inspection API. 

886 

887 Returns self.class\_. 

888 

889 """ 

890 return self.class_ 

891 

892 class_: Type[_O] 

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

894 

895 _identity_class: Type[_O] 

896 

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

898 _dependency_processors: List[_DependencyProcessor] 

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

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

901 _all_tables: Set[TableClause] 

902 _polymorphic_attr_key: Optional[str] 

903 

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

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

906 

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

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

909 

910 _columntoproperty: _ColumnMapping 

911 

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

913 _validate_polymorphic_identity: Optional[ 

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

915 ] 

916 

917 tables: Sequence[TableClause] 

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

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

920 is aware of. 

921 

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

923 :class:`_expression.Alias` 

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

925 :class:`_schema.Table` 

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

927 

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

929 Behavior is undefined if directly modified. 

930 

931 """ 

932 

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

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

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

936 

937 The dictionary contains string attribute names as keys 

938 mapped to the actual validation method. 

939 

940 """ 

941 

942 always_refresh: bool 

943 allow_partial_pks: bool 

944 version_id_col: Optional[ColumnElement[Any]] 

945 

946 with_polymorphic: Optional[ 

947 Tuple[ 

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

949 Optional[FromClause], 

950 ] 

951 ] 

952 

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

954 

955 local_table: FromClause 

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

957 :class:`_orm.Mapper` refers. 

958 

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

960 :class:`.FromClause`. 

961 

962 The "local" table is the 

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

964 managing from an attribute access and flush perspective. For 

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

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

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

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

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

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

971 

972 .. seealso:: 

973 

974 :attr:`_orm.Mapper.persist_selectable`. 

975 

976 :attr:`_orm.Mapper.selectable`. 

977 

978 """ 

979 

980 persist_selectable: FromClause 

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

982 is mapped. 

983 

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

985 :class:`.FromClause`. 

986 

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

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

989 represents the inheriting class hierarchy overall in an inheritance 

990 scenario. 

991 

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

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

994 alternate subquery used for selecting columns. 

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

996 will be written on a persist operation. 

997 

998 .. seealso:: 

999 

1000 :attr:`_orm.Mapper.selectable`. 

1001 

1002 :attr:`_orm.Mapper.local_table`. 

1003 

1004 """ 

1005 

1006 inherits: Optional[Mapper[Any]] 

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

1008 inherits from, if any. 

1009 

1010 """ 

1011 

1012 inherit_condition: Optional[ColumnElement[bool]] 

1013 

1014 configured: bool = False 

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

1016 

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

1018 Behavior is undefined if directly modified. 

1019 

1020 .. seealso:: 

1021 

1022 :func:`.configure_mappers`. 

1023 

1024 """ 

1025 

1026 concrete: bool 

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

1028 inheritance mapper. 

1029 

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

1031 Behavior is undefined if directly modified. 

1032 

1033 """ 

1034 

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

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

1037 objects 

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

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

1040 

1041 This list is against the selectable in 

1042 :attr:`_orm.Mapper.persist_selectable`. 

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

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

1045 :class:`_expression.Join`, the 

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

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

1048 

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

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

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

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

1053 

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

1055 Behavior is undefined if directly modified. 

1056 

1057 """ 

1058 

1059 class_manager: ClassManager[_O] 

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

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

1062 

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

1064 Behavior is undefined if directly modified. 

1065 

1066 """ 

1067 

1068 single: bool 

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

1070 inheritance mapper. 

1071 

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

1073 

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

1075 Behavior is undefined if directly modified. 

1076 

1077 """ 

1078 

1079 polymorphic_on: Optional[KeyedColumnElement[Any]] 

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

1081 ``polymorphic_on`` argument 

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

1083 

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

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

1086 :func:`.cast`. 

1087 

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

1089 Behavior is undefined if directly modified. 

1090 

1091 """ 

1092 

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

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

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

1096 

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

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

1099 

1100 An inheritance chain of mappers will all reference the same 

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

1102 result rows to target mappers. 

1103 

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

1105 Behavior is undefined if directly modified. 

1106 

1107 """ 

1108 

1109 polymorphic_identity: Optional[Any] 

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

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

1112 

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

1114 comparable to the type of column represented by 

1115 :attr:`_orm.Mapper.polymorphic_on`. 

1116 

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

1118 Behavior is undefined if directly modified. 

1119 

1120 """ 

1121 

1122 base_mapper: Mapper[Any] 

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

1124 

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

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

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

1128 objects in the inheritance chain. 

1129 

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

1131 Behavior is undefined if directly modified. 

1132 

1133 """ 

1134 

1135 columns: ReadOnlyColumnCollection[str, Column[Any]] 

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

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

1138 

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

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

1141 except that only those columns included in 

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

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

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

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

1146 

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

1148 Behavior is undefined if directly modified. 

1149 

1150 """ 

1151 

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

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

1154 

1155 @util.memoized_property 

1156 def _path_registry(self) -> _CachingEntityRegistry: 

1157 return PathRegistry.per_mapper(self) 

1158 

1159 def _configure_inheritance(self): 

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

1161 being present.""" 

1162 

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

1164 self._inheriting_mappers = util.WeakSequence() 

1165 

1166 if self.inherits: 

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

1168 raise sa_exc.ArgumentError( 

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

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

1171 ) 

1172 

1173 self.dispatch._update(self.inherits.dispatch) 

1174 

1175 if self.single: 

1176 self.persist_selectable = self.inherits.persist_selectable 

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

1178 if self.concrete: 

1179 self.persist_selectable = self.local_table 

1180 for mapper in self.iterate_to_root(): 

1181 if mapper.polymorphic_on is not None: 

1182 mapper._requires_row_aliasing = True 

1183 else: 

1184 if self.inherit_condition is None: 

1185 # figure out inherit condition from our table to the 

1186 # immediate table of the inherited mapper, not its 

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

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

1189 try: 

1190 self.inherit_condition = sql_util.join_condition( 

1191 self.inherits.local_table, self.local_table 

1192 ) 

1193 except sa_exc.NoForeignKeysError as nfe: 

1194 assert self.inherits.local_table is not None 

1195 assert self.local_table is not None 

1196 raise sa_exc.NoForeignKeysError( 

1197 "Can't determine the inherit condition " 

1198 "between inherited table '%s' and " 

1199 "inheriting " 

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

1201 "foreign key relationships established. " 

1202 "Please ensure the inheriting table has " 

1203 "a foreign key relationship to the " 

1204 "inherited " 

1205 "table, or provide an " 

1206 "'on clause' using " 

1207 "the 'inherit_condition' mapper argument." 

1208 % ( 

1209 self.inherits.local_table.description, 

1210 self.local_table.description, 

1211 ) 

1212 ) from nfe 

1213 except sa_exc.AmbiguousForeignKeysError as afe: 

1214 assert self.inherits.local_table is not None 

1215 assert self.local_table is not None 

1216 raise sa_exc.AmbiguousForeignKeysError( 

1217 "Can't determine the inherit condition " 

1218 "between inherited table '%s' and " 

1219 "inheriting " 

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

1221 "foreign key relationship established. " 

1222 "Please specify the 'on clause' using " 

1223 "the 'inherit_condition' mapper argument." 

1224 % ( 

1225 self.inherits.local_table.description, 

1226 self.local_table.description, 

1227 ) 

1228 ) from afe 

1229 assert self.inherits.persist_selectable is not None 

1230 self.persist_selectable = sql.join( 

1231 self.inherits.persist_selectable, 

1232 self.local_table, 

1233 self.inherit_condition, 

1234 ) 

1235 

1236 fks = util.to_set(self.inherit_foreign_keys) 

1237 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 

1238 self.persist_selectable.onclause, 

1239 consider_as_foreign_keys=fks, 

1240 ) 

1241 else: 

1242 self.persist_selectable = self.local_table 

1243 

1244 if self.polymorphic_identity is None: 

1245 self._identity_class = self.class_ 

1246 

1247 if ( 

1248 not self.polymorphic_abstract 

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

1250 ): 

1251 util.warn( 

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

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

1254 f"'polymorphic_on' column of " 

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

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

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

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

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

1260 "class unmapped when using Declarative, set the " 

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

1262 ) 

1263 elif self.concrete: 

1264 self._identity_class = self.class_ 

1265 else: 

1266 self._identity_class = self.inherits._identity_class 

1267 

1268 if self.version_id_col is None: 

1269 self.version_id_col = self.inherits.version_id_col 

1270 self.version_id_generator = self.inherits.version_id_generator 

1271 elif ( 

1272 self.inherits.version_id_col is not None 

1273 and self.version_id_col is not self.inherits.version_id_col 

1274 ): 

1275 util.warn( 

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

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

1278 "the inherited versioning column. " 

1279 "version_id_col should only be specified on " 

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

1281 % ( 

1282 self.version_id_col.description, 

1283 self.inherits.version_id_col.description, 

1284 ) 

1285 ) 

1286 

1287 self.polymorphic_map = self.inherits.polymorphic_map 

1288 self.batch = self.inherits.batch 

1289 self.inherits._inheriting_mappers.append(self) 

1290 self.base_mapper = self.inherits.base_mapper 

1291 self.passive_updates = self.inherits.passive_updates 

1292 self.passive_deletes = ( 

1293 self.inherits.passive_deletes or self.passive_deletes 

1294 ) 

1295 self._all_tables = self.inherits._all_tables 

1296 

1297 if self.polymorphic_identity is not None: 

1298 if self.polymorphic_identity in self.polymorphic_map: 

1299 util.warn( 

1300 "Reassigning polymorphic association for identity %r " 

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

1302 "value for polymorphic_identity." 

1303 % ( 

1304 self.polymorphic_identity, 

1305 self.polymorphic_map[self.polymorphic_identity], 

1306 self, 

1307 self.polymorphic_identity, 

1308 ) 

1309 ) 

1310 self.polymorphic_map[self.polymorphic_identity] = self 

1311 

1312 if self.polymorphic_load and self.concrete: 

1313 raise sa_exc.ArgumentError( 

1314 "polymorphic_load is not currently supported " 

1315 "with concrete table inheritance" 

1316 ) 

1317 if self.polymorphic_load == "inline": 

1318 self.inherits._add_with_polymorphic_subclass(self) 

1319 elif self.polymorphic_load == "selectin": 

1320 pass 

1321 elif self.polymorphic_load is not None: 

1322 raise sa_exc.ArgumentError( 

1323 "unknown argument for polymorphic_load: %r" 

1324 % self.polymorphic_load 

1325 ) 

1326 

1327 else: 

1328 self._all_tables = set() 

1329 self.base_mapper = self 

1330 assert self.local_table is not None 

1331 self.persist_selectable = self.local_table 

1332 if self.polymorphic_identity is not None: 

1333 self.polymorphic_map[self.polymorphic_identity] = self 

1334 self._identity_class = self.class_ 

1335 

1336 if self.persist_selectable is None: 

1337 raise sa_exc.ArgumentError( 

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

1339 % self 

1340 ) 

1341 

1342 def _set_with_polymorphic( 

1343 self, with_polymorphic: Optional[_WithPolymorphicArg] 

1344 ) -> None: 

1345 if with_polymorphic == "*": 

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

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

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

1349 self.with_polymorphic = cast( 

1350 """Tuple[ 

1351 Union[ 

1352 Literal["*"], 

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

1354 ], 

1355 Optional["FromClause"], 

1356 ]""", 

1357 with_polymorphic, 

1358 ) 

1359 else: 

1360 self.with_polymorphic = (with_polymorphic, None) 

1361 elif with_polymorphic is not None: 

1362 raise sa_exc.ArgumentError( 

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

1364 ) 

1365 else: 

1366 self.with_polymorphic = None 

1367 

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

1369 self.with_polymorphic = ( 

1370 self.with_polymorphic[0], 

1371 coercions.expect( 

1372 roles.FromClauseRole, 

1373 self.with_polymorphic[1], 

1374 ), 

1375 ) 

1376 

1377 if self.configured: 

1378 self._expire_memoizations() 

1379 

1380 def _add_with_polymorphic_subclass(self, mapper): 

1381 subcl = mapper.class_ 

1382 if self.with_polymorphic is None: 

1383 self._set_with_polymorphic((subcl,)) 

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

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

1386 self._set_with_polymorphic( 

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

1388 ) 

1389 

1390 def _set_concrete_base(self, mapper): 

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

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

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

1394 

1395 assert self.concrete 

1396 assert not self.inherits 

1397 assert isinstance(mapper, Mapper) 

1398 self.inherits = mapper 

1399 self.inherits.polymorphic_map.update(self.polymorphic_map) 

1400 self.polymorphic_map = self.inherits.polymorphic_map 

1401 for mapper in self.iterate_to_root(): 

1402 if mapper.polymorphic_on is not None: 

1403 mapper._requires_row_aliasing = True 

1404 self.batch = self.inherits.batch 

1405 for mp in self.self_and_descendants: 

1406 mp.base_mapper = self.inherits.base_mapper 

1407 self.inherits._inheriting_mappers.append(self) 

1408 self.passive_updates = self.inherits.passive_updates 

1409 self._all_tables = self.inherits._all_tables 

1410 

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

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

1413 key, key, local=False, column=None 

1414 ): 

1415 self._adapt_inherited_property(key, prop, False) 

1416 

1417 def _set_polymorphic_on(self, polymorphic_on): 

1418 self.polymorphic_on = polymorphic_on 

1419 self._configure_polymorphic_setter(True) 

1420 

1421 def _configure_class_instrumentation(self): 

1422 """Associate this Mapper with the 

1423 given class and entity name. 

1424 

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

1426 name combination will return this mapper. Also decorate the 

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

1428 auto-session attachment logic. 

1429 

1430 """ 

1431 

1432 # we expect that declarative has applied the class manager 

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

1434 # this raises as of 2.0. 

1435 manager = attributes.opt_manager_of_class(self.class_) 

1436 

1437 if manager is None or not manager.registry: 

1438 raise sa_exc.InvalidRequestError( 

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

1440 "invoked directly outside of a declarative registry." 

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

1442 "function for a classical mapping." 

1443 ) 

1444 

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

1446 

1447 # this invokes the class_instrument event and sets up 

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

1449 # occur after the instrument_class event above. 

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

1451 # :( 

1452 

1453 manager = instrumentation.register_class( 

1454 self.class_, 

1455 mapper=self, 

1456 expired_attribute_loader=util.partial( 

1457 loading._load_scalar_attributes, self 

1458 ), 

1459 # finalize flag means instrument the __init__ method 

1460 # and call the class_instrument event 

1461 finalize=True, 

1462 ) 

1463 

1464 self.class_manager = manager 

1465 

1466 assert manager.registry is not None 

1467 self.registry = manager.registry 

1468 

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

1470 # e_name None or not. 

1471 if manager.mapper is None: 

1472 return 

1473 

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

1475 

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

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

1478 method = method._sa_original_init 

1479 if hasattr(method, "__func__"): 

1480 method = method.__func__ 

1481 if callable(method): 

1482 if hasattr(method, "__sa_reconstructor__"): 

1483 self._reconstructor = method 

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

1485 elif hasattr(method, "__sa_validators__"): 

1486 validation_opts = method.__sa_validation_opts__ 

1487 for name in method.__sa_validators__: 

1488 if name in self.validators: 

1489 raise sa_exc.InvalidRequestError( 

1490 "A validation function for mapped " 

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

1492 % (name, self) 

1493 ) 

1494 self.validators = self.validators.union( 

1495 {name: (method, validation_opts)} 

1496 ) 

1497 

1498 def _set_dispose_flags(self) -> None: 

1499 self.configured = True 

1500 self._ready_for_configure = True 

1501 self._dispose_called = True 

1502 

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

1504 

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

1506 try: 

1507 prop = self._props[key] 

1508 except KeyError as err: 

1509 raise sa_exc.ArgumentError( 

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

1511 "no attribute is mapped to this name." 

1512 ) from err 

1513 try: 

1514 expr = prop.expression 

1515 except AttributeError as ae: 

1516 raise sa_exc.ArgumentError( 

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

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

1519 ) from ae 

1520 if not isinstance(expr, Column): 

1521 raise sa_exc.ArgumentError( 

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

1523 "property does not refer to a single " 

1524 "mapped Column" 

1525 ) 

1526 return expr 

1527 

1528 def _configure_pks(self) -> None: 

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

1530 

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

1532 

1533 self._pks_by_table = {} 

1534 self._cols_by_table = {} 

1535 

1536 all_cols = util.column_set( 

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

1538 ) 

1539 

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

1541 

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

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

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

1545 # ordering is important since it determines the ordering of 

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

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

1548 fc.primary_key 

1549 ).intersection( 

1550 pk_cols 

1551 ) 

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

1553 all_cols 

1554 ) 

1555 

1556 if self._primary_key_argument: 

1557 coerced_pk_arg = [ 

1558 ( 

1559 self._str_arg_to_mapped_col("primary_key", c) 

1560 if isinstance(c, str) 

1561 else c 

1562 ) 

1563 for c in ( 

1564 coercions.expect( 

1565 roles.DDLConstraintColumnRole, 

1566 coerce_pk, 

1567 argname="primary_key", 

1568 ) 

1569 for coerce_pk in self._primary_key_argument 

1570 ) 

1571 ] 

1572 else: 

1573 coerced_pk_arg = None 

1574 

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

1576 # primary key mappings 

1577 if coerced_pk_arg: 

1578 for k in coerced_pk_arg: 

1579 if k.table not in self._pks_by_table: 

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

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

1582 

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

1584 elif ( 

1585 self.persist_selectable not in self._pks_by_table 

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

1587 ): 

1588 raise sa_exc.ArgumentError( 

1589 "Mapper %s could not assemble any primary " 

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

1591 % (self, self.persist_selectable.description) 

1592 ) 

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

1594 self.local_table, schema.Table 

1595 ): 

1596 util.warn( 

1597 "Could not assemble any primary " 

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

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

1600 % self.local_table.description 

1601 ) 

1602 

1603 if ( 

1604 self.inherits 

1605 and not self.concrete 

1606 and not self._primary_key_argument 

1607 ): 

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

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

1610 self.primary_key = self.inherits.primary_key 

1611 else: 

1612 # determine primary key from argument or persist_selectable pks 

1613 primary_key: Collection[ColumnElement[Any]] 

1614 

1615 if coerced_pk_arg: 

1616 primary_key = [ 

1617 cc if cc is not None else c 

1618 for cc, c in ( 

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

1620 for c in coerced_pk_arg 

1621 ) 

1622 ] 

1623 else: 

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

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

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

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

1628 primary_key = sql_util.reduce_columns( 

1629 self._pks_by_table[self.persist_selectable], 

1630 ignore_nonexistent_tables=True, 

1631 ) 

1632 

1633 if len(primary_key) == 0: 

1634 raise sa_exc.ArgumentError( 

1635 "Mapper %s could not assemble any primary " 

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

1637 % (self, self.persist_selectable.description) 

1638 ) 

1639 

1640 self.primary_key = tuple(primary_key) 

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

1642 

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

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

1645 self._readonly_props = { 

1646 self._columntoproperty[col] 

1647 for col in self._columntoproperty 

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

1649 and ( 

1650 not hasattr(col, "table") 

1651 or col.table not in self._cols_by_table 

1652 ) 

1653 } 

1654 

1655 def _configure_properties(self) -> None: 

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

1657 

1658 # object attribute names mapped to MapperProperty objects 

1659 self._props = util.OrderedDict() 

1660 

1661 # table columns mapped to MapperProperty 

1662 self._columntoproperty = _ColumnMapping(self) 

1663 

1664 explicit_col_props_by_column: Dict[ 

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

1666 ] = {} 

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

1668 

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

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

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

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

1673 if self._init_properties: 

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

1675 if not isinstance(prop_arg, MapperProperty): 

1676 possible_col_prop = self._make_prop_from_column( 

1677 key, prop_arg 

1678 ) 

1679 else: 

1680 possible_col_prop = prop_arg 

1681 

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

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

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

1685 # the Table. 

1686 

1687 _map_as_property_now = True 

1688 if isinstance(possible_col_prop, properties.ColumnProperty): 

1689 for given_col in possible_col_prop.columns: 

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

1691 _map_as_property_now = False 

1692 explicit_col_props_by_key[key] = possible_col_prop 

1693 explicit_col_props_by_column[given_col] = ( 

1694 key, 

1695 possible_col_prop, 

1696 ) 

1697 

1698 if _map_as_property_now: 

1699 self._configure_property( 

1700 key, 

1701 possible_col_prop, 

1702 init=False, 

1703 ) 

1704 

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

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

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

1708 if self.inherits: 

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

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

1711 continue 

1712 

1713 incoming_prop = explicit_col_props_by_key.get(key) 

1714 if incoming_prop: 

1715 new_prop = self._reconcile_prop_with_incoming_columns( 

1716 key, 

1717 inherited_prop, 

1718 warn_only=False, 

1719 incoming_prop=incoming_prop, 

1720 ) 

1721 explicit_col_props_by_key[key] = new_prop 

1722 

1723 for inc_col in incoming_prop.columns: 

1724 explicit_col_props_by_column[inc_col] = ( 

1725 key, 

1726 new_prop, 

1727 ) 

1728 elif key not in self._props: 

1729 self._adapt_inherited_property(key, inherited_prop, False) 

1730 

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

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

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

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

1735 # reconciliation against inherited columns occurs here also. 

1736 

1737 for column in self.persist_selectable.columns: 

1738 if column in explicit_col_props_by_column: 

1739 # column was explicitly passed to properties; configure 

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

1741 # Table / selectable 

1742 key, prop = explicit_col_props_by_column[column] 

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

1744 continue 

1745 

1746 elif column in self._columntoproperty: 

1747 continue 

1748 

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

1750 if self._should_exclude( 

1751 column.key, 

1752 column_key, 

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

1754 column=column, 

1755 ): 

1756 continue 

1757 

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

1759 # of the inheriting mapper 

1760 for mapper in self.iterate_to_root(): 

1761 if column in mapper._columntoproperty: 

1762 column_key = mapper._columntoproperty[column].key 

1763 

1764 self._configure_property( 

1765 column_key, 

1766 column, 

1767 init=False, 

1768 setparent=True, 

1769 ) 

1770 

1771 def _configure_polymorphic_setter(self, init=False): 

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

1773 'polymorphic_on' column, if applicable, and not 

1774 already generated by _configure_properties (which is typical). 

1775 

1776 Also create a setter function which will assign this 

1777 attribute to the value of the 'polymorphic_identity' 

1778 upon instance construction, also if applicable. This 

1779 routine will run when an instance is created. 

1780 

1781 """ 

1782 setter = False 

1783 polymorphic_key: Optional[str] = None 

1784 

1785 if self.polymorphic_on is not None: 

1786 setter = True 

1787 

1788 if isinstance(self.polymorphic_on, str): 

1789 # polymorphic_on specified as a string - link 

1790 # it to mapped ColumnProperty 

1791 try: 

1792 self.polymorphic_on = self._props[self.polymorphic_on] 

1793 except KeyError as err: 

1794 raise sa_exc.ArgumentError( 

1795 "Can't determine polymorphic_on " 

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

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

1798 ) from err 

1799 

1800 if self.polymorphic_on in self._columntoproperty: 

1801 # polymorphic_on is a column that is already mapped 

1802 # to a ColumnProperty 

1803 prop = self._columntoproperty[self.polymorphic_on] 

1804 elif isinstance(self.polymorphic_on, MapperProperty): 

1805 # polymorphic_on is directly a MapperProperty, 

1806 # ensure it's a ColumnProperty 

1807 if not isinstance( 

1808 self.polymorphic_on, properties.ColumnProperty 

1809 ): 

1810 raise sa_exc.ArgumentError( 

1811 "Only direct column-mapped " 

1812 "property or SQL expression " 

1813 "can be passed for polymorphic_on" 

1814 ) 

1815 prop = self.polymorphic_on 

1816 else: 

1817 # polymorphic_on is a Column or SQL expression and 

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

1819 # only present in the with_polymorphic selectable or 

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

1821 # hope is compatible with this mapper's persist_selectable 

1822 col = self.persist_selectable.corresponding_column( 

1823 self.polymorphic_on 

1824 ) 

1825 if col is None: 

1826 # polymorphic_on doesn't derive from any 

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

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

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

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

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

1832 # represented somehow in either persist_selectable or 

1833 # with_polymorphic. Otherwise as of 0.7.4 we 

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

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

1836 setter = False 

1837 instrument = False 

1838 col = self.polymorphic_on 

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

1840 self.with_polymorphic is None 

1841 or self.with_polymorphic[1] is None 

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

1843 is None 

1844 ): 

1845 raise sa_exc.InvalidRequestError( 

1846 "Could not map polymorphic_on column " 

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

1848 "loads will not function properly" 

1849 % col.description 

1850 ) 

1851 else: 

1852 # column/expression that polymorphic_on derives from 

1853 # is present in our mapped table 

1854 # and is probably mapped, but polymorphic_on itself 

1855 # is not. This happens when 

1856 # the polymorphic_on is only directly present in the 

1857 # with_polymorphic selectable, as when use 

1858 # polymorphic_union. 

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

1860 instrument = True 

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

1862 if key: 

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

1864 raise sa_exc.InvalidRequestError( 

1865 "Cannot exclude or override the " 

1866 "discriminator column %r" % key 

1867 ) 

1868 else: 

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

1870 key = col.key 

1871 

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

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

1874 

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

1876 # column in the property 

1877 self.polymorphic_on = prop.columns[0] 

1878 polymorphic_key = prop.key 

1879 else: 

1880 # no polymorphic_on was set. 

1881 # check inheriting mappers for one. 

1882 for mapper in self.iterate_to_root(): 

1883 # determine if polymorphic_on of the parent 

1884 # should be propagated here. If the col 

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

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

1887 # inheritance), we can use it 

1888 if mapper.polymorphic_on is not None: 

1889 if self.persist_selectable is mapper.persist_selectable: 

1890 self.polymorphic_on = mapper.polymorphic_on 

1891 else: 

1892 self.polymorphic_on = ( 

1893 self.persist_selectable 

1894 ).corresponding_column(mapper.polymorphic_on) 

1895 # we can use the parent mapper's _set_polymorphic_identity 

1896 # directly; it ensures the polymorphic_identity of the 

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

1898 if self.polymorphic_on is not None: 

1899 self._set_polymorphic_identity = ( 

1900 mapper._set_polymorphic_identity 

1901 ) 

1902 self._polymorphic_attr_key = ( 

1903 mapper._polymorphic_attr_key 

1904 ) 

1905 self._validate_polymorphic_identity = ( 

1906 mapper._validate_polymorphic_identity 

1907 ) 

1908 else: 

1909 self._set_polymorphic_identity = None 

1910 self._polymorphic_attr_key = None 

1911 return 

1912 

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

1914 raise sa_exc.InvalidRequestError( 

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

1916 "on a mapper hierarchy which includes the " 

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

1918 ) 

1919 

1920 if setter: 

1921 

1922 def _set_polymorphic_identity(state): 

1923 dict_ = state.dict 

1924 # TODO: what happens if polymorphic_on column attribute name 

1925 # does not match .key? 

1926 

1927 polymorphic_identity = ( 

1928 state.manager.mapper.polymorphic_identity 

1929 ) 

1930 if ( 

1931 polymorphic_identity is None 

1932 and state.manager.mapper.polymorphic_abstract 

1933 ): 

1934 raise sa_exc.InvalidRequestError( 

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

1936 "mapper is marked polymorphic_abstract=True" 

1937 ) 

1938 

1939 state.get_impl(polymorphic_key).set( 

1940 state, 

1941 dict_, 

1942 polymorphic_identity, 

1943 None, 

1944 ) 

1945 

1946 self._polymorphic_attr_key = polymorphic_key 

1947 

1948 def _validate_polymorphic_identity(mapper, state, dict_): 

1949 if ( 

1950 polymorphic_key in dict_ 

1951 and dict_[polymorphic_key] 

1952 not in mapper._acceptable_polymorphic_identities 

1953 ): 

1954 util.warn_limited( 

1955 "Flushing object %s with " 

1956 "incompatible polymorphic identity %r; the " 

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

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

1959 ) 

1960 

1961 self._set_polymorphic_identity = _set_polymorphic_identity 

1962 self._validate_polymorphic_identity = ( 

1963 _validate_polymorphic_identity 

1964 ) 

1965 else: 

1966 self._polymorphic_attr_key = None 

1967 self._set_polymorphic_identity = None 

1968 

1969 _validate_polymorphic_identity = None 

1970 

1971 @HasMemoized.memoized_attribute 

1972 def _version_id_prop(self): 

1973 if self.version_id_col is not None: 

1974 return self._columntoproperty[self.version_id_col] 

1975 else: 

1976 return None 

1977 

1978 @HasMemoized.memoized_attribute 

1979 def _acceptable_polymorphic_identities(self): 

1980 identities = set() 

1981 

1982 stack = deque([self]) 

1983 while stack: 

1984 item = stack.popleft() 

1985 if item.persist_selectable is self.persist_selectable: 

1986 identities.add(item.polymorphic_identity) 

1987 stack.extend(item._inheriting_mappers) 

1988 

1989 return identities 

1990 

1991 @HasMemoized.memoized_attribute 

1992 def _prop_set(self): 

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

1994 

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

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

1997 descriptor_props = util.preloaded.orm_descriptor_props 

1998 

1999 if not self.concrete: 

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

2001 elif key not in self._props: 

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

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

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

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

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

2007 # descriptors that might have side effects when invoked. 

2008 implementing_attribute = self.class_manager._get_class_attr_mro( 

2009 key, prop 

2010 ) 

2011 if implementing_attribute is prop or ( 

2012 isinstance( 

2013 implementing_attribute, attributes.InstrumentedAttribute 

2014 ) 

2015 and implementing_attribute._parententity is prop.parent 

2016 ): 

2017 self._configure_property( 

2018 key, 

2019 descriptor_props.ConcreteInheritedProperty(), 

2020 init=init, 

2021 setparent=True, 

2022 ) 

2023 

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

2025 def _configure_property( 

2026 self, 

2027 key: str, 

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

2029 *, 

2030 init: bool = True, 

2031 setparent: bool = True, 

2032 warn_for_existing: bool = False, 

2033 ) -> MapperProperty[Any]: 

2034 descriptor_props = util.preloaded.orm_descriptor_props 

2035 self._log( 

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

2037 ) 

2038 

2039 if not isinstance(prop_arg, MapperProperty): 

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

2041 key, prop_arg 

2042 ) 

2043 else: 

2044 prop = prop_arg 

2045 

2046 if isinstance(prop, properties.ColumnProperty): 

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

2048 

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

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

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

2052 if col is None and self.inherits: 

2053 path = [self] 

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

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

2056 if col is not None: 

2057 for m2 in path: 

2058 m2.persist_selectable._refresh_for_new_column(col) 

2059 col = self.persist_selectable.corresponding_column( 

2060 prop.columns[0] 

2061 ) 

2062 break 

2063 path.append(m) 

2064 

2065 # subquery expression, column not present in the mapped 

2066 # selectable. 

2067 if col is None: 

2068 col = prop.columns[0] 

2069 

2070 # column is coming in after _readonly_props was 

2071 # initialized; check for 'readonly' 

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

2073 not hasattr(col, "table") 

2074 or col.table not in self._cols_by_table 

2075 ): 

2076 self._readonly_props.add(prop) 

2077 

2078 else: 

2079 # if column is coming in after _cols_by_table was 

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

2081 if ( 

2082 hasattr(self, "_cols_by_table") 

2083 and col.table in self._cols_by_table 

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

2085 ): 

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

2087 

2088 # if this properties.ColumnProperty represents the "polymorphic 

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

2090 # columns in SELECT statements. 

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

2092 prop._is_polymorphic_discriminator = ( 

2093 col is self.polymorphic_on 

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

2095 ) 

2096 

2097 if isinstance(col, expression.Label): 

2098 # new in 1.4, get column property against expressions 

2099 # to be addressable in subqueries 

2100 col.key = col._tq_key_label = key 

2101 

2102 self.columns.add(col, key) 

2103 

2104 for col in prop.columns: 

2105 for proxy_col in col.proxy_set: 

2106 self._columntoproperty[proxy_col] = prop 

2107 

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

2109 util.warn( 

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

2111 "assigned to attribute " 

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

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

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

2115 ) 

2116 

2117 prop.key = key 

2118 

2119 if setparent: 

2120 prop.set_parent(self, init) 

2121 

2122 if key in self._props and getattr( 

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

2124 ): 

2125 syn = self._props[key]._mapped_by_synonym 

2126 raise sa_exc.ArgumentError( 

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

2128 "a ColumnProperty already exists keyed to the name " 

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

2130 ) 

2131 

2132 # replacement cases 

2133 

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

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

2136 if ( 

2137 key in self._props 

2138 and not isinstance( 

2139 self._props[key], descriptor_props.ConcreteInheritedProperty 

2140 ) 

2141 and not isinstance(prop, descriptor_props.SynonymProperty) 

2142 ): 

2143 if warn_for_existing: 

2144 util.warn_deprecated( 

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

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

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

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

2149 "release", 

2150 "2.0", 

2151 ) 

2152 oldprop = self._props[key] 

2153 self._path_registry.pop(oldprop, None) 

2154 

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

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

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

2158 # get replaced. 

2159 elif ( 

2160 warn_for_existing 

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

2162 and not isinstance(prop, descriptor_props.SynonymProperty) 

2163 and not isinstance( 

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

2165 descriptor_props.ConcreteInheritedProperty, 

2166 ) 

2167 ): 

2168 util.warn_deprecated( 

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

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

2171 "attribute of the same name. " 

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

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

2174 "release", 

2175 "2.0", 

2176 ) 

2177 

2178 self._props[key] = prop 

2179 

2180 prop.instrument_class(self) 

2181 

2182 for mapper in self._inheriting_mappers: 

2183 mapper._adapt_inherited_property(key, prop, init) 

2184 

2185 if init: 

2186 prop.init() 

2187 prop.post_instrument_class(self) 

2188 

2189 if self.configured: 

2190 self._expire_memoizations() 

2191 

2192 return prop 

2193 

2194 def _make_prop_from_column( 

2195 self, 

2196 key: str, 

2197 column: Union[ 

2198 Sequence[KeyedColumnElement[Any]], KeyedColumnElement[Any] 

2199 ], 

2200 ) -> ColumnProperty[Any]: 

2201 columns = util.to_list(column) 

2202 mapped_column = [] 

2203 for c in columns: 

2204 mc = self.persist_selectable.corresponding_column(c) 

2205 if mc is None: 

2206 mc = self.local_table.corresponding_column(c) 

2207 if mc is not None: 

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

2209 # mapped table, this corresponds to adding a 

2210 # column after the fact to the local table. 

2211 # [ticket:1523] 

2212 self.persist_selectable._refresh_for_new_column(mc) 

2213 mc = self.persist_selectable.corresponding_column(c) 

2214 if mc is None: 

2215 raise sa_exc.ArgumentError( 

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

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

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

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

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

2221 ) 

2222 mapped_column.append(mc) 

2223 return properties.ColumnProperty(*mapped_column) 

2224 

2225 def _reconcile_prop_with_incoming_columns( 

2226 self, 

2227 key: str, 

2228 existing_prop: MapperProperty[Any], 

2229 warn_only: bool, 

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

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

2232 ) -> ColumnProperty[Any]: 

2233 if incoming_prop and ( 

2234 self.concrete 

2235 or not isinstance(existing_prop, properties.ColumnProperty) 

2236 ): 

2237 return incoming_prop 

2238 

2239 existing_column = existing_prop.columns[0] 

2240 

2241 if incoming_prop and existing_column in incoming_prop.columns: 

2242 return incoming_prop 

2243 

2244 if incoming_prop is None: 

2245 assert single_column is not None 

2246 incoming_column = single_column 

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

2248 else: 

2249 assert single_column is None 

2250 incoming_column = incoming_prop.columns[0] 

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

2252 

2253 if ( 

2254 ( 

2255 not self._inherits_equated_pairs 

2256 or (equated_pair_key not in self._inherits_equated_pairs) 

2257 ) 

2258 and not existing_column.shares_lineage(incoming_column) 

2259 and existing_column is not self.version_id_col 

2260 and incoming_column is not self.version_id_col 

2261 ): 

2262 msg = ( 

2263 "Implicitly combining column %s with column " 

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

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

2266 "explicitly." 

2267 % ( 

2268 existing_prop.columns[-1], 

2269 incoming_column, 

2270 key, 

2271 ) 

2272 ) 

2273 if warn_only: 

2274 util.warn(msg) 

2275 else: 

2276 raise sa_exc.InvalidRequestError(msg) 

2277 

2278 # existing properties.ColumnProperty from an inheriting 

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

2280 # breakpoint() 

2281 new_prop = existing_prop.copy() 

2282 

2283 new_prop.columns.insert(0, incoming_column) 

2284 self._log( 

2285 "inserting column to existing list " 

2286 "in properties.ColumnProperty %s", 

2287 key, 

2288 ) 

2289 return new_prop # type: ignore 

2290 

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

2292 def _property_from_column( 

2293 self, 

2294 key: str, 

2295 column: KeyedColumnElement[Any], 

2296 ) -> ColumnProperty[Any]: 

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

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

2299 

2300 descriptor_props = util.preloaded.orm_descriptor_props 

2301 

2302 prop = self._props.get(key) 

2303 

2304 if isinstance(prop, properties.ColumnProperty): 

2305 return self._reconcile_prop_with_incoming_columns( 

2306 key, 

2307 prop, 

2308 single_column=column, 

2309 warn_only=prop.parent is not self, 

2310 ) 

2311 elif prop is None or isinstance( 

2312 prop, descriptor_props.ConcreteInheritedProperty 

2313 ): 

2314 return self._make_prop_from_column(key, column) 

2315 else: 

2316 raise sa_exc.ArgumentError( 

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

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

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

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

2321 "to remove all awareness of the column entirely " 

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

2323 "use the 'include_properties' or 'exclude_properties' " 

2324 "mapper arguments to control specifically which table " 

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

2326 ) 

2327 

2328 @util.langhelpers.tag_method_for_warnings( 

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

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

2331 "operation.", 

2332 sa_exc.SAWarning, 

2333 ) 

2334 def _check_configure(self) -> None: 

2335 if self.registry._new_mappers: 

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

2337 

2338 def _post_configure_properties(self) -> None: 

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

2340 attached to this mapper. 

2341 

2342 This is a deferred configuration step which is intended 

2343 to execute once all mappers have been constructed. 

2344 

2345 """ 

2346 

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

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

2349 for key, prop in l: 

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

2351 

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

2353 prop.init() 

2354 

2355 if prop._configure_finished: 

2356 prop.post_instrument_class(self) 

2357 

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

2359 self.configured = True 

2360 

2361 def add_properties(self, dict_of_properties): 

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

2363 using `add_property`. 

2364 

2365 """ 

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

2367 self.add_property(key, value) 

2368 

2369 def add_property( 

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

2371 ) -> None: 

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

2373 

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

2375 property to the initial properties dictionary sent to the 

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

2377 the given MapperProperty is configured immediately. 

2378 

2379 """ 

2380 prop = self._configure_property( 

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

2382 ) 

2383 assert isinstance(prop, MapperProperty) 

2384 self._init_properties[key] = prop 

2385 

2386 def _expire_memoizations(self) -> None: 

2387 for mapper in self.iterate_to_root(): 

2388 mapper._reset_memoizations() 

2389 

2390 @property 

2391 def _log_desc(self) -> str: 

2392 return ( 

2393 "(" 

2394 + self.class_.__name__ 

2395 + "|" 

2396 + ( 

2397 self.local_table is not None 

2398 and self.local_table.description 

2399 or str(self.local_table) 

2400 ) 

2401 + ")" 

2402 ) 

2403 

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

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

2406 

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

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

2409 

2410 def __repr__(self) -> str: 

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

2412 

2413 def __str__(self) -> str: 

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

2415 self.class_.__name__, 

2416 ( 

2417 self.local_table.description 

2418 if self.local_table is not None 

2419 else self.persist_selectable.description 

2420 ), 

2421 ) 

2422 

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

2424 orphan_possible = False 

2425 for mapper in self.iterate_to_root(): 

2426 for key, cls in mapper._delete_orphans: 

2427 orphan_possible = True 

2428 

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

2430 state, key, optimistic=state.has_identity 

2431 ) 

2432 

2433 if self.legacy_is_orphan and has_parent: 

2434 return False 

2435 elif not self.legacy_is_orphan and not has_parent: 

2436 return True 

2437 

2438 if self.legacy_is_orphan: 

2439 return orphan_possible 

2440 else: 

2441 return False 

2442 

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

2444 return key in self._props 

2445 

2446 def get_property( 

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

2448 ) -> MapperProperty[Any]: 

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

2450 

2451 if _configure_mappers: 

2452 self._check_configure() 

2453 

2454 try: 

2455 return self._props[key] 

2456 except KeyError as err: 

2457 raise sa_exc.InvalidRequestError( 

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

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

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

2461 ) from err 

2462 

2463 def get_property_by_column( 

2464 self, column: ColumnElement[_T] 

2465 ) -> MapperProperty[_T]: 

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

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

2468 

2469 return self._columntoproperty[column] 

2470 

2471 @property 

2472 def iterate_properties(self): 

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

2474 

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

2476 

2477 def _mappers_from_spec( 

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

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

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

2481 represents. 

2482 

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

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

2485 

2486 """ 

2487 if spec == "*": 

2488 mappers = list(self.self_and_descendants) 

2489 elif spec: 

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

2491 for m in util.to_list(spec): 

2492 m = _class_to_mapper(m) 

2493 if not m.isa(self): 

2494 raise sa_exc.InvalidRequestError( 

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

2496 ) 

2497 

2498 if selectable is None: 

2499 mapper_set.update(m.iterate_to_root()) 

2500 else: 

2501 mapper_set.add(m) 

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

2503 else: 

2504 mappers = [] 

2505 

2506 if selectable is not None: 

2507 tables = set( 

2508 sql_util.find_tables(selectable, include_aliases=True) 

2509 ) 

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

2511 return mappers 

2512 

2513 def _selectable_from_mappers( 

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

2515 ) -> FromClause: 

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

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

2518 mapped tables. 

2519 

2520 """ 

2521 from_obj = self.persist_selectable 

2522 for m in mappers: 

2523 if m is self: 

2524 continue 

2525 if m.concrete: 

2526 raise sa_exc.InvalidRequestError( 

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

2528 "when concrete-inheriting mappers are used." 

2529 ) 

2530 elif not m.single: 

2531 if innerjoin: 

2532 from_obj = from_obj.join( 

2533 m.local_table, m.inherit_condition 

2534 ) 

2535 else: 

2536 from_obj = from_obj.outerjoin( 

2537 m.local_table, m.inherit_condition 

2538 ) 

2539 

2540 return from_obj 

2541 

2542 @HasMemoized.memoized_attribute 

2543 def _version_id_has_server_side_value(self) -> bool: 

2544 vid_col = self.version_id_col 

2545 

2546 if vid_col is None: 

2547 return False 

2548 

2549 elif not isinstance(vid_col, Column): 

2550 return True 

2551 else: 

2552 return vid_col.server_default is not None or ( 

2553 vid_col.default is not None 

2554 and ( 

2555 not vid_col.default.is_scalar 

2556 and not vid_col.default.is_callable 

2557 ) 

2558 ) 

2559 

2560 @HasMemoized.memoized_attribute 

2561 def _single_table_criteria_component(self): 

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

2563 

2564 hierarchy = tuple( 

2565 m.polymorphic_identity 

2566 for m in self.self_and_descendants 

2567 if not m.polymorphic_abstract 

2568 ) 

2569 

2570 return ( 

2571 self.polymorphic_on._annotate( 

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

2573 ), 

2574 hierarchy, 

2575 ) 

2576 else: 

2577 return None 

2578 

2579 @HasMemoized.memoized_attribute 

2580 def _single_table_criterion(self): 

2581 component = self._single_table_criteria_component 

2582 if component is not None: 

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

2584 else: 

2585 return None 

2586 

2587 @HasMemoized.memoized_attribute 

2588 def _has_aliased_polymorphic_fromclause(self): 

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

2590 like a subquery. 

2591 

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

2593 if this is present. 

2594 

2595 """ 

2596 return self.with_polymorphic and isinstance( 

2597 self.with_polymorphic[1], 

2598 expression.AliasedReturnsRows, 

2599 ) 

2600 

2601 @HasMemoized.memoized_attribute 

2602 def _should_select_with_poly_adapter(self): 

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

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

2605 rows for mapped classes and subclasses against this Mapper. 

2606 

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

2608 for this condition. 

2609 

2610 """ 

2611 

2612 # this has been simplified as of #8456. 

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

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

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

2616 # 

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

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

2619 # flattened JOIN for with_polymorphic.) 

2620 # 

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

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

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

2624 # some kind or polymorphic_union. 

2625 # 

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

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

2628 # on it (such as test_join_from_polymorphic_explicit_aliased_three). 

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

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

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

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

2633 # legacy case we should probably disable. 

2634 # 

2635 # 

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

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

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

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

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

2641 # polymorphic base class. 

2642 # 

2643 return ( 

2644 self._has_aliased_polymorphic_fromclause 

2645 or self._requires_row_aliasing 

2646 or (self.base_mapper._has_aliased_polymorphic_fromclause) 

2647 or self.base_mapper._requires_row_aliasing 

2648 ) 

2649 

2650 @HasMemoized.memoized_attribute 

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

2652 self._check_configure() 

2653 

2654 if not self.with_polymorphic: 

2655 return [] 

2656 return self._mappers_from_spec(*self.with_polymorphic) 

2657 

2658 @HasMemoized.memoized_attribute 

2659 def _post_inspect(self): 

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

2661 

2662 E.g. when Query calls: 

2663 

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

2665 

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

2667 

2668 """ 

2669 self._check_configure() 

2670 

2671 @HasMemoized_ro_memoized_attribute 

2672 def _with_polymorphic_selectable(self) -> FromClause: 

2673 if not self.with_polymorphic: 

2674 return self.persist_selectable 

2675 

2676 spec, selectable = self.with_polymorphic 

2677 if selectable is not None: 

2678 return selectable 

2679 else: 

2680 return self._selectable_from_mappers( 

2681 self._mappers_from_spec(spec, selectable), False 

2682 ) 

2683 

2684 with_polymorphic_mappers = _with_polymorphic_mappers 

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

2686 default "polymorphic" query. 

2687 

2688 """ 

2689 

2690 @HasMemoized_ro_memoized_attribute 

2691 def _insert_cols_evaluating_none(self): 

2692 return { 

2693 table: frozenset( 

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

2695 ) 

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

2697 } 

2698 

2699 @HasMemoized.memoized_attribute 

2700 def _insert_cols_as_none(self): 

2701 return { 

2702 table: frozenset( 

2703 col.key 

2704 for col in columns 

2705 if not col.primary_key 

2706 and not col.server_default 

2707 and not col.default 

2708 and not col.type.should_evaluate_none 

2709 ) 

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

2711 } 

2712 

2713 @HasMemoized.memoized_attribute 

2714 def _propkey_to_col(self): 

2715 return { 

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

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

2718 } 

2719 

2720 @HasMemoized.memoized_attribute 

2721 def _pk_keys_by_table(self): 

2722 return { 

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

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

2725 } 

2726 

2727 @HasMemoized.memoized_attribute 

2728 def _pk_attr_keys_by_table(self): 

2729 return { 

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

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

2732 } 

2733 

2734 @HasMemoized.memoized_attribute 

2735 def _server_default_cols( 

2736 self, 

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

2738 return { 

2739 table: frozenset( 

2740 [ 

2741 col 

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

2743 if col.server_default is not None 

2744 or ( 

2745 col.default is not None 

2746 and col.default.is_clause_element 

2747 ) 

2748 ] 

2749 ) 

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

2751 } 

2752 

2753 @HasMemoized.memoized_attribute 

2754 def _server_onupdate_default_cols( 

2755 self, 

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

2757 return { 

2758 table: frozenset( 

2759 [ 

2760 col 

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

2762 if col.server_onupdate is not None 

2763 or ( 

2764 col.onupdate is not None 

2765 and col.onupdate.is_clause_element 

2766 ) 

2767 ] 

2768 ) 

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

2770 } 

2771 

2772 @HasMemoized.memoized_attribute 

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

2774 return { 

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

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

2777 } 

2778 

2779 @HasMemoized.memoized_attribute 

2780 def _server_onupdate_default_col_keys( 

2781 self, 

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

2783 return { 

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

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

2786 } 

2787 

2788 @HasMemoized.memoized_attribute 

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

2790 result: Set[str] = set() 

2791 

2792 col_to_property = self._columntoproperty 

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

2794 result.update( 

2795 col_to_property[col].key 

2796 for col in columns.intersection(col_to_property) 

2797 ) 

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

2799 result.update( 

2800 col_to_property[col].key 

2801 for col in columns.intersection(col_to_property) 

2802 ) 

2803 return result 

2804 

2805 @HasMemoized.memoized_instancemethod 

2806 def __clause_element__(self): 

2807 annotations: Dict[str, Any] = { 

2808 "entity_namespace": self, 

2809 "parententity": self, 

2810 "parentmapper": self, 

2811 } 

2812 if self.persist_selectable is not self.local_table: 

2813 # joined table inheritance, with polymorphic selectable, 

2814 # etc. 

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

2816 { 

2817 "entity_namespace": self, 

2818 "parententity": self, 

2819 "parentmapper": self, 

2820 } 

2821 )._set_propagate_attrs( 

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

2823 ) 

2824 

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

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

2827 ) 

2828 

2829 @util.memoized_property 

2830 def select_identity_token(self): 

2831 return ( 

2832 expression.null() 

2833 ._annotate( 

2834 { 

2835 "entity_namespace": self, 

2836 "parententity": self, 

2837 "parentmapper": self, 

2838 "identity_token": True, 

2839 } 

2840 ) 

2841 ._set_propagate_attrs( 

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

2843 ) 

2844 ) 

2845 

2846 @property 

2847 def selectable(self) -> FromClause: 

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

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

2850 

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

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

2853 full "polymorphic" selectable is returned. 

2854 

2855 """ 

2856 return self._with_polymorphic_selectable 

2857 

2858 def _with_polymorphic_args( 

2859 self, 

2860 spec: Any = None, 

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

2862 innerjoin: bool = False, 

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

2864 if selectable not in (None, False): 

2865 selectable = coercions.expect( 

2866 roles.FromClauseRole, 

2867 selectable, 

2868 ) 

2869 

2870 if self.with_polymorphic: 

2871 if not spec: 

2872 spec = self.with_polymorphic[0] 

2873 if selectable is False: 

2874 selectable = self.with_polymorphic[1] 

2875 elif selectable is False: 

2876 selectable = None 

2877 mappers = self._mappers_from_spec(spec, selectable) 

2878 if selectable is not None: 

2879 return mappers, selectable 

2880 else: 

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

2882 

2883 @HasMemoized.memoized_attribute 

2884 def _polymorphic_properties(self): 

2885 return list( 

2886 self._iterate_polymorphic_properties( 

2887 self._with_polymorphic_mappers 

2888 ) 

2889 ) 

2890 

2891 @property 

2892 def _all_column_expressions(self): 

2893 poly_properties = self._polymorphic_properties 

2894 adapter = self._polymorphic_adapter 

2895 

2896 return [ 

2897 adapter.columns[c] if adapter else c 

2898 for prop in poly_properties 

2899 if isinstance(prop, properties.ColumnProperty) 

2900 and prop._renders_in_subqueries 

2901 for c in prop.columns 

2902 ] 

2903 

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

2905 if polymorphic_mappers: 

2906 poly_properties = self._iterate_polymorphic_properties( 

2907 polymorphic_mappers 

2908 ) 

2909 else: 

2910 poly_properties = self._polymorphic_properties 

2911 

2912 return [ 

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

2914 for prop in poly_properties 

2915 if isinstance(prop, properties.ColumnProperty) 

2916 ] 

2917 

2918 @HasMemoized.memoized_attribute 

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

2920 if self._has_aliased_polymorphic_fromclause: 

2921 return orm_util.ORMAdapter( 

2922 orm_util._TraceAdaptRole.MAPPER_POLYMORPHIC_ADAPTER, 

2923 self, 

2924 selectable=self.selectable, 

2925 equivalents=self._equivalent_columns, 

2926 limit_on_entity=False, 

2927 ) 

2928 else: 

2929 return None 

2930 

2931 def _iterate_polymorphic_properties(self, mappers=None): 

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

2933 a SELECT.""" 

2934 if mappers is None: 

2935 mappers = self._with_polymorphic_mappers 

2936 

2937 if not mappers: 

2938 for c in self.iterate_properties: 

2939 yield c 

2940 else: 

2941 # in the polymorphic case, filter out discriminator columns 

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

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

2944 for c in util.unique_list( 

2945 chain( 

2946 *[ 

2947 list(mapper.iterate_properties) 

2948 for mapper in [self] + mappers 

2949 ] 

2950 ) 

2951 ): 

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

2953 self.polymorphic_on is None 

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

2955 ): 

2956 continue 

2957 yield c 

2958 

2959 @HasMemoized.memoized_attribute 

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

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

2962 associated this mapper. 

2963 

2964 This is an object that provides each property based on 

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

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

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

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

2969 column. The namespace object can also be iterated, 

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

2971 

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

2973 of this attribute which limit the types of properties 

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

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

2976 

2977 .. warning:: 

2978 

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

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

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

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

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

2984 accessing attributes dynamically, favor using the dict-access 

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

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

2987 

2988 .. seealso:: 

2989 

2990 :attr:`_orm.Mapper.all_orm_descriptors` 

2991 

2992 """ 

2993 

2994 self._check_configure() 

2995 return util.ReadOnlyProperties(self._props) 

2996 

2997 @HasMemoized.memoized_attribute 

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

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

3000 with the mapped class. 

3001 

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

3003 associated with the mapped class or its superclasses. 

3004 

3005 This namespace includes attributes that are mapped to the class 

3006 as well as attributes declared by extension modules. 

3007 It includes any Python descriptor type that inherits from 

3008 :class:`.InspectionAttr`. This includes 

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

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

3011 :class:`.AssociationProxy`. 

3012 

3013 To distinguish between mapped attributes and extension attributes, 

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

3015 to a constant that distinguishes between different extension types. 

3016 

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

3018 

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

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

3021 

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

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

3024 3 below. The order will be the 

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

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

3027 or the mapper. 

3028 

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

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

3031 class in which it first appeared. 

3032 

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

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

3035 

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

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

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

3039 referring to the collection of mapped properties via 

3040 :attr:`_orm.Mapper.attrs`. 

3041 

3042 .. warning:: 

3043 

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

3045 accessor namespace is an 

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

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

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

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

3050 accessing attributes dynamically, favor using the dict-access 

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

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

3053 collisions. 

3054 

3055 .. seealso:: 

3056 

3057 :attr:`_orm.Mapper.attrs` 

3058 

3059 """ 

3060 return util.ReadOnlyProperties( 

3061 dict(self.class_manager._all_sqla_attributes()) 

3062 ) 

3063 

3064 @HasMemoized.memoized_attribute 

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

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

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

3068 all synonyms that refer to primary key columns 

3069 

3070 """ 

3071 descriptor_props = util.preloaded.orm_descriptor_props 

3072 

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

3074 

3075 return { 

3076 syn.key: syn.name 

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

3078 if isinstance(syn, descriptor_props.SynonymProperty) 

3079 and syn.name in pk_keys 

3080 } 

3081 

3082 @HasMemoized.memoized_attribute 

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

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

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

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

3087 

3088 .. seealso:: 

3089 

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

3091 :class:`.MapperProperty` 

3092 objects. 

3093 

3094 """ 

3095 descriptor_props = util.preloaded.orm_descriptor_props 

3096 

3097 return self._filter_properties(descriptor_props.SynonymProperty) 

3098 

3099 @property 

3100 def entity_namespace(self): 

3101 return self.class_ 

3102 

3103 @HasMemoized.memoized_attribute 

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

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

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

3107 

3108 .. seealso:: 

3109 

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

3111 :class:`.MapperProperty` 

3112 objects. 

3113 

3114 """ 

3115 return self._filter_properties(properties.ColumnProperty) 

3116 

3117 @HasMemoized.memoized_attribute 

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

3119 def relationships( 

3120 self, 

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

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

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

3124 

3125 .. warning:: 

3126 

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

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

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

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

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

3132 accessing attributes dynamically, favor using the dict-access 

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

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

3135 collisions. 

3136 

3137 .. seealso:: 

3138 

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

3140 :class:`.MapperProperty` 

3141 objects. 

3142 

3143 """ 

3144 return self._filter_properties( 

3145 util.preloaded.orm_relationships.RelationshipProperty 

3146 ) 

3147 

3148 @HasMemoized.memoized_attribute 

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

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

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

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

3153 

3154 .. seealso:: 

3155 

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

3157 :class:`.MapperProperty` 

3158 objects. 

3159 

3160 """ 

3161 return self._filter_properties( 

3162 util.preloaded.orm_descriptor_props.CompositeProperty 

3163 ) 

3164 

3165 def _filter_properties( 

3166 self, type_: Type[_MP] 

3167 ) -> util.ReadOnlyProperties[_MP]: 

3168 self._check_configure() 

3169 return util.ReadOnlyProperties( 

3170 util.OrderedDict( 

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

3172 ) 

3173 ) 

3174 

3175 @HasMemoized.memoized_attribute 

3176 def _get_clause(self): 

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

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

3179 by primary key. 

3180 

3181 """ 

3182 params = [ 

3183 ( 

3184 primary_key, 

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

3186 ) 

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

3188 ] 

3189 return ( 

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

3191 util.column_dict(params), 

3192 ) 

3193 

3194 @HasMemoized.memoized_attribute 

3195 def _equivalent_columns(self) -> _EquivalentColumnMap: 

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

3197 the determination of column pairs that are equated to 

3198 one another based on inherit condition. This is designed 

3199 to work with the queries that util.polymorphic_union 

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

3201 the base table directly (including the subclass table columns 

3202 only). 

3203 

3204 The resulting structure is a dictionary of columns mapped 

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

3206 

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

3208 

3209 """ # noqa: E501 

3210 result: _EquivalentColumnMap = {} 

3211 

3212 def visit_binary(binary): 

3213 if binary.operator == operators.eq: 

3214 if binary.left in result: 

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

3216 else: 

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

3218 if binary.right in result: 

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

3220 else: 

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

3222 

3223 for mapper in self.base_mapper.self_and_descendants: 

3224 if mapper.inherit_condition is not None: 

3225 visitors.traverse( 

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

3227 ) 

3228 

3229 return result 

3230 

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

3232 if isinstance( 

3233 obj, 

3234 ( 

3235 _MappedAttribute, 

3236 instrumentation.ClassManager, 

3237 expression.ColumnElement, 

3238 ), 

3239 ): 

3240 return False 

3241 else: 

3242 return assigned_name not in self._dataclass_fields 

3243 

3244 @HasMemoized.memoized_attribute 

3245 def _dataclass_fields(self): 

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

3247 

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

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

3250 present on the class. 

3251 

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

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

3254 

3255 """ 

3256 

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

3258 return True 

3259 

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

3261 # either local or from an inherited class 

3262 # ignore dataclass field default values 

3263 if local: 

3264 if self.class_.__dict__.get( 

3265 assigned_name, None 

3266 ) is not None and self._is_userland_descriptor( 

3267 assigned_name, self.class_.__dict__[assigned_name] 

3268 ): 

3269 return True 

3270 else: 

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

3272 if attr is not None and self._is_userland_descriptor( 

3273 assigned_name, attr 

3274 ): 

3275 return True 

3276 

3277 if ( 

3278 self.include_properties is not None 

3279 and name not in self.include_properties 

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

3281 ): 

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

3283 return True 

3284 

3285 if self.exclude_properties is not None and ( 

3286 name in self.exclude_properties 

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

3288 ): 

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

3290 return True 

3291 

3292 return False 

3293 

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

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

3296 common inherited parent as this mapper.""" 

3297 

3298 return self.base_mapper is other.base_mapper 

3299 

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

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

3302 one. common parent but different branch 

3303 

3304 """ 

3305 return ( 

3306 self.base_mapper is other.base_mapper 

3307 and not self.isa(other) 

3308 and not other.isa(self) 

3309 ) 

3310 

3311 def _canload( 

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

3313 ) -> bool: 

3314 s = self.primary_mapper() 

3315 if self.polymorphic_on is not None or allow_subtypes: 

3316 return _state_mapper(state).isa(s) 

3317 else: 

3318 return _state_mapper(state) is s 

3319 

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

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

3322 

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

3324 while m and m is not other: 

3325 m = m.inherits 

3326 return bool(m) 

3327 

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

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

3330 while m: 

3331 yield m 

3332 m = m.inherits 

3333 

3334 @HasMemoized.memoized_attribute 

3335 def self_and_descendants(self) -> Sequence[Mapper[Any]]: 

3336 """The collection including this mapper and all descendant mappers. 

3337 

3338 This includes not just the immediately inheriting mappers but 

3339 all their inheriting mappers as well. 

3340 

3341 """ 

3342 descendants = [] 

3343 stack = deque([self]) 

3344 while stack: 

3345 item = stack.popleft() 

3346 descendants.append(item) 

3347 stack.extend(item._inheriting_mappers) 

3348 return util.WeakSequence(descendants) 

3349 

3350 def polymorphic_iterator(self) -> Iterator[Mapper[Any]]: 

3351 """Iterate through the collection including this mapper and 

3352 all descendant mappers. 

3353 

3354 This includes not just the immediately inheriting mappers but 

3355 all their inheriting mappers as well. 

3356 

3357 To iterate through an entire hierarchy, use 

3358 ``mapper.base_mapper.polymorphic_iterator()``. 

3359 

3360 """ 

3361 return iter(self.self_and_descendants) 

3362 

3363 def primary_mapper(self) -> Mapper[Any]: 

3364 """Return the primary mapper corresponding to this mapper's class key 

3365 (class).""" 

3366 

3367 return self.class_manager.mapper 

3368 

3369 @property 

3370 def primary_base_mapper(self) -> Mapper[Any]: 

3371 return self.class_manager.mapper.base_mapper 

3372 

3373 def _result_has_identity_key(self, result, adapter=None): 

3374 pk_cols: Sequence[ColumnElement[Any]] 

3375 if adapter is not None: 

3376 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3377 else: 

3378 pk_cols = self.primary_key 

3379 rk = result.keys() 

3380 for col in pk_cols: 

3381 if col not in rk: 

3382 return False 

3383 else: 

3384 return True 

3385 

3386 def identity_key_from_row( 

3387 self, 

3388 row: Union[Row[Unpack[TupleAny]], RowMapping], 

3389 identity_token: Optional[Any] = None, 

3390 adapter: Optional[ORMAdapter] = None, 

3391 ) -> _IdentityKeyType[_O]: 

3392 """Return an identity-map key for use in storing/retrieving an 

3393 item from the identity map. 

3394 

3395 :param row: A :class:`.Row` or :class:`.RowMapping` produced from a 

3396 result set that selected from the ORM mapped primary key columns. 

3397 

3398 .. versionchanged:: 2.0 

3399 :class:`.Row` or :class:`.RowMapping` are accepted 

3400 for the "row" argument 

3401 

3402 """ 

3403 pk_cols: Sequence[ColumnElement[Any]] 

3404 if adapter is not None: 

3405 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3406 else: 

3407 pk_cols = self.primary_key 

3408 

3409 mapping: RowMapping 

3410 if hasattr(row, "_mapping"): 

3411 mapping = row._mapping 

3412 else: 

3413 mapping = row # type: ignore[assignment] 

3414 

3415 return ( 

3416 self._identity_class, 

3417 tuple(mapping[column] for column in pk_cols), 

3418 identity_token, 

3419 ) 

3420 

3421 def identity_key_from_primary_key( 

3422 self, 

3423 primary_key: Tuple[Any, ...], 

3424 identity_token: Optional[Any] = None, 

3425 ) -> _IdentityKeyType[_O]: 

3426 """Return an identity-map key for use in storing/retrieving an 

3427 item from an identity map. 

3428 

3429 :param primary_key: A list of values indicating the identifier. 

3430 

3431 """ 

3432 return ( 

3433 self._identity_class, 

3434 tuple(primary_key), 

3435 identity_token, 

3436 ) 

3437 

3438 def identity_key_from_instance(self, instance: _O) -> _IdentityKeyType[_O]: 

3439 """Return the identity key for the given instance, based on 

3440 its primary key attributes. 

3441 

3442 If the instance's state is expired, calling this method 

3443 will result in a database check to see if the object has been deleted. 

3444 If the row no longer exists, 

3445 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3446 

3447 This value is typically also found on the instance state under the 

3448 attribute name `key`. 

3449 

3450 """ 

3451 state = attributes.instance_state(instance) 

3452 return self._identity_key_from_state(state, PassiveFlag.PASSIVE_OFF) 

3453 

3454 def _identity_key_from_state( 

3455 self, 

3456 state: InstanceState[_O], 

3457 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3458 ) -> _IdentityKeyType[_O]: 

3459 dict_ = state.dict 

3460 manager = state.manager 

3461 return ( 

3462 self._identity_class, 

3463 tuple( 

3464 [ 

3465 manager[prop.key].impl.get(state, dict_, passive) 

3466 for prop in self._identity_key_props 

3467 ] 

3468 ), 

3469 state.identity_token, 

3470 ) 

3471 

3472 def primary_key_from_instance(self, instance: _O) -> Tuple[Any, ...]: 

3473 """Return the list of primary key values for the given 

3474 instance. 

3475 

3476 If the instance's state is expired, calling this method 

3477 will result in a database check to see if the object has been deleted. 

3478 If the row no longer exists, 

3479 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3480 

3481 """ 

3482 state = attributes.instance_state(instance) 

3483 identity_key = self._identity_key_from_state( 

3484 state, PassiveFlag.PASSIVE_OFF 

3485 ) 

3486 return identity_key[1] 

3487 

3488 @HasMemoized.memoized_attribute 

3489 def _persistent_sortkey_fn(self): 

3490 key_fns = [col.type.sort_key_function for col in self.primary_key] 

3491 

3492 if set(key_fns).difference([None]): 

3493 

3494 def key(state): 

3495 return tuple( 

3496 key_fn(val) if key_fn is not None else val 

3497 for key_fn, val in zip(key_fns, state.key[1]) 

3498 ) 

3499 

3500 else: 

3501 

3502 def key(state): 

3503 return state.key[1] 

3504 

3505 return key 

3506 

3507 @HasMemoized.memoized_attribute 

3508 def _identity_key_props(self): 

3509 return [self._columntoproperty[col] for col in self.primary_key] 

3510 

3511 @HasMemoized.memoized_attribute 

3512 def _all_pk_cols(self): 

3513 collection: Set[ColumnClause[Any]] = set() 

3514 for table in self.tables: 

3515 collection.update(self._pks_by_table[table]) 

3516 return collection 

3517 

3518 @HasMemoized.memoized_attribute 

3519 def _should_undefer_in_wildcard(self): 

3520 cols: Set[ColumnElement[Any]] = set(self.primary_key) 

3521 if self.polymorphic_on is not None: 

3522 cols.add(self.polymorphic_on) 

3523 return cols 

3524 

3525 @HasMemoized.memoized_attribute 

3526 def _primary_key_propkeys(self): 

3527 return {self._columntoproperty[col].key for col in self._all_pk_cols} 

3528 

3529 def _get_state_attr_by_column( 

3530 self, 

3531 state: InstanceState[_O], 

3532 dict_: _InstanceDict, 

3533 column: ColumnElement[Any], 

3534 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3535 ) -> Any: 

3536 prop = self._columntoproperty[column] 

3537 return state.manager[prop.key].impl.get(state, dict_, passive=passive) 

3538 

3539 def _set_committed_state_attr_by_column(self, state, dict_, column, value): 

3540 prop = self._columntoproperty[column] 

3541 state.manager[prop.key].impl.set_committed_value(state, dict_, value) 

3542 

3543 def _set_state_attr_by_column(self, state, dict_, column, value): 

3544 prop = self._columntoproperty[column] 

3545 state.manager[prop.key].impl.set(state, dict_, value, None) 

3546 

3547 def _get_committed_attr_by_column(self, obj, column): 

3548 state = attributes.instance_state(obj) 

3549 dict_ = attributes.instance_dict(obj) 

3550 return self._get_committed_state_attr_by_column( 

3551 state, dict_, column, passive=PassiveFlag.PASSIVE_OFF 

3552 ) 

3553 

3554 def _get_committed_state_attr_by_column( 

3555 self, state, dict_, column, passive=PassiveFlag.PASSIVE_RETURN_NO_VALUE 

3556 ): 

3557 prop = self._columntoproperty[column] 

3558 return state.manager[prop.key].impl.get_committed_value( 

3559 state, dict_, passive=passive 

3560 ) 

3561 

3562 def _optimized_get_statement(self, state, attribute_names): 

3563 """assemble a WHERE clause which retrieves a given state by primary 

3564 key, using a minimized set of tables. 

3565 

3566 Applies to a joined-table inheritance mapper where the 

3567 requested attribute names are only present on joined tables, 

3568 not the base table. The WHERE clause attempts to include 

3569 only those tables to minimize joins. 

3570 

3571 """ 

3572 props = self._props 

3573 

3574 col_attribute_names = set(attribute_names).intersection( 

3575 state.mapper.column_attrs.keys() 

3576 ) 

3577 tables: Set[FromClause] = set( 

3578 chain( 

3579 *[ 

3580 sql_util.find_tables(c, check_columns=True) 

3581 for key in col_attribute_names 

3582 for c in props[key].columns 

3583 ] 

3584 ) 

3585 ) 

3586 

3587 if self.base_mapper.local_table in tables: 

3588 return None 

3589 

3590 def visit_binary(binary): 

3591 leftcol = binary.left 

3592 rightcol = binary.right 

3593 if leftcol is None or rightcol is None: 

3594 return 

3595 

3596 if leftcol.table not in tables: 

3597 leftval = self._get_committed_state_attr_by_column( 

3598 state, 

3599 state.dict, 

3600 leftcol, 

3601 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3602 ) 

3603 if leftval in orm_util._none_set: 

3604 raise _OptGetColumnsNotAvailable() 

3605 binary.left = sql.bindparam( 

3606 None, leftval, type_=binary.right.type 

3607 ) 

3608 elif rightcol.table not in tables: 

3609 rightval = self._get_committed_state_attr_by_column( 

3610 state, 

3611 state.dict, 

3612 rightcol, 

3613 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3614 ) 

3615 if rightval in orm_util._none_set: 

3616 raise _OptGetColumnsNotAvailable() 

3617 binary.right = sql.bindparam( 

3618 None, rightval, type_=binary.right.type 

3619 ) 

3620 

3621 allconds: List[ColumnElement[bool]] = [] 

3622 

3623 start = False 

3624 

3625 # as of #7507, from the lowest base table on upwards, 

3626 # we include all intermediary tables. 

3627 

3628 for mapper in reversed(list(self.iterate_to_root())): 

3629 if mapper.local_table in tables: 

3630 start = True 

3631 elif not isinstance(mapper.local_table, expression.TableClause): 

3632 return None 

3633 if start and not mapper.single: 

3634 assert mapper.inherits 

3635 assert not mapper.concrete 

3636 assert mapper.inherit_condition is not None 

3637 allconds.append(mapper.inherit_condition) 

3638 tables.add(mapper.local_table) 

3639 

3640 # only the bottom table needs its criteria to be altered to fit 

3641 # the primary key ident - the rest of the tables upwards to the 

3642 # descendant-most class should all be present and joined to each 

3643 # other. 

3644 try: 

3645 _traversed = visitors.cloned_traverse( 

3646 allconds[0], {}, {"binary": visit_binary} 

3647 ) 

3648 except _OptGetColumnsNotAvailable: 

3649 return None 

3650 else: 

3651 allconds[0] = _traversed 

3652 

3653 cond = sql.and_(*allconds) 

3654 

3655 cols = [] 

3656 for key in col_attribute_names: 

3657 cols.extend(props[key].columns) 

3658 return ( 

3659 sql.select(*cols) 

3660 .where(cond) 

3661 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 

3662 ) 

3663 

3664 def _iterate_to_target_viawpoly(self, mapper): 

3665 if self.isa(mapper): 

3666 prev = self 

3667 for m in self.iterate_to_root(): 

3668 yield m 

3669 

3670 if m is not prev and prev not in m._with_polymorphic_mappers: 

3671 break 

3672 

3673 prev = m 

3674 if m is mapper: 

3675 break 

3676 

3677 @HasMemoized.memoized_attribute 

3678 def _would_selectinload_combinations_cache(self): 

3679 return {} 

3680 

3681 def _would_selectin_load_only_from_given_mapper(self, super_mapper): 

3682 """return True if this mapper would "selectin" polymorphic load based 

3683 on the given super mapper, and not from a setting from a subclass. 

3684 

3685 given:: 

3686 

3687 class A: ... 

3688 

3689 

3690 class B(A): 

3691 __mapper_args__ = {"polymorphic_load": "selectin"} 

3692 

3693 

3694 class C(B): ... 

3695 

3696 

3697 class D(B): 

3698 __mapper_args__ = {"polymorphic_load": "selectin"} 

3699 

3700 ``inspect(C)._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3701 returns True, because C does selectin loading because of B's setting. 

3702 

3703 OTOH, ``inspect(D) 

3704 ._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3705 returns False, because D does selectin loading because of its own 

3706 setting; when we are doing a selectin poly load from B, we want to 

3707 filter out D because it would already have its own selectin poly load 

3708 set up separately. 

3709 

3710 Added as part of #9373. 

3711 

3712 """ 

3713 cache = self._would_selectinload_combinations_cache 

3714 

3715 try: 

3716 return cache[super_mapper] 

3717 except KeyError: 

3718 pass 

3719 

3720 # assert that given object is a supermapper, meaning we already 

3721 # strong reference it directly or indirectly. this allows us 

3722 # to not worry that we are creating new strongrefs to unrelated 

3723 # mappers or other objects. 

3724 assert self.isa(super_mapper) 

3725 

3726 mapper = super_mapper 

3727 for m in self._iterate_to_target_viawpoly(mapper): 

3728 if m.polymorphic_load == "selectin": 

3729 retval = m is super_mapper 

3730 break 

3731 else: 

3732 retval = False 

3733 

3734 cache[super_mapper] = retval 

3735 return retval 

3736 

3737 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 

3738 if not enabled_via_opt: 

3739 # common case, takes place for all polymorphic loads 

3740 mapper = polymorphic_from 

3741 for m in self._iterate_to_target_viawpoly(mapper): 

3742 if m.polymorphic_load == "selectin": 

3743 return m 

3744 else: 

3745 # uncommon case, selectin load options were used 

3746 enabled_via_opt = set(enabled_via_opt) 

3747 enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 

3748 for entity in enabled_via_opt.union([polymorphic_from]): 

3749 mapper = entity.mapper 

3750 for m in self._iterate_to_target_viawpoly(mapper): 

3751 if ( 

3752 m.polymorphic_load == "selectin" 

3753 or m in enabled_via_opt_mappers 

3754 ): 

3755 return enabled_via_opt_mappers.get(m, m) 

3756 

3757 return None 

3758 

3759 @util.preload_module("sqlalchemy.orm.strategy_options") 

3760 def _subclass_load_via_in(self, entity, polymorphic_from): 

3761 """Assemble a that can load the columns local to 

3762 this subclass as a SELECT with IN. 

3763 

3764 """ 

3765 

3766 strategy_options = util.preloaded.orm_strategy_options 

3767 

3768 assert self.inherits 

3769 

3770 if self.polymorphic_on is not None: 

3771 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 

3772 keep_props = set([polymorphic_prop] + self._identity_key_props) 

3773 else: 

3774 keep_props = set(self._identity_key_props) 

3775 

3776 disable_opt = strategy_options.Load(entity) 

3777 enable_opt = strategy_options.Load(entity) 

3778 

3779 classes_to_include = {self} 

3780 m: Optional[Mapper[Any]] = self.inherits 

3781 while ( 

3782 m is not None 

3783 and m is not polymorphic_from 

3784 and m.polymorphic_load == "selectin" 

3785 ): 

3786 classes_to_include.add(m) 

3787 m = m.inherits 

3788 

3789 for prop in self.column_attrs + self.relationships: 

3790 # skip prop keys that are not instrumented on the mapped class. 

3791 # this is primarily the "_sa_polymorphic_on" property that gets 

3792 # created for an ad-hoc polymorphic_on SQL expression, issue #8704 

3793 if prop.key not in self.class_manager: 

3794 continue 

3795 

3796 if prop.parent in classes_to_include or prop in keep_props: 

3797 # "enable" options, to turn on the properties that we want to 

3798 # load by default (subject to options from the query) 

3799 if not isinstance(prop, StrategizedProperty): 

3800 continue 

3801 

3802 enable_opt = enable_opt._set_generic_strategy( 

3803 # convert string name to an attribute before passing 

3804 # to loader strategy. note this must be in terms 

3805 # of given entity, such as AliasedClass, etc. 

3806 (getattr(entity.entity_namespace, prop.key),), 

3807 dict(prop.strategy_key), 

3808 _reconcile_to_other=True, 

3809 ) 

3810 else: 

3811 # "disable" options, to turn off the properties from the 

3812 # superclass that we *don't* want to load, applied after 

3813 # the options from the query to override them 

3814 disable_opt = disable_opt._set_generic_strategy( 

3815 # convert string name to an attribute before passing 

3816 # to loader strategy. note this must be in terms 

3817 # of given entity, such as AliasedClass, etc. 

3818 (getattr(entity.entity_namespace, prop.key),), 

3819 {"do_nothing": True}, 

3820 _reconcile_to_other=False, 

3821 ) 

3822 

3823 primary_key = [ 

3824 sql_util._deep_annotate(pk, {"_orm_adapt": True}) 

3825 for pk in self.primary_key 

3826 ] 

3827 

3828 in_expr: ColumnElement[Any] 

3829 

3830 if len(primary_key) > 1: 

3831 in_expr = sql.tuple_(*primary_key) 

3832 else: 

3833 in_expr = primary_key[0] 

3834 

3835 if entity.is_aliased_class: 

3836 assert entity.mapper is self 

3837 

3838 q = sql.select(entity).set_label_style( 

3839 LABEL_STYLE_TABLENAME_PLUS_COL 

3840 ) 

3841 

3842 in_expr = entity._adapter.traverse(in_expr) 

3843 primary_key = [entity._adapter.traverse(k) for k in primary_key] 

3844 q = q.where( 

3845 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3846 ).order_by(*primary_key) 

3847 else: 

3848 q = sql.select(self).set_label_style( 

3849 LABEL_STYLE_TABLENAME_PLUS_COL 

3850 ) 

3851 q = q.where( 

3852 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3853 ).order_by(*primary_key) 

3854 

3855 return q, enable_opt, disable_opt 

3856 

3857 @HasMemoized.memoized_attribute 

3858 def _subclass_load_via_in_mapper(self): 

3859 # the default is loading this mapper against the basemost mapper 

3860 return self._subclass_load_via_in(self, self.base_mapper) 

3861 

3862 def cascade_iterator( 

3863 self, 

3864 type_: str, 

3865 state: InstanceState[_O], 

3866 halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None, 

3867 ) -> Iterator[ 

3868 Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict] 

3869 ]: 

3870 r"""Iterate each element and its mapper in an object graph, 

3871 for all relationships that meet the given cascade rule. 

3872 

3873 :param type\_: 

3874 The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 

3875 etc.). 

3876 

3877 .. note:: the ``"all"`` cascade is not accepted here. For a generic 

3878 object traversal function, see :ref:`faq_walk_objects`. 

3879 

3880 :param state: 

3881 The lead InstanceState. child items will be processed per 

3882 the relationships defined for this object's mapper. 

3883 

3884 :return: the method yields individual object instances. 

3885 

3886 .. seealso:: 

3887 

3888 :ref:`unitofwork_cascades` 

3889 

3890 :ref:`faq_walk_objects` - illustrates a generic function to 

3891 traverse all objects without relying on cascades. 

3892 

3893 """ 

3894 visited_states: Set[InstanceState[Any]] = set() 

3895 prp, mpp = object(), object() 

3896 

3897 assert state.mapper.isa(self) 

3898 

3899 # this is actually a recursive structure, fully typing it seems 

3900 # a little too difficult for what it's worth here 

3901 visitables: Deque[ 

3902 Tuple[ 

3903 Deque[Any], 

3904 object, 

3905 Optional[InstanceState[Any]], 

3906 Optional[_InstanceDict], 

3907 ] 

3908 ] 

3909 

3910 visitables = deque( 

3911 [(deque(state.mapper._props.values()), prp, state, state.dict)] 

3912 ) 

3913 

3914 while visitables: 

3915 iterator, item_type, parent_state, parent_dict = visitables[-1] 

3916 if not iterator: 

3917 visitables.pop() 

3918 continue 

3919 

3920 if item_type is prp: 

3921 prop = iterator.popleft() 

3922 if not prop.cascade or type_ not in prop.cascade: 

3923 continue 

3924 assert parent_state is not None 

3925 assert parent_dict is not None 

3926 queue = deque( 

3927 prop.cascade_iterator( 

3928 type_, 

3929 parent_state, 

3930 parent_dict, 

3931 visited_states, 

3932 halt_on, 

3933 ) 

3934 ) 

3935 if queue: 

3936 visitables.append((queue, mpp, None, None)) 

3937 elif item_type is mpp: 

3938 ( 

3939 instance, 

3940 instance_mapper, 

3941 corresponding_state, 

3942 corresponding_dict, 

3943 ) = iterator.popleft() 

3944 yield ( 

3945 instance, 

3946 instance_mapper, 

3947 corresponding_state, 

3948 corresponding_dict, 

3949 ) 

3950 visitables.append( 

3951 ( 

3952 deque(instance_mapper._props.values()), 

3953 prp, 

3954 corresponding_state, 

3955 corresponding_dict, 

3956 ) 

3957 ) 

3958 

3959 @HasMemoized.memoized_attribute 

3960 def _compiled_cache(self): 

3961 return util.LRUCache(self._compiled_cache_size) 

3962 

3963 @HasMemoized.memoized_attribute 

3964 def _multiple_persistence_tables(self): 

3965 return len(self.tables) > 1 

3966 

3967 @HasMemoized.memoized_attribute 

3968 def _sorted_tables(self): 

3969 table_to_mapper: Dict[TableClause, Mapper[Any]] = {} 

3970 

3971 for mapper in self.base_mapper.self_and_descendants: 

3972 for t in mapper.tables: 

3973 table_to_mapper.setdefault(t, mapper) 

3974 

3975 extra_dependencies = [] 

3976 for table, mapper in table_to_mapper.items(): 

3977 super_ = mapper.inherits 

3978 if super_: 

3979 extra_dependencies.extend( 

3980 [(super_table, table) for super_table in super_.tables] 

3981 ) 

3982 

3983 def skip(fk): 

3984 # attempt to skip dependencies that are not 

3985 # significant to the inheritance chain 

3986 # for two tables that are related by inheritance. 

3987 # while that dependency may be important, it's technically 

3988 # not what we mean to sort on here. 

3989 parent = table_to_mapper.get(fk.parent.table) 

3990 dep = table_to_mapper.get(fk.column.table) 

3991 if ( 

3992 parent is not None 

3993 and dep is not None 

3994 and dep is not parent 

3995 and dep.inherit_condition is not None 

3996 ): 

3997 cols = set(sql_util._find_columns(dep.inherit_condition)) 

3998 if parent.inherit_condition is not None: 

3999 cols = cols.union( 

4000 sql_util._find_columns(parent.inherit_condition) 

4001 ) 

4002 return fk.parent not in cols and fk.column not in cols 

4003 else: 

4004 return fk.parent not in cols 

4005 return False 

4006 

4007 sorted_ = sql_util.sort_tables( 

4008 table_to_mapper, 

4009 skip_fn=skip, 

4010 extra_dependencies=extra_dependencies, 

4011 ) 

4012 

4013 ret = util.OrderedDict() 

4014 for t in sorted_: 

4015 ret[t] = table_to_mapper[t] 

4016 return ret 

4017 

4018 def _memo(self, key: Any, callable_: Callable[[], _T]) -> _T: 

4019 if key in self._memoized_values: 

4020 return cast(_T, self._memoized_values[key]) 

4021 else: 

4022 self._memoized_values[key] = value = callable_() 

4023 return value 

4024 

4025 @util.memoized_property 

4026 def _table_to_equated(self): 

4027 """memoized map of tables to collections of columns to be 

4028 synchronized upwards to the base mapper.""" 

4029 

4030 result: util.defaultdict[ 

4031 Table, 

4032 List[ 

4033 Tuple[ 

4034 Mapper[Any], 

4035 List[Tuple[ColumnElement[Any], ColumnElement[Any]]], 

4036 ] 

4037 ], 

4038 ] = util.defaultdict(list) 

4039 

4040 def set_union(x, y): 

4041 return x.union(y) 

4042 

4043 for table in self._sorted_tables: 

4044 cols = set(table.c) 

4045 

4046 for m in self.iterate_to_root(): 

4047 if m._inherits_equated_pairs and cols.intersection( 

4048 reduce( 

4049 set_union, 

4050 [l.proxy_set for l, r in m._inherits_equated_pairs], 

4051 ) 

4052 ): 

4053 result[table].append((m, m._inherits_equated_pairs)) 

4054 

4055 return result 

4056 

4057 

4058class _OptGetColumnsNotAvailable(Exception): 

4059 pass 

4060 

4061 

4062def configure_mappers() -> None: 

4063 """Initialize the inter-mapper relationships of all mappers that 

4064 have been constructed thus far across all :class:`_orm.registry` 

4065 collections. 

4066 

4067 The configure step is used to reconcile and initialize the 

4068 :func:`_orm.relationship` linkages between mapped classes, as well as to 

4069 invoke configuration events such as the 

4070 :meth:`_orm.MapperEvents.before_configured` and 

4071 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM 

4072 extensions or user-defined extension hooks. 

4073 

4074 Mapper configuration is normally invoked automatically, the first time 

4075 mappings from a particular :class:`_orm.registry` are used, as well as 

4076 whenever mappings are used and additional not-yet-configured mappers have 

4077 been constructed. The automatic configuration process however is local only 

4078 to the :class:`_orm.registry` involving the target mapper and any related 

4079 :class:`_orm.registry` objects which it may depend on; this is 

4080 equivalent to invoking the :meth:`_orm.registry.configure` method 

4081 on a particular :class:`_orm.registry`. 

4082 

4083 By contrast, the :func:`_orm.configure_mappers` function will invoke the 

4084 configuration process on all :class:`_orm.registry` objects that 

4085 exist in memory, and may be useful for scenarios where many individual 

4086 :class:`_orm.registry` objects that are nonetheless interrelated are 

4087 in use. 

4088 

4089 .. versionchanged:: 1.4 

4090 

4091 As of SQLAlchemy 1.4.0b2, this function works on a 

4092 per-:class:`_orm.registry` basis, locating all :class:`_orm.registry` 

4093 objects present and invoking the :meth:`_orm.registry.configure` method 

4094 on each. The :meth:`_orm.registry.configure` method may be preferred to 

4095 limit the configuration of mappers to those local to a particular 

4096 :class:`_orm.registry` and/or declarative base class. 

4097 

4098 Points at which automatic configuration is invoked include when a mapped 

4099 class is instantiated into an instance, as well as when ORM queries 

4100 are emitted using :meth:`.Session.query` or :meth:`_orm.Session.execute` 

4101 with an ORM-enabled statement. 

4102 

4103 The mapper configure process, whether invoked by 

4104 :func:`_orm.configure_mappers` or from :meth:`_orm.registry.configure`, 

4105 provides several event hooks that can be used to augment the mapper 

4106 configuration step. These hooks include: 

4107 

4108 * :meth:`.MapperEvents.before_configured` - called once before 

4109 :func:`.configure_mappers` or :meth:`_orm.registry.configure` does any 

4110 work; this can be used to establish additional options, properties, or 

4111 related mappings before the operation proceeds. 

4112 

4113 * :meth:`.MapperEvents.mapper_configured` - called as each individual 

4114 :class:`_orm.Mapper` is configured within the process; will include all 

4115 mapper state except for backrefs set up by other mappers that are still 

4116 to be configured. 

4117 

4118 * :meth:`.MapperEvents.after_configured` - called once after 

4119 :func:`.configure_mappers` or :meth:`_orm.registry.configure` is 

4120 complete; at this stage, all :class:`_orm.Mapper` objects that fall 

4121 within the scope of the configuration operation will be fully configured. 

4122 Note that the calling application may still have other mappings that 

4123 haven't been produced yet, such as if they are in modules as yet 

4124 unimported, and may also have mappings that are still to be configured, 

4125 if they are in other :class:`_orm.registry` collections not part of the 

4126 current scope of configuration. 

4127 

4128 """ 

4129 

4130 _configure_registries(_all_registries(), cascade=True) 

4131 

4132 

4133def _configure_registries( 

4134 registries: Set[_RegistryType], cascade: bool 

4135) -> None: 

4136 for reg in registries: 

4137 if reg._new_mappers: 

4138 break 

4139 else: 

4140 return 

4141 

4142 with _CONFIGURE_MUTEX: 

4143 global _already_compiling 

4144 if _already_compiling: 

4145 return 

4146 _already_compiling = True 

4147 try: 

4148 # double-check inside mutex 

4149 for reg in registries: 

4150 if reg._new_mappers: 

4151 break 

4152 else: 

4153 return 

4154 

4155 Mapper.dispatch._for_class(Mapper).before_configured() # type: ignore # noqa: E501 

4156 # initialize properties on all mappers 

4157 # note that _mapper_registry is unordered, which 

4158 # may randomly conceal/reveal issues related to 

4159 # the order of mapper compilation 

4160 

4161 _do_configure_registries(registries, cascade) 

4162 finally: 

4163 _already_compiling = False 

4164 Mapper.dispatch._for_class(Mapper).after_configured() # type: ignore 

4165 

4166 

4167@util.preload_module("sqlalchemy.orm.decl_api") 

4168def _do_configure_registries( 

4169 registries: Set[_RegistryType], cascade: bool 

4170) -> None: 

4171 registry = util.preloaded.orm_decl_api.registry 

4172 

4173 orig = set(registries) 

4174 

4175 for reg in registry._recurse_with_dependencies(registries): 

4176 has_skip = False 

4177 

4178 for mapper in reg._mappers_to_configure(): 

4179 run_configure = None 

4180 

4181 for fn in mapper.dispatch.before_mapper_configured: 

4182 run_configure = fn(mapper, mapper.class_) 

4183 if run_configure is EXT_SKIP: 

4184 has_skip = True 

4185 break 

4186 if run_configure is EXT_SKIP: 

4187 continue 

4188 

4189 if getattr(mapper, "_configure_failed", False): 

4190 e = sa_exc.InvalidRequestError( 

4191 "One or more mappers failed to initialize - " 

4192 "can't proceed with initialization of other " 

4193 "mappers. Triggering mapper: '%s'. " 

4194 "Original exception was: %s" 

4195 % (mapper, mapper._configure_failed) 

4196 ) 

4197 e._configure_failed = mapper._configure_failed # type: ignore 

4198 raise e 

4199 

4200 if not mapper.configured: 

4201 try: 

4202 mapper._post_configure_properties() 

4203 mapper._expire_memoizations() 

4204 mapper.dispatch.mapper_configured(mapper, mapper.class_) 

4205 except Exception: 

4206 exc = sys.exc_info()[1] 

4207 if not hasattr(exc, "_configure_failed"): 

4208 mapper._configure_failed = exc 

4209 raise 

4210 if not has_skip: 

4211 reg._new_mappers = False 

4212 

4213 if not cascade and reg._dependencies.difference(orig): 

4214 raise sa_exc.InvalidRequestError( 

4215 "configure was called with cascade=False but " 

4216 "additional registries remain" 

4217 ) 

4218 

4219 

4220@util.preload_module("sqlalchemy.orm.decl_api") 

4221def _dispose_registries(registries: Set[_RegistryType], cascade: bool) -> None: 

4222 registry = util.preloaded.orm_decl_api.registry 

4223 

4224 orig = set(registries) 

4225 

4226 for reg in registry._recurse_with_dependents(registries): 

4227 if not cascade and reg._dependents.difference(orig): 

4228 raise sa_exc.InvalidRequestError( 

4229 "Registry has dependent registries that are not disposed; " 

4230 "pass cascade=True to clear these also" 

4231 ) 

4232 

4233 while reg._managers: 

4234 try: 

4235 manager, _ = reg._managers.popitem() 

4236 except KeyError: 

4237 # guard against race between while and popitem 

4238 pass 

4239 else: 

4240 reg._dispose_manager_and_mapper(manager) 

4241 

4242 reg._dependents.clear() 

4243 for dep in reg._dependencies: 

4244 dep._dependents.discard(reg) 

4245 reg._dependencies.clear() 

4246 # this wasn't done in the 1.3 clear_mappers() and in fact it 

4247 # was a bug, as it could cause configure_mappers() to invoke 

4248 # the "before_configured" event even though mappers had all been 

4249 # disposed. 

4250 reg._new_mappers = False 

4251 

4252 

4253def reconstructor(fn: _Fn) -> _Fn: 

4254 """Decorate a method as the 'reconstructor' hook. 

4255 

4256 Designates a single method as the "reconstructor", an ``__init__``-like 

4257 method that will be called by the ORM after the instance has been 

4258 loaded from the database or otherwise reconstituted. 

4259 

4260 .. tip:: 

4261 

4262 The :func:`_orm.reconstructor` decorator makes use of the 

4263 :meth:`_orm.InstanceEvents.load` event hook, which can be 

4264 used directly. 

4265 

4266 The reconstructor will be invoked with no arguments. Scalar 

4267 (non-collection) database-mapped attributes of the instance will 

4268 be available for use within the function. Eagerly-loaded 

4269 collections are generally not yet available and will usually only 

4270 contain the first element. ORM state changes made to objects at 

4271 this stage will not be recorded for the next flush() operation, so 

4272 the activity within a reconstructor should be conservative. 

4273 

4274 .. seealso:: 

4275 

4276 :meth:`.InstanceEvents.load` 

4277 

4278 """ 

4279 fn.__sa_reconstructor__ = True # type: ignore[attr-defined] 

4280 return fn 

4281 

4282 

4283def validates( 

4284 *names: str, include_removes: bool = False, include_backrefs: bool = True 

4285) -> Callable[[_Fn], _Fn]: 

4286 r"""Decorate a method as a 'validator' for one or more named properties. 

4287 

4288 Designates a method as a validator, a method which receives the 

4289 name of the attribute as well as a value to be assigned, or in the 

4290 case of a collection, the value to be added to the collection. 

4291 The function can then raise validation exceptions to halt the 

4292 process from continuing (where Python's built-in ``ValueError`` 

4293 and ``AssertionError`` exceptions are reasonable choices), or can 

4294 modify or replace the value before proceeding. The function should 

4295 otherwise return the given value. 

4296 

4297 Note that a validator for a collection **cannot** issue a load of that 

4298 collection within the validation routine - this usage raises 

4299 an assertion to avoid recursion overflows. This is a reentrant 

4300 condition which is not supported. 

4301 

4302 :param \*names: list of attribute names to be validated. 

4303 :param include_removes: if True, "remove" events will be 

4304 sent as well - the validation function must accept an additional 

4305 argument "is_remove" which will be a boolean. 

4306 

4307 :param include_backrefs: defaults to ``True``; if ``False``, the 

4308 validation function will not emit if the originator is an attribute 

4309 event related via a backref. This can be used for bi-directional 

4310 :func:`.validates` usage where only one validator should emit per 

4311 attribute operation. 

4312 

4313 .. versionchanged:: 2.0.16 This paramter inadvertently defaulted to 

4314 ``False`` for releases 2.0.0 through 2.0.15. Its correct default 

4315 of ``True`` is restored in 2.0.16. 

4316 

4317 .. seealso:: 

4318 

4319 :ref:`simple_validators` - usage examples for :func:`.validates` 

4320 

4321 """ 

4322 

4323 def wrap(fn: _Fn) -> _Fn: 

4324 fn.__sa_validators__ = names # type: ignore[attr-defined] 

4325 fn.__sa_validation_opts__ = { # type: ignore[attr-defined] 

4326 "include_removes": include_removes, 

4327 "include_backrefs": include_backrefs, 

4328 } 

4329 return fn 

4330 

4331 return wrap 

4332 

4333 

4334def _event_on_load(state, ctx): 

4335 instrumenting_mapper = state.manager.mapper 

4336 

4337 if instrumenting_mapper._reconstructor: 

4338 instrumenting_mapper._reconstructor(state.obj()) 

4339 

4340 

4341def _event_on_init(state, args, kwargs): 

4342 """Run init_instance hooks. 

4343 

4344 This also includes mapper compilation, normally not needed 

4345 here but helps with some piecemeal configuration 

4346 scenarios (such as in the ORM tutorial). 

4347 

4348 """ 

4349 

4350 instrumenting_mapper = state.manager.mapper 

4351 if instrumenting_mapper: 

4352 instrumenting_mapper._check_configure() 

4353 if instrumenting_mapper._set_polymorphic_identity: 

4354 instrumenting_mapper._set_polymorphic_identity(state) 

4355 

4356 

4357class _ColumnMapping(Dict["ColumnElement[Any]", "MapperProperty[Any]"]): 

4358 """Error reporting helper for mapper._columntoproperty.""" 

4359 

4360 __slots__ = ("mapper",) 

4361 

4362 def __init__(self, mapper): 

4363 # TODO: weakref would be a good idea here 

4364 self.mapper = mapper 

4365 

4366 def __missing__(self, column): 

4367 prop = self.mapper._props.get(column) 

4368 if prop: 

4369 raise orm_exc.UnmappedColumnError( 

4370 "Column '%s.%s' is not available, due to " 

4371 "conflicting property '%s':%r" 

4372 % (column.table.name, column.name, column.key, prop) 

4373 ) 

4374 raise orm_exc.UnmappedColumnError( 

4375 "No column %s is configured on mapper %s..." 

4376 % (column, self.mapper) 

4377 )