Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/sqlalchemy/orm/mapper.py: 45%

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

1424 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 

92 

93if TYPE_CHECKING: 

94 from ._typing import _IdentityKeyType 

95 from ._typing import _InstanceDict 

96 from ._typing import _ORMColumnExprArgument 

97 from ._typing import _RegistryType 

98 from .decl_api import registry 

99 from .dependency import DependencyProcessor 

100 from .descriptor_props import CompositeProperty 

101 from .descriptor_props import SynonymProperty 

102 from .events import MapperEvents 

103 from .instrumentation import ClassManager 

104 from .path_registry import CachingEntityRegistry 

105 from .properties import ColumnProperty 

106 from .relationships import RelationshipProperty 

107 from .state import InstanceState 

108 from .util import ORMAdapter 

109 from ..engine import Row 

110 from ..engine import RowMapping 

111 from ..sql._typing import _ColumnExpressionArgument 

112 from ..sql._typing import _EquivalentColumnMap 

113 from ..sql.base import ReadOnlyColumnCollection 

114 from ..sql.elements import ColumnClause 

115 from ..sql.elements import ColumnElement 

116 from ..sql.selectable import FromClause 

117 from ..util import OrderedSet 

118 

119 

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

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

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

123 

124 

125_WithPolymorphicArg = Union[ 

126 Literal["*"], 

127 Tuple[ 

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

129 Optional["FromClause"], 

130 ], 

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

132] 

133 

134 

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

136 weakref.WeakKeyDictionary() 

137) 

138 

139 

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

141 with _CONFIGURE_MUTEX: 

142 return set(_mapper_registries) 

143 

144 

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

146 for reg in _all_registries(): 

147 yield from reg._mappers_to_configure() 

148 

149 

150_already_compiling = False 

151 

152 

153# a constant returned by _get_attr_by_column to indicate 

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

155# column 

156NO_ATTRIBUTE = util.symbol("NO_ATTRIBUTE") 

157 

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

159_CONFIGURE_MUTEX = threading.RLock() 

160 

161 

162@inspection._self_inspects 

163@log.class_logger 

164class Mapper( 

165 ORMFromClauseRole, 

166 ORMEntityColumnsClauseRole[_O], 

167 MemoizedHasCacheKey, 

168 InspectionAttr, 

169 log.Identified, 

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

171 EventTarget, 

172 Generic[_O], 

173): 

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

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

176 proceed. 

177 

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

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

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

181 :ref:`orm_mapping_classes_toplevel`. 

182 

183 """ 

184 

185 dispatch: dispatcher[Mapper[_O]] 

186 

187 _dispose_called = False 

188 _configure_failed: Any = False 

189 _ready_for_configure = False 

190 

191 @util.deprecated_params( 

192 non_primary=( 

193 "1.3", 

194 "The :paramref:`.mapper.non_primary` parameter is deprecated, " 

195 "and will be removed in a future release. The functionality " 

196 "of non primary mappers is now better suited using the " 

197 ":class:`.AliasedClass` construct, which can also be used " 

198 "as the target of a :func:`_orm.relationship` in 1.3.", 

199 ), 

200 ) 

201 def __init__( 

202 self, 

203 class_: Type[_O], 

204 local_table: Optional[FromClause] = None, 

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

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

207 non_primary: bool = False, 

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

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

210 inherit_foreign_keys: Optional[ 

211 Sequence[_ORMColumnExprArgument[Any]] 

212 ] = None, 

213 always_refresh: bool = False, 

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

215 version_id_generator: Optional[ 

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

217 ] = None, 

218 polymorphic_on: Optional[ 

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

220 ] = None, 

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

222 polymorphic_identity: Optional[Any] = None, 

223 concrete: bool = False, 

224 with_polymorphic: Optional[_WithPolymorphicArg] = None, 

225 polymorphic_abstract: bool = False, 

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

227 allow_partial_pks: bool = True, 

228 batch: bool = True, 

229 column_prefix: Optional[str] = None, 

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

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

232 passive_updates: bool = True, 

233 passive_deletes: bool = False, 

234 confirm_deleted_rows: bool = True, 

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

236 legacy_is_orphan: bool = False, 

237 _compiled_cache_size: int = 100, 

238 ): 

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

240 

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

242 is normally invoked through the 

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

244 :ref:`Declarative <orm_declarative_mapping>` or 

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

246 

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

248 removed; for a classical mapping configuration, use the 

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

250 

251 Parameters documented below may be passed to either the 

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

253 ``__mapper_args__`` declarative class attribute described at 

254 :ref:`orm_declarative_mapper_options`. 

255 

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

257 this argument is automatically passed as the declared class 

258 itself. 

259 

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

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

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

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

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

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

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

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

268 present. 

269 

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

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

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

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

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

275 inheritance scheme which uses 

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

277 

278 .. versionadded:: 2.0 

279 

280 .. seealso:: 

281 

282 :ref:`orm_inheritance_abstract_poly` 

283 

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

285 class will overwrite all data within object instances that already 

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

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

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

289 :meth:`_query.Query.populate_existing`. 

290 

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

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

293 possibly existing within the database. This affects whether a 

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

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

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

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

298 

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

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

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

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

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

304 

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

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

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

308 has partial NULL values will not be emitted. 

309 

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

311 of multiple entities can be batched together for efficiency. 

312 Setting to False indicates 

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

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

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

316 in between individual row persistence operations. 

317 

318 :param column_prefix: A string which will be prepended 

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

320 objects are automatically assigned as attributes to the 

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

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

323 dictionary. 

324 

325 This parameter is typically useful with imperative mappings 

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

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

328 ``user_id``, ``user_name``, and ``password``:: 

329 

330 class User(Base): 

331 __table__ = user_table 

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

333 

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

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

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

337 

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

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

340 approach to automating a naming scheme is to intercept the 

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

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

343 pattern. 

344 

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

346 table inheritance with its parent mapper. 

347 

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

349 

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

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

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

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

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

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

356 exception in a future release. 

357 

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

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

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

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

362 are needed immediately before the flush completes. 

363 

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

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

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

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

368 

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

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

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

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

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

374 attributes are not to be accessed in any case. 

375 

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

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

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

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

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

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

382 defaults will not be fetched. 

383 

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

385 :paramref:`.Mapper.eager_defaults` 

386 

387 .. seealso:: 

388 

389 :ref:`orm_server_defaults` 

390 

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

392 INSERTed at once using the 

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

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

395 feature to be very performant on supporting backends. 

396 

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

398 be excluded from mapping. 

399 

400 .. seealso:: 

401 

402 :ref:`include_exclude_cols` 

403 

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

405 names to map. 

406 

407 .. seealso:: 

408 

409 :ref:`include_exclude_cols` 

410 

411 :param inherits: A mapped class or the corresponding 

412 :class:`_orm.Mapper` 

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

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

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

416 is passed automatically as a result of the natural class 

417 hierarchy of the declared classes. 

418 

419 .. seealso:: 

420 

421 :ref:`inheritance_toplevel` 

422 

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

424 expression which will 

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

426 between the two tables. 

427 

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

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

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

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

432 

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

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

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

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

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

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

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

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

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

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

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

444 orphan object has been flushed yet or not. 

445 

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

447 for more detail on this change. 

448 

449 :param non_primary: Specify that this :class:`_orm.Mapper` 

450 is in addition 

451 to the "primary" mapper, that is, the one used for persistence. 

452 The :class:`_orm.Mapper` created here may be used for ad-hoc 

453 mapping of the class to an alternate selectable, for loading 

454 only. 

455 

456 .. seealso:: 

457 

458 :ref:`relationship_aliased_class` - the new pattern that removes 

459 the need for the :paramref:`_orm.Mapper.non_primary` flag. 

460 

461 :param passive_deletes: Indicates DELETE behavior of foreign key 

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

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

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

465 on the superclass mapper. 

466 

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

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

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

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

471 superclass table, and not this table. 

472 

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

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

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

476 validate these attributes; note that the primary key columns 

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

478 the object as a whole. 

479 

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

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

482 to specify passive_deletes without this taking effect for 

483 all subclass mappers. 

484 

485 .. seealso:: 

486 

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

488 used with :func:`_orm.relationship` 

489 

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

491 CASCADE for joined-table inheritance mappers 

492 

493 :param passive_updates: Indicates UPDATE behavior of foreign key 

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

495 inheritance mapping. Defaults to ``True``. 

496 

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

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

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

500 on joined-table rows. 

501 

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

503 referential integrity and will not be issuing its own CASCADE 

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

505 emit an UPDATE statement for the dependent columns during a 

506 primary key change. 

507 

508 .. seealso:: 

509 

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

511 used with :func:`_orm.relationship` 

512 

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

514 CASCADE for joined-table inheritance mappers 

515 

516 :param polymorphic_load: Specifies "polymorphic loading" behavior 

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

518 table inheritance only). Valid values are: 

519 

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

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

522 in a SELECT query against the base. 

523 

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

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

526 the columns specific to this subclass. The SELECT uses 

527 IN to fetch multiple subclasses at once. 

528 

529 .. versionadded:: 1.2 

530 

531 .. seealso:: 

532 

533 :ref:`with_polymorphic_mapper_config` 

534 

535 :ref:`polymorphic_selectin` 

536 

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

538 SQL expression used to determine the target class for an 

539 incoming row, when inheriting classes are present. 

540 

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

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

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

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

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

546 

547 class Employee(Base): 

548 __tablename__ = "employee" 

549 

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

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

552 

553 __mapper_args__ = { 

554 "polymorphic_on": discriminator, 

555 "polymorphic_identity": "employee", 

556 } 

557 

558 It may also be specified 

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

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

561 approach:: 

562 

563 class Employee(Base): 

564 __tablename__ = "employee" 

565 

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

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

568 

569 __mapper_args__ = { 

570 "polymorphic_on": case( 

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

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

573 else_="employee", 

574 ), 

575 "polymorphic_identity": "employee", 

576 } 

577 

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

579 which is of particular use when using annotated column 

580 configurations:: 

581 

582 class Employee(Base): 

583 __tablename__ = "employee" 

584 

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

586 discriminator: Mapped[str] 

587 

588 __mapper_args__ = { 

589 "polymorphic_on": "discriminator", 

590 "polymorphic_identity": "employee", 

591 } 

592 

593 When setting ``polymorphic_on`` to reference an 

594 attribute or expression that's not present in the 

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

596 of the discriminator should be persisted to the database, 

597 the value of the 

598 discriminator is not automatically set on new 

599 instances; this must be handled by the user, 

600 either through manual means or via event listeners. 

601 A typical approach to establishing such a listener 

602 looks like:: 

603 

604 from sqlalchemy import event 

605 from sqlalchemy.orm import object_mapper 

606 

607 

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

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

610 mapper = object_mapper(instance) 

611 instance.discriminator = mapper.polymorphic_identity 

612 

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

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

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

616 in the database. 

617 

618 .. warning:: 

619 

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

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

622 columns are not yet supported. 

623 

624 .. seealso:: 

625 

626 :ref:`inheritance_toplevel` 

627 

628 :param polymorphic_identity: Specifies the value which 

629 identifies this particular class as returned by the column expression 

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

631 rows are received, the value corresponding to the 

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

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

634 reconstructed object. 

635 

636 .. seealso:: 

637 

638 :ref:`inheritance_toplevel` 

639 

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

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

642 persistence behavior of that attribute. Note that 

643 :class:`_schema.Column` 

644 objects present in 

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

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

647 When using Declarative, this argument is passed automatically, 

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

649 in the declared class body. 

650 

651 .. seealso:: 

652 

653 :ref:`orm_mapping_properties` - in the 

654 :ref:`orm_mapping_classes_toplevel` 

655 

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

657 objects, or alternatively string names of attribute names which 

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

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

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

661 can be overridden here. 

662 

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

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

665 

666 .. seealso:: 

667 

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

669 

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

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

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

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

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

675 version id, a 

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

677 thrown. 

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

679 unless ``version_id_generator`` specifies an alternative version 

680 generator. 

681 

682 .. seealso:: 

683 

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

685 and rationale. 

686 

687 :param version_id_generator: Define how new version ids should 

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

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

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

691 

692 def generate_version(version): 

693 return next_version 

694 

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

696 or programmatic versioning schemes outside of the version id 

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

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

699 of important points when using this option. 

700 

701 .. seealso:: 

702 

703 :ref:`custom_version_counter` 

704 

705 :ref:`server_side_version_counter` 

706 

707 

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

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

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

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

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

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

714 loaded immediately. The second tuple argument <selectable> 

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

716 classes. 

717 

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

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

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

721 indicating polymorphic loading styles. 

722 

723 .. seealso:: 

724 

725 :ref:`with_polymorphic_mapper_config` 

726 

727 """ 

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

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

730 self.class_.__module__, 

731 self.class_.__name__, 

732 ) 

733 

734 self._primary_key_argument = util.to_list(primary_key) 

735 self.non_primary = non_primary 

736 

737 self.always_refresh = always_refresh 

738 

739 if isinstance(version_id_col, MapperProperty): 

740 self.version_id_prop = version_id_col 

741 self.version_id_col = None 

742 else: 

743 self.version_id_col = ( 

744 coercions.expect( 

745 roles.ColumnArgumentOrKeyRole, 

746 version_id_col, 

747 argname="version_id_col", 

748 ) 

749 if version_id_col is not None 

750 else None 

751 ) 

752 

753 if version_id_generator is False: 

754 self.version_id_generator = False 

755 elif version_id_generator is None: 

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

757 else: 

758 self.version_id_generator = version_id_generator 

759 

760 self.concrete = concrete 

761 self.single = False 

762 

763 if inherits is not None: 

764 self.inherits = _parse_mapper_argument(inherits) 

765 else: 

766 self.inherits = None 

767 

768 if local_table is not None: 

769 self.local_table = coercions.expect( 

770 roles.StrictFromClauseRole, 

771 local_table, 

772 disable_inspection=True, 

773 argname="local_table", 

774 ) 

775 elif self.inherits: 

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

777 # .local_table need not be Optional 

778 self.local_table = self.inherits.local_table 

779 self.single = True 

780 else: 

781 raise sa_exc.ArgumentError( 

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

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

784 ) 

785 

786 if inherit_condition is not None: 

787 self.inherit_condition = coercions.expect( 

788 roles.OnClauseRole, inherit_condition 

789 ) 

790 else: 

791 self.inherit_condition = None 

792 

793 self.inherit_foreign_keys = inherit_foreign_keys 

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

795 self._delete_orphans = [] 

796 self.batch = batch 

797 self.eager_defaults = eager_defaults 

798 self.column_prefix = column_prefix 

799 

800 # interim - polymorphic_on is further refined in 

801 # _configure_polymorphic_setter 

802 self.polymorphic_on = ( 

803 coercions.expect( # type: ignore 

804 roles.ColumnArgumentOrKeyRole, 

805 polymorphic_on, 

806 argname="polymorphic_on", 

807 ) 

808 if polymorphic_on is not None 

809 else None 

810 ) 

811 self.polymorphic_abstract = polymorphic_abstract 

812 self._dependency_processors = [] 

813 self.validators = util.EMPTY_DICT 

814 self.passive_updates = passive_updates 

815 self.passive_deletes = passive_deletes 

816 self.legacy_is_orphan = legacy_is_orphan 

817 self._clause_adapter = None 

818 self._requires_row_aliasing = False 

819 self._inherits_equated_pairs = None 

820 self._memoized_values = {} 

821 self._compiled_cache_size = _compiled_cache_size 

822 self._reconstructor = None 

823 self.allow_partial_pks = allow_partial_pks 

824 

825 if self.inherits and not self.concrete: 

826 self.confirm_deleted_rows = False 

827 else: 

828 self.confirm_deleted_rows = confirm_deleted_rows 

829 

830 self._set_with_polymorphic(with_polymorphic) 

831 self.polymorphic_load = polymorphic_load 

832 

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

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

835 # the object instance for that row. 

836 self.polymorphic_identity = polymorphic_identity 

837 

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

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

840 # upon a select operation. 

841 if _polymorphic_map is None: 

842 self.polymorphic_map = {} 

843 else: 

844 self.polymorphic_map = _polymorphic_map 

845 

846 if include_properties is not None: 

847 self.include_properties = util.to_set(include_properties) 

848 else: 

849 self.include_properties = None 

850 if exclude_properties: 

851 self.exclude_properties = util.to_set(exclude_properties) 

852 else: 

853 self.exclude_properties = None 

854 

855 # prevent this mapper from being constructed 

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

857 # configure_mappers() until construction succeeds) 

858 with _CONFIGURE_MUTEX: 

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

860 class_, self 

861 ) 

862 self._configure_inheritance() 

863 self._configure_class_instrumentation() 

864 self._configure_properties() 

865 self._configure_polymorphic_setter() 

866 self._configure_pks() 

867 self.registry._flag_new_mapper(self) 

868 self._log("constructed") 

869 self._expire_memoizations() 

870 

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

872 

873 def _prefer_eager_defaults(self, dialect, table): 

874 if self.eager_defaults == "auto": 

875 if not table.implicit_returning: 

876 return False 

877 

878 return ( 

879 table in self._server_default_col_keys 

880 and dialect.insert_executemany_returning 

881 ) 

882 else: 

883 return self.eager_defaults 

884 

885 def _gen_cache_key(self, anon_map, bindparams): 

886 return (self,) 

887 

888 # ### BEGIN 

889 # ATTRIBUTE DECLARATIONS START HERE 

890 

891 is_mapper = True 

892 """Part of the inspection API.""" 

893 

894 represents_outer_join = False 

895 

896 registry: _RegistryType 

897 

898 @property 

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

900 """Part of the inspection API. 

901 

902 Returns self. 

903 

904 """ 

905 return self 

906 

907 @property 

908 def entity(self): 

909 r"""Part of the inspection API. 

910 

911 Returns self.class\_. 

912 

913 """ 

914 return self.class_ 

915 

916 class_: Type[_O] 

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

918 

919 _identity_class: Type[_O] 

920 

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

922 _dependency_processors: List[DependencyProcessor] 

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

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

925 _all_tables: Set[TableClause] 

926 _polymorphic_attr_key: Optional[str] 

927 

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

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

930 

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

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

933 

934 _columntoproperty: _ColumnMapping 

935 

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

937 _validate_polymorphic_identity: Optional[ 

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

939 ] 

940 

941 tables: Sequence[TableClause] 

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

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

944 is aware of. 

945 

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

947 :class:`_expression.Alias` 

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

949 :class:`_schema.Table` 

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

951 

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

953 Behavior is undefined if directly modified. 

954 

955 """ 

956 

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

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

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

960 

961 The dictionary contains string attribute names as keys 

962 mapped to the actual validation method. 

963 

964 """ 

965 

966 always_refresh: bool 

967 allow_partial_pks: bool 

968 version_id_col: Optional[ColumnElement[Any]] 

969 

970 with_polymorphic: Optional[ 

971 Tuple[ 

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

973 Optional[FromClause], 

974 ] 

975 ] 

976 

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

978 

979 local_table: FromClause 

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

981 :class:`_orm.Mapper` refers. 

982 

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

984 :class:`.FromClause`. 

985 

986 The "local" table is the 

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

988 managing from an attribute access and flush perspective. For 

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

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

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

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

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

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

995 

996 .. seealso:: 

997 

998 :attr:`_orm.Mapper.persist_selectable`. 

999 

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

1001 

1002 """ 

1003 

1004 persist_selectable: FromClause 

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

1006 is mapped. 

1007 

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

1009 :class:`.FromClause`. 

1010 

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

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

1013 represents the inheriting class hierarchy overall in an inheritance 

1014 scenario. 

1015 

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

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

1018 alternate subquery used for selecting columns. 

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

1020 will be written on a persist operation. 

1021 

1022 .. seealso:: 

1023 

1024 :attr:`_orm.Mapper.selectable`. 

1025 

1026 :attr:`_orm.Mapper.local_table`. 

1027 

1028 """ 

1029 

1030 inherits: Optional[Mapper[Any]] 

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

1032 inherits from, if any. 

1033 

1034 """ 

1035 

1036 inherit_condition: Optional[ColumnElement[bool]] 

1037 

1038 configured: bool = False 

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

1040 

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

1042 Behavior is undefined if directly modified. 

1043 

1044 .. seealso:: 

1045 

1046 :func:`.configure_mappers`. 

1047 

1048 """ 

1049 

1050 concrete: bool 

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

1052 inheritance mapper. 

1053 

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

1055 Behavior is undefined if directly modified. 

1056 

1057 """ 

1058 

1059 primary_key: Tuple[Column[Any], ...] 

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

1061 objects 

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

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

1064 

1065 This list is against the selectable in 

1066 :attr:`_orm.Mapper.persist_selectable`. 

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

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

1069 :class:`_expression.Join`, the 

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

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

1072 

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

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

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

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

1077 

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

1079 Behavior is undefined if directly modified. 

1080 

1081 """ 

1082 

1083 class_manager: ClassManager[_O] 

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

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

1086 

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

1088 Behavior is undefined if directly modified. 

1089 

1090 """ 

1091 

1092 single: bool 

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

1094 inheritance mapper. 

1095 

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

1097 

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

1099 Behavior is undefined if directly modified. 

1100 

1101 """ 

1102 

1103 non_primary: bool 

1104 """Represent ``True`` if this :class:`_orm.Mapper` is a "non-primary" 

1105 mapper, e.g. a mapper that is used only to select rows but not for 

1106 persistence management. 

1107 

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

1109 Behavior is undefined if directly modified. 

1110 

1111 """ 

1112 

1113 polymorphic_on: Optional[KeyedColumnElement[Any]] 

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

1115 ``polymorphic_on`` argument 

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

1117 

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

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

1120 :func:`.cast`. 

1121 

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

1123 Behavior is undefined if directly modified. 

1124 

1125 """ 

1126 

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

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

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

1130 

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

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

1133 

1134 An inheritance chain of mappers will all reference the same 

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

1136 result rows to target mappers. 

1137 

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

1139 Behavior is undefined if directly modified. 

1140 

1141 """ 

1142 

1143 polymorphic_identity: Optional[Any] 

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

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

1146 

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

1148 comparable to the type of column represented by 

1149 :attr:`_orm.Mapper.polymorphic_on`. 

1150 

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

1152 Behavior is undefined if directly modified. 

1153 

1154 """ 

1155 

1156 base_mapper: Mapper[Any] 

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

1158 

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

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

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

1162 objects in the inheritance chain. 

1163 

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

1165 Behavior is undefined if directly modified. 

1166 

1167 """ 

1168 

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

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

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

1172 

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

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

1175 except that only those columns included in 

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

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

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

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

1180 

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

1182 Behavior is undefined if directly modified. 

1183 

1184 """ 

1185 

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

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

1188 

1189 @util.non_memoized_property 

1190 @util.deprecated("1.3", "Use .persist_selectable") 

1191 def mapped_table(self): 

1192 return self.persist_selectable 

1193 

1194 @util.memoized_property 

1195 def _path_registry(self) -> CachingEntityRegistry: 

1196 return PathRegistry.per_mapper(self) 

1197 

1198 def _configure_inheritance(self): 

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

1200 being present.""" 

1201 

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

1203 self._inheriting_mappers = util.WeakSequence() 

1204 

1205 if self.inherits: 

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

1207 raise sa_exc.ArgumentError( 

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

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

1210 ) 

1211 

1212 self.dispatch._update(self.inherits.dispatch) 

1213 

1214 if self.non_primary != self.inherits.non_primary: 

1215 np = not self.non_primary and "primary" or "non-primary" 

1216 raise sa_exc.ArgumentError( 

1217 "Inheritance of %s mapper for class '%s' is " 

1218 "only allowed from a %s mapper" 

1219 % (np, self.class_.__name__, np) 

1220 ) 

1221 

1222 if self.single: 

1223 self.persist_selectable = self.inherits.persist_selectable 

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

1225 if self.concrete: 

1226 self.persist_selectable = self.local_table 

1227 for mapper in self.iterate_to_root(): 

1228 if mapper.polymorphic_on is not None: 

1229 mapper._requires_row_aliasing = True 

1230 else: 

1231 if self.inherit_condition is None: 

1232 # figure out inherit condition from our table to the 

1233 # immediate table of the inherited mapper, not its 

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

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

1236 try: 

1237 self.inherit_condition = sql_util.join_condition( 

1238 self.inherits.local_table, self.local_table 

1239 ) 

1240 except sa_exc.NoForeignKeysError as nfe: 

1241 assert self.inherits.local_table is not None 

1242 assert self.local_table is not None 

1243 raise sa_exc.NoForeignKeysError( 

1244 "Can't determine the inherit condition " 

1245 "between inherited table '%s' and " 

1246 "inheriting " 

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

1248 "foreign key relationships established. " 

1249 "Please ensure the inheriting table has " 

1250 "a foreign key relationship to the " 

1251 "inherited " 

1252 "table, or provide an " 

1253 "'on clause' using " 

1254 "the 'inherit_condition' mapper argument." 

1255 % ( 

1256 self.inherits.local_table.description, 

1257 self.local_table.description, 

1258 ) 

1259 ) from nfe 

1260 except sa_exc.AmbiguousForeignKeysError as afe: 

1261 assert self.inherits.local_table is not None 

1262 assert self.local_table is not None 

1263 raise sa_exc.AmbiguousForeignKeysError( 

1264 "Can't determine the inherit condition " 

1265 "between inherited table '%s' and " 

1266 "inheriting " 

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

1268 "foreign key relationship established. " 

1269 "Please specify the 'on clause' using " 

1270 "the 'inherit_condition' mapper argument." 

1271 % ( 

1272 self.inherits.local_table.description, 

1273 self.local_table.description, 

1274 ) 

1275 ) from afe 

1276 assert self.inherits.persist_selectable is not None 

1277 self.persist_selectable = sql.join( 

1278 self.inherits.persist_selectable, 

1279 self.local_table, 

1280 self.inherit_condition, 

1281 ) 

1282 

1283 fks = util.to_set(self.inherit_foreign_keys) 

1284 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 

1285 self.persist_selectable.onclause, 

1286 consider_as_foreign_keys=fks, 

1287 ) 

1288 else: 

1289 self.persist_selectable = self.local_table 

1290 

1291 if self.polymorphic_identity is None: 

1292 self._identity_class = self.class_ 

1293 

1294 if ( 

1295 not self.polymorphic_abstract 

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

1297 ): 

1298 util.warn( 

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

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

1301 f"'polymorphic_on' column of " 

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

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

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

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

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

1307 "class unmapped when using Declarative, set the " 

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

1309 ) 

1310 elif self.concrete: 

1311 self._identity_class = self.class_ 

1312 else: 

1313 self._identity_class = self.inherits._identity_class 

1314 

1315 if self.version_id_col is None: 

1316 self.version_id_col = self.inherits.version_id_col 

1317 self.version_id_generator = self.inherits.version_id_generator 

1318 elif ( 

1319 self.inherits.version_id_col is not None 

1320 and self.version_id_col is not self.inherits.version_id_col 

1321 ): 

1322 util.warn( 

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

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

1325 "the inherited versioning column. " 

1326 "version_id_col should only be specified on " 

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

1328 % ( 

1329 self.version_id_col.description, 

1330 self.inherits.version_id_col.description, 

1331 ) 

1332 ) 

1333 

1334 self.polymorphic_map = self.inherits.polymorphic_map 

1335 self.batch = self.inherits.batch 

1336 self.inherits._inheriting_mappers.append(self) 

1337 self.base_mapper = self.inherits.base_mapper 

1338 self.passive_updates = self.inherits.passive_updates 

1339 self.passive_deletes = ( 

1340 self.inherits.passive_deletes or self.passive_deletes 

1341 ) 

1342 self._all_tables = self.inherits._all_tables 

1343 

1344 if self.polymorphic_identity is not None: 

1345 if self.polymorphic_identity in self.polymorphic_map: 

1346 util.warn( 

1347 "Reassigning polymorphic association for identity %r " 

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

1349 "value for polymorphic_identity." 

1350 % ( 

1351 self.polymorphic_identity, 

1352 self.polymorphic_map[self.polymorphic_identity], 

1353 self, 

1354 self.polymorphic_identity, 

1355 ) 

1356 ) 

1357 self.polymorphic_map[self.polymorphic_identity] = self 

1358 

1359 if self.polymorphic_load and self.concrete: 

1360 raise sa_exc.ArgumentError( 

1361 "polymorphic_load is not currently supported " 

1362 "with concrete table inheritance" 

1363 ) 

1364 if self.polymorphic_load == "inline": 

1365 self.inherits._add_with_polymorphic_subclass(self) 

1366 elif self.polymorphic_load == "selectin": 

1367 pass 

1368 elif self.polymorphic_load is not None: 

1369 raise sa_exc.ArgumentError( 

1370 "unknown argument for polymorphic_load: %r" 

1371 % self.polymorphic_load 

1372 ) 

1373 

1374 else: 

1375 self._all_tables = set() 

1376 self.base_mapper = self 

1377 assert self.local_table is not None 

1378 self.persist_selectable = self.local_table 

1379 if self.polymorphic_identity is not None: 

1380 self.polymorphic_map[self.polymorphic_identity] = self 

1381 self._identity_class = self.class_ 

1382 

1383 if self.persist_selectable is None: 

1384 raise sa_exc.ArgumentError( 

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

1386 % self 

1387 ) 

1388 

1389 def _set_with_polymorphic( 

1390 self, with_polymorphic: Optional[_WithPolymorphicArg] 

1391 ) -> None: 

1392 if with_polymorphic == "*": 

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

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

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

1396 self.with_polymorphic = cast( 

1397 """Tuple[ 

1398 Union[ 

1399 Literal["*"], 

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

1401 ], 

1402 Optional["FromClause"], 

1403 ]""", 

1404 with_polymorphic, 

1405 ) 

1406 else: 

1407 self.with_polymorphic = (with_polymorphic, None) 

1408 elif with_polymorphic is not None: 

1409 raise sa_exc.ArgumentError( 

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

1411 ) 

1412 else: 

1413 self.with_polymorphic = None 

1414 

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

1416 self.with_polymorphic = ( 

1417 self.with_polymorphic[0], 

1418 coercions.expect( 

1419 roles.StrictFromClauseRole, 

1420 self.with_polymorphic[1], 

1421 allow_select=True, 

1422 ), 

1423 ) 

1424 

1425 if self.configured: 

1426 self._expire_memoizations() 

1427 

1428 def _add_with_polymorphic_subclass(self, mapper): 

1429 subcl = mapper.class_ 

1430 if self.with_polymorphic is None: 

1431 self._set_with_polymorphic((subcl,)) 

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

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

1434 self._set_with_polymorphic( 

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

1436 ) 

1437 

1438 def _set_concrete_base(self, mapper): 

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

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

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

1442 

1443 assert self.concrete 

1444 assert not self.inherits 

1445 assert isinstance(mapper, Mapper) 

1446 self.inherits = mapper 

1447 self.inherits.polymorphic_map.update(self.polymorphic_map) 

1448 self.polymorphic_map = self.inherits.polymorphic_map 

1449 for mapper in self.iterate_to_root(): 

1450 if mapper.polymorphic_on is not None: 

1451 mapper._requires_row_aliasing = True 

1452 self.batch = self.inherits.batch 

1453 for mp in self.self_and_descendants: 

1454 mp.base_mapper = self.inherits.base_mapper 

1455 self.inherits._inheriting_mappers.append(self) 

1456 self.passive_updates = self.inherits.passive_updates 

1457 self._all_tables = self.inherits._all_tables 

1458 

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

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

1461 key, key, local=False, column=None 

1462 ): 

1463 self._adapt_inherited_property(key, prop, False) 

1464 

1465 def _set_polymorphic_on(self, polymorphic_on): 

1466 self.polymorphic_on = polymorphic_on 

1467 self._configure_polymorphic_setter(True) 

1468 

1469 def _configure_class_instrumentation(self): 

1470 """If this mapper is to be a primary mapper (i.e. the 

1471 non_primary flag is not set), associate this Mapper with the 

1472 given class and entity name. 

1473 

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

1475 name combination will return this mapper. Also decorate the 

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

1477 auto-session attachment logic. 

1478 

1479 """ 

1480 

1481 # we expect that declarative has applied the class manager 

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

1483 # this raises as of 2.0. 

1484 manager = attributes.opt_manager_of_class(self.class_) 

1485 

1486 if self.non_primary: 

1487 if not manager or not manager.is_mapped: 

1488 raise sa_exc.InvalidRequestError( 

1489 "Class %s has no primary mapper configured. Configure " 

1490 "a primary mapper first before setting up a non primary " 

1491 "Mapper." % self.class_ 

1492 ) 

1493 self.class_manager = manager 

1494 

1495 assert manager.registry is not None 

1496 self.registry = manager.registry 

1497 self._identity_class = manager.mapper._identity_class 

1498 manager.registry._add_non_primary_mapper(self) 

1499 return 

1500 

1501 if manager is None or not manager.registry: 

1502 raise sa_exc.InvalidRequestError( 

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

1504 "invoked directly outside of a declarative registry." 

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

1506 "function for a classical mapping." 

1507 ) 

1508 

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

1510 

1511 # this invokes the class_instrument event and sets up 

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

1513 # occur after the instrument_class event above. 

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

1515 # :( 

1516 

1517 manager = instrumentation.register_class( 

1518 self.class_, 

1519 mapper=self, 

1520 expired_attribute_loader=util.partial( 

1521 loading.load_scalar_attributes, self 

1522 ), 

1523 # finalize flag means instrument the __init__ method 

1524 # and call the class_instrument event 

1525 finalize=True, 

1526 ) 

1527 

1528 self.class_manager = manager 

1529 

1530 assert manager.registry is not None 

1531 self.registry = manager.registry 

1532 

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

1534 # e_name None or not. 

1535 if manager.mapper is None: 

1536 return 

1537 

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

1539 

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

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

1542 method = method._sa_original_init 

1543 if hasattr(method, "__func__"): 

1544 method = method.__func__ 

1545 if callable(method): 

1546 if hasattr(method, "__sa_reconstructor__"): 

1547 self._reconstructor = method 

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

1549 elif hasattr(method, "__sa_validators__"): 

1550 validation_opts = method.__sa_validation_opts__ 

1551 for name in method.__sa_validators__: 

1552 if name in self.validators: 

1553 raise sa_exc.InvalidRequestError( 

1554 "A validation function for mapped " 

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

1556 % (name, self) 

1557 ) 

1558 self.validators = self.validators.union( 

1559 {name: (method, validation_opts)} 

1560 ) 

1561 

1562 def _set_dispose_flags(self) -> None: 

1563 self.configured = True 

1564 self._ready_for_configure = True 

1565 self._dispose_called = True 

1566 

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

1568 

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

1570 try: 

1571 prop = self._props[key] 

1572 except KeyError as err: 

1573 raise sa_exc.ArgumentError( 

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

1575 "no attribute is mapped to this name." 

1576 ) from err 

1577 try: 

1578 expr = prop.expression 

1579 except AttributeError as ae: 

1580 raise sa_exc.ArgumentError( 

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

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

1583 ) from ae 

1584 if not isinstance(expr, Column): 

1585 raise sa_exc.ArgumentError( 

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

1587 "property does not refer to a single " 

1588 "mapped Column" 

1589 ) 

1590 return expr 

1591 

1592 def _configure_pks(self) -> None: 

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

1594 

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

1596 

1597 self._pks_by_table = {} 

1598 self._cols_by_table = {} 

1599 

1600 all_cols = util.column_set( 

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

1602 ) 

1603 

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

1605 

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

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

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

1609 # ordering is important since it determines the ordering of 

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

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

1612 fc.primary_key 

1613 ).intersection( 

1614 pk_cols 

1615 ) 

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

1617 all_cols 

1618 ) 

1619 

1620 if self._primary_key_argument: 

1621 coerced_pk_arg = [ 

1622 ( 

1623 self._str_arg_to_mapped_col("primary_key", c) 

1624 if isinstance(c, str) 

1625 else c 

1626 ) 

1627 for c in ( 

1628 coercions.expect( 

1629 roles.DDLConstraintColumnRole, 

1630 coerce_pk, 

1631 argname="primary_key", 

1632 ) 

1633 for coerce_pk in self._primary_key_argument 

1634 ) 

1635 ] 

1636 else: 

1637 coerced_pk_arg = None 

1638 

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

1640 # primary key mappings 

1641 if coerced_pk_arg: 

1642 for k in coerced_pk_arg: 

1643 if k.table not in self._pks_by_table: 

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

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

1646 

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

1648 elif ( 

1649 self.persist_selectable not in self._pks_by_table 

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

1651 ): 

1652 raise sa_exc.ArgumentError( 

1653 "Mapper %s could not assemble any primary " 

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

1655 % (self, self.persist_selectable.description) 

1656 ) 

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

1658 self.local_table, schema.Table 

1659 ): 

1660 util.warn( 

1661 "Could not assemble any primary " 

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

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

1664 % self.local_table.description 

1665 ) 

1666 

1667 if ( 

1668 self.inherits 

1669 and not self.concrete 

1670 and not self._primary_key_argument 

1671 ): 

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

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

1674 self.primary_key = self.inherits.primary_key 

1675 else: 

1676 # determine primary key from argument or persist_selectable pks 

1677 primary_key: Collection[ColumnElement[Any]] 

1678 

1679 if coerced_pk_arg: 

1680 primary_key = [ 

1681 cc if cc is not None else c 

1682 for cc, c in ( 

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

1684 for c in coerced_pk_arg 

1685 ) 

1686 ] 

1687 else: 

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

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

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

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

1692 primary_key = sql_util.reduce_columns( 

1693 self._pks_by_table[self.persist_selectable], 

1694 ignore_nonexistent_tables=True, 

1695 ) 

1696 

1697 if len(primary_key) == 0: 

1698 raise sa_exc.ArgumentError( 

1699 "Mapper %s could not assemble any primary " 

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

1701 % (self, self.persist_selectable.description) 

1702 ) 

1703 

1704 self.primary_key = tuple(primary_key) 

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

1706 

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

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

1709 self._readonly_props = { 

1710 self._columntoproperty[col] 

1711 for col in self._columntoproperty 

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

1713 and ( 

1714 not hasattr(col, "table") 

1715 or col.table not in self._cols_by_table 

1716 ) 

1717 } 

1718 

1719 def _configure_properties(self) -> None: 

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

1721 

1722 # object attribute names mapped to MapperProperty objects 

1723 self._props = util.OrderedDict() 

1724 

1725 # table columns mapped to MapperProperty 

1726 self._columntoproperty = _ColumnMapping(self) 

1727 

1728 explicit_col_props_by_column: Dict[ 

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

1730 ] = {} 

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

1732 

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

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

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

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

1737 if self._init_properties: 

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

1739 if not isinstance(prop_arg, MapperProperty): 

1740 possible_col_prop = self._make_prop_from_column( 

1741 key, prop_arg 

1742 ) 

1743 else: 

1744 possible_col_prop = prop_arg 

1745 

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

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

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

1749 # the Table. 

1750 

1751 _map_as_property_now = True 

1752 if isinstance(possible_col_prop, properties.ColumnProperty): 

1753 for given_col in possible_col_prop.columns: 

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

1755 _map_as_property_now = False 

1756 explicit_col_props_by_key[key] = possible_col_prop 

1757 explicit_col_props_by_column[given_col] = ( 

1758 key, 

1759 possible_col_prop, 

1760 ) 

1761 

1762 if _map_as_property_now: 

1763 self._configure_property( 

1764 key, 

1765 possible_col_prop, 

1766 init=False, 

1767 ) 

1768 

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

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

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

1772 if self.inherits: 

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

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

1775 continue 

1776 

1777 incoming_prop = explicit_col_props_by_key.get(key) 

1778 if incoming_prop: 

1779 new_prop = self._reconcile_prop_with_incoming_columns( 

1780 key, 

1781 inherited_prop, 

1782 warn_only=False, 

1783 incoming_prop=incoming_prop, 

1784 ) 

1785 explicit_col_props_by_key[key] = new_prop 

1786 

1787 for inc_col in incoming_prop.columns: 

1788 explicit_col_props_by_column[inc_col] = ( 

1789 key, 

1790 new_prop, 

1791 ) 

1792 elif key not in self._props: 

1793 self._adapt_inherited_property(key, inherited_prop, False) 

1794 

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

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

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

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

1799 # reconciliation against inherited columns occurs here also. 

1800 

1801 for column in self.persist_selectable.columns: 

1802 if column in explicit_col_props_by_column: 

1803 # column was explicitly passed to properties; configure 

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

1805 # Table / selectable 

1806 key, prop = explicit_col_props_by_column[column] 

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

1808 continue 

1809 

1810 elif column in self._columntoproperty: 

1811 continue 

1812 

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

1814 if self._should_exclude( 

1815 column.key, 

1816 column_key, 

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

1818 column=column, 

1819 ): 

1820 continue 

1821 

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

1823 # of the inheriting mapper 

1824 for mapper in self.iterate_to_root(): 

1825 if column in mapper._columntoproperty: 

1826 column_key = mapper._columntoproperty[column].key 

1827 

1828 self._configure_property( 

1829 column_key, 

1830 column, 

1831 init=False, 

1832 setparent=True, 

1833 ) 

1834 

1835 def _configure_polymorphic_setter(self, init=False): 

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

1837 'polymorphic_on' column, if applicable, and not 

1838 already generated by _configure_properties (which is typical). 

1839 

1840 Also create a setter function which will assign this 

1841 attribute to the value of the 'polymorphic_identity' 

1842 upon instance construction, also if applicable. This 

1843 routine will run when an instance is created. 

1844 

1845 """ 

1846 setter = False 

1847 polymorphic_key: Optional[str] = None 

1848 

1849 if self.polymorphic_on is not None: 

1850 setter = True 

1851 

1852 if isinstance(self.polymorphic_on, str): 

1853 # polymorphic_on specified as a string - link 

1854 # it to mapped ColumnProperty 

1855 try: 

1856 self.polymorphic_on = self._props[self.polymorphic_on] 

1857 except KeyError as err: 

1858 raise sa_exc.ArgumentError( 

1859 "Can't determine polymorphic_on " 

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

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

1862 ) from err 

1863 

1864 if self.polymorphic_on in self._columntoproperty: 

1865 # polymorphic_on is a column that is already mapped 

1866 # to a ColumnProperty 

1867 prop = self._columntoproperty[self.polymorphic_on] 

1868 elif isinstance(self.polymorphic_on, MapperProperty): 

1869 # polymorphic_on is directly a MapperProperty, 

1870 # ensure it's a ColumnProperty 

1871 if not isinstance( 

1872 self.polymorphic_on, properties.ColumnProperty 

1873 ): 

1874 raise sa_exc.ArgumentError( 

1875 "Only direct column-mapped " 

1876 "property or SQL expression " 

1877 "can be passed for polymorphic_on" 

1878 ) 

1879 prop = self.polymorphic_on 

1880 else: 

1881 # polymorphic_on is a Column or SQL expression and 

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

1883 # only present in the with_polymorphic selectable or 

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

1885 # hope is compatible with this mapper's persist_selectable 

1886 col = self.persist_selectable.corresponding_column( 

1887 self.polymorphic_on 

1888 ) 

1889 if col is None: 

1890 # polymorphic_on doesn't derive from any 

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

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

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

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

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

1896 # represented somehow in either persist_selectable or 

1897 # with_polymorphic. Otherwise as of 0.7.4 we 

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

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

1900 setter = False 

1901 instrument = False 

1902 col = self.polymorphic_on 

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

1904 self.with_polymorphic is None 

1905 or self.with_polymorphic[1] is None 

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

1907 is None 

1908 ): 

1909 raise sa_exc.InvalidRequestError( 

1910 "Could not map polymorphic_on column " 

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

1912 "loads will not function properly" 

1913 % col.description 

1914 ) 

1915 else: 

1916 # column/expression that polymorphic_on derives from 

1917 # is present in our mapped table 

1918 # and is probably mapped, but polymorphic_on itself 

1919 # is not. This happens when 

1920 # the polymorphic_on is only directly present in the 

1921 # with_polymorphic selectable, as when use 

1922 # polymorphic_union. 

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

1924 instrument = True 

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

1926 if key: 

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

1928 raise sa_exc.InvalidRequestError( 

1929 "Cannot exclude or override the " 

1930 "discriminator column %r" % key 

1931 ) 

1932 else: 

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

1934 key = col.key 

1935 

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

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

1938 

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

1940 # column in the property 

1941 self.polymorphic_on = prop.columns[0] 

1942 polymorphic_key = prop.key 

1943 else: 

1944 # no polymorphic_on was set. 

1945 # check inheriting mappers for one. 

1946 for mapper in self.iterate_to_root(): 

1947 # determine if polymorphic_on of the parent 

1948 # should be propagated here. If the col 

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

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

1951 # inheritance), we can use it 

1952 if mapper.polymorphic_on is not None: 

1953 if self.persist_selectable is mapper.persist_selectable: 

1954 self.polymorphic_on = mapper.polymorphic_on 

1955 else: 

1956 self.polymorphic_on = ( 

1957 self.persist_selectable 

1958 ).corresponding_column(mapper.polymorphic_on) 

1959 # we can use the parent mapper's _set_polymorphic_identity 

1960 # directly; it ensures the polymorphic_identity of the 

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

1962 if self.polymorphic_on is not None: 

1963 self._set_polymorphic_identity = ( 

1964 mapper._set_polymorphic_identity 

1965 ) 

1966 self._polymorphic_attr_key = ( 

1967 mapper._polymorphic_attr_key 

1968 ) 

1969 self._validate_polymorphic_identity = ( 

1970 mapper._validate_polymorphic_identity 

1971 ) 

1972 else: 

1973 self._set_polymorphic_identity = None 

1974 self._polymorphic_attr_key = None 

1975 return 

1976 

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

1978 raise sa_exc.InvalidRequestError( 

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

1980 "on a mapper hierarchy which includes the " 

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

1982 ) 

1983 

1984 if setter: 

1985 

1986 def _set_polymorphic_identity(state): 

1987 dict_ = state.dict 

1988 # TODO: what happens if polymorphic_on column attribute name 

1989 # does not match .key? 

1990 

1991 polymorphic_identity = ( 

1992 state.manager.mapper.polymorphic_identity 

1993 ) 

1994 if ( 

1995 polymorphic_identity is None 

1996 and state.manager.mapper.polymorphic_abstract 

1997 ): 

1998 raise sa_exc.InvalidRequestError( 

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

2000 "mapper is marked polymorphic_abstract=True" 

2001 ) 

2002 

2003 state.get_impl(polymorphic_key).set( 

2004 state, 

2005 dict_, 

2006 polymorphic_identity, 

2007 None, 

2008 ) 

2009 

2010 self._polymorphic_attr_key = polymorphic_key 

2011 

2012 def _validate_polymorphic_identity(mapper, state, dict_): 

2013 if ( 

2014 polymorphic_key in dict_ 

2015 and dict_[polymorphic_key] 

2016 not in mapper._acceptable_polymorphic_identities 

2017 ): 

2018 util.warn_limited( 

2019 "Flushing object %s with " 

2020 "incompatible polymorphic identity %r; the " 

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

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

2023 ) 

2024 

2025 self._set_polymorphic_identity = _set_polymorphic_identity 

2026 self._validate_polymorphic_identity = ( 

2027 _validate_polymorphic_identity 

2028 ) 

2029 else: 

2030 self._polymorphic_attr_key = None 

2031 self._set_polymorphic_identity = None 

2032 

2033 _validate_polymorphic_identity = None 

2034 

2035 @HasMemoized.memoized_attribute 

2036 def _version_id_prop(self): 

2037 if self.version_id_col is not None: 

2038 return self._columntoproperty[self.version_id_col] 

2039 else: 

2040 return None 

2041 

2042 @HasMemoized.memoized_attribute 

2043 def _acceptable_polymorphic_identities(self): 

2044 identities = set() 

2045 

2046 stack = deque([self]) 

2047 while stack: 

2048 item = stack.popleft() 

2049 if item.persist_selectable is self.persist_selectable: 

2050 identities.add(item.polymorphic_identity) 

2051 stack.extend(item._inheriting_mappers) 

2052 

2053 return identities 

2054 

2055 @HasMemoized.memoized_attribute 

2056 def _prop_set(self): 

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

2058 

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

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

2061 descriptor_props = util.preloaded.orm_descriptor_props 

2062 

2063 if not self.concrete: 

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

2065 elif key not in self._props: 

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

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

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

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

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

2071 # descriptors that might have side effects when invoked. 

2072 implementing_attribute = self.class_manager._get_class_attr_mro( 

2073 key, prop 

2074 ) 

2075 if implementing_attribute is prop or ( 

2076 isinstance( 

2077 implementing_attribute, attributes.InstrumentedAttribute 

2078 ) 

2079 and implementing_attribute._parententity is prop.parent 

2080 ): 

2081 self._configure_property( 

2082 key, 

2083 descriptor_props.ConcreteInheritedProperty(), 

2084 init=init, 

2085 setparent=True, 

2086 ) 

2087 

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

2089 def _configure_property( 

2090 self, 

2091 key: str, 

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

2093 *, 

2094 init: bool = True, 

2095 setparent: bool = True, 

2096 warn_for_existing: bool = False, 

2097 ) -> MapperProperty[Any]: 

2098 descriptor_props = util.preloaded.orm_descriptor_props 

2099 self._log( 

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

2101 ) 

2102 

2103 if not isinstance(prop_arg, MapperProperty): 

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

2105 key, prop_arg 

2106 ) 

2107 else: 

2108 prop = prop_arg 

2109 

2110 if isinstance(prop, properties.ColumnProperty): 

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

2112 

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

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

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

2116 if col is None and self.inherits: 

2117 path = [self] 

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

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

2120 if col is not None: 

2121 for m2 in path: 

2122 m2.persist_selectable._refresh_for_new_column(col) 

2123 col = self.persist_selectable.corresponding_column( 

2124 prop.columns[0] 

2125 ) 

2126 break 

2127 path.append(m) 

2128 

2129 # subquery expression, column not present in the mapped 

2130 # selectable. 

2131 if col is None: 

2132 col = prop.columns[0] 

2133 

2134 # column is coming in after _readonly_props was 

2135 # initialized; check for 'readonly' 

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

2137 not hasattr(col, "table") 

2138 or col.table not in self._cols_by_table 

2139 ): 

2140 self._readonly_props.add(prop) 

2141 

2142 else: 

2143 # if column is coming in after _cols_by_table was 

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

2145 if ( 

2146 hasattr(self, "_cols_by_table") 

2147 and col.table in self._cols_by_table 

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

2149 ): 

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

2151 

2152 # if this properties.ColumnProperty represents the "polymorphic 

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

2154 # columns in SELECT statements. 

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

2156 prop._is_polymorphic_discriminator = ( 

2157 col is self.polymorphic_on 

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

2159 ) 

2160 

2161 if isinstance(col, expression.Label): 

2162 # new in 1.4, get column property against expressions 

2163 # to be addressable in subqueries 

2164 col.key = col._tq_key_label = key 

2165 

2166 self.columns.add(col, key) 

2167 

2168 for col in prop.columns: 

2169 for proxy_col in col.proxy_set: 

2170 self._columntoproperty[proxy_col] = prop 

2171 

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

2173 util.warn( 

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

2175 "assigned to attribute " 

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

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

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

2179 ) 

2180 

2181 prop.key = key 

2182 

2183 if setparent: 

2184 prop.set_parent(self, init) 

2185 

2186 if key in self._props and getattr( 

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

2188 ): 

2189 syn = self._props[key]._mapped_by_synonym 

2190 raise sa_exc.ArgumentError( 

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

2192 "a ColumnProperty already exists keyed to the name " 

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

2194 ) 

2195 

2196 # replacement cases 

2197 

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

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

2200 if ( 

2201 key in self._props 

2202 and not isinstance( 

2203 self._props[key], descriptor_props.ConcreteInheritedProperty 

2204 ) 

2205 and not isinstance(prop, descriptor_props.SynonymProperty) 

2206 ): 

2207 if warn_for_existing: 

2208 util.warn_deprecated( 

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

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

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

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

2213 "release", 

2214 "2.0", 

2215 ) 

2216 oldprop = self._props[key] 

2217 self._path_registry.pop(oldprop, None) 

2218 

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

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

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

2222 # get replaced. 

2223 elif ( 

2224 warn_for_existing 

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

2226 and not isinstance(prop, descriptor_props.SynonymProperty) 

2227 and not isinstance( 

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

2229 descriptor_props.ConcreteInheritedProperty, 

2230 ) 

2231 ): 

2232 util.warn_deprecated( 

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

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

2235 "attribute of the same name. " 

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

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

2238 "release", 

2239 "2.0", 

2240 ) 

2241 

2242 self._props[key] = prop 

2243 

2244 if not self.non_primary: 

2245 prop.instrument_class(self) 

2246 

2247 for mapper in self._inheriting_mappers: 

2248 mapper._adapt_inherited_property(key, prop, init) 

2249 

2250 if init: 

2251 prop.init() 

2252 prop.post_instrument_class(self) 

2253 

2254 if self.configured: 

2255 self._expire_memoizations() 

2256 

2257 return prop 

2258 

2259 def _make_prop_from_column( 

2260 self, 

2261 key: str, 

2262 column: Union[ 

2263 Sequence[KeyedColumnElement[Any]], KeyedColumnElement[Any] 

2264 ], 

2265 ) -> ColumnProperty[Any]: 

2266 columns = util.to_list(column) 

2267 mapped_column = [] 

2268 for c in columns: 

2269 mc = self.persist_selectable.corresponding_column(c) 

2270 if mc is None: 

2271 mc = self.local_table.corresponding_column(c) 

2272 if mc is not None: 

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

2274 # mapped table, this corresponds to adding a 

2275 # column after the fact to the local table. 

2276 # [ticket:1523] 

2277 self.persist_selectable._refresh_for_new_column(mc) 

2278 mc = self.persist_selectable.corresponding_column(c) 

2279 if mc is None: 

2280 raise sa_exc.ArgumentError( 

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

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

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

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

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

2286 ) 

2287 mapped_column.append(mc) 

2288 return properties.ColumnProperty(*mapped_column) 

2289 

2290 def _reconcile_prop_with_incoming_columns( 

2291 self, 

2292 key: str, 

2293 existing_prop: MapperProperty[Any], 

2294 warn_only: bool, 

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

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

2297 ) -> ColumnProperty[Any]: 

2298 if incoming_prop and ( 

2299 self.concrete 

2300 or not isinstance(existing_prop, properties.ColumnProperty) 

2301 ): 

2302 return incoming_prop 

2303 

2304 existing_column = existing_prop.columns[0] 

2305 

2306 if incoming_prop and existing_column in incoming_prop.columns: 

2307 return incoming_prop 

2308 

2309 if incoming_prop is None: 

2310 assert single_column is not None 

2311 incoming_column = single_column 

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

2313 else: 

2314 assert single_column is None 

2315 incoming_column = incoming_prop.columns[0] 

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

2317 

2318 if ( 

2319 ( 

2320 not self._inherits_equated_pairs 

2321 or (equated_pair_key not in self._inherits_equated_pairs) 

2322 ) 

2323 and not existing_column.shares_lineage(incoming_column) 

2324 and existing_column is not self.version_id_col 

2325 and incoming_column is not self.version_id_col 

2326 ): 

2327 msg = ( 

2328 "Implicitly combining column %s with column " 

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

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

2331 "explicitly." 

2332 % ( 

2333 existing_prop.columns[-1], 

2334 incoming_column, 

2335 key, 

2336 ) 

2337 ) 

2338 if warn_only: 

2339 util.warn(msg) 

2340 else: 

2341 raise sa_exc.InvalidRequestError(msg) 

2342 

2343 # existing properties.ColumnProperty from an inheriting 

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

2345 # breakpoint() 

2346 new_prop = existing_prop.copy() 

2347 

2348 new_prop.columns.insert(0, incoming_column) 

2349 self._log( 

2350 "inserting column to existing list " 

2351 "in properties.ColumnProperty %s", 

2352 key, 

2353 ) 

2354 return new_prop # type: ignore 

2355 

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

2357 def _property_from_column( 

2358 self, 

2359 key: str, 

2360 column: KeyedColumnElement[Any], 

2361 ) -> ColumnProperty[Any]: 

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

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

2364 

2365 descriptor_props = util.preloaded.orm_descriptor_props 

2366 

2367 prop = self._props.get(key) 

2368 

2369 if isinstance(prop, properties.ColumnProperty): 

2370 return self._reconcile_prop_with_incoming_columns( 

2371 key, 

2372 prop, 

2373 single_column=column, 

2374 warn_only=prop.parent is not self, 

2375 ) 

2376 elif prop is None or isinstance( 

2377 prop, descriptor_props.ConcreteInheritedProperty 

2378 ): 

2379 return self._make_prop_from_column(key, column) 

2380 else: 

2381 raise sa_exc.ArgumentError( 

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

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

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

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

2386 "to remove all awareness of the column entirely " 

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

2388 "use the 'include_properties' or 'exclude_properties' " 

2389 "mapper arguments to control specifically which table " 

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

2391 ) 

2392 

2393 @util.langhelpers.tag_method_for_warnings( 

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

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

2396 "operation.", 

2397 sa_exc.SAWarning, 

2398 ) 

2399 def _check_configure(self) -> None: 

2400 if self.registry._new_mappers: 

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

2402 

2403 def _post_configure_properties(self) -> None: 

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

2405 attached to this mapper. 

2406 

2407 This is a deferred configuration step which is intended 

2408 to execute once all mappers have been constructed. 

2409 

2410 """ 

2411 

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

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

2414 for key, prop in l: 

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

2416 

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

2418 prop.init() 

2419 

2420 if prop._configure_finished: 

2421 prop.post_instrument_class(self) 

2422 

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

2424 self.configured = True 

2425 

2426 def add_properties(self, dict_of_properties): 

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

2428 using `add_property`. 

2429 

2430 """ 

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

2432 self.add_property(key, value) 

2433 

2434 def add_property( 

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

2436 ) -> None: 

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

2438 

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

2440 property to the initial properties dictionary sent to the 

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

2442 the given MapperProperty is configured immediately. 

2443 

2444 """ 

2445 prop = self._configure_property( 

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

2447 ) 

2448 assert isinstance(prop, MapperProperty) 

2449 self._init_properties[key] = prop 

2450 

2451 def _expire_memoizations(self) -> None: 

2452 for mapper in self.iterate_to_root(): 

2453 mapper._reset_memoizations() 

2454 

2455 @property 

2456 def _log_desc(self) -> str: 

2457 return ( 

2458 "(" 

2459 + self.class_.__name__ 

2460 + "|" 

2461 + ( 

2462 self.local_table is not None 

2463 and self.local_table.description 

2464 or str(self.local_table) 

2465 ) 

2466 + (self.non_primary and "|non-primary" or "") 

2467 + ")" 

2468 ) 

2469 

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

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

2472 

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

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

2475 

2476 def __repr__(self) -> str: 

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

2478 

2479 def __str__(self) -> str: 

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

2481 self.class_.__name__, 

2482 self.non_primary and " (non-primary)" or "", 

2483 ( 

2484 self.local_table.description 

2485 if self.local_table is not None 

2486 else self.persist_selectable.description 

2487 ), 

2488 ) 

2489 

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

2491 orphan_possible = False 

2492 for mapper in self.iterate_to_root(): 

2493 for key, cls in mapper._delete_orphans: 

2494 orphan_possible = True 

2495 

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

2497 state, key, optimistic=state.has_identity 

2498 ) 

2499 

2500 if self.legacy_is_orphan and has_parent: 

2501 return False 

2502 elif not self.legacy_is_orphan and not has_parent: 

2503 return True 

2504 

2505 if self.legacy_is_orphan: 

2506 return orphan_possible 

2507 else: 

2508 return False 

2509 

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

2511 return key in self._props 

2512 

2513 def get_property( 

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

2515 ) -> MapperProperty[Any]: 

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

2517 

2518 if _configure_mappers: 

2519 self._check_configure() 

2520 

2521 try: 

2522 return self._props[key] 

2523 except KeyError as err: 

2524 raise sa_exc.InvalidRequestError( 

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

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

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

2528 ) from err 

2529 

2530 def get_property_by_column( 

2531 self, column: ColumnElement[_T] 

2532 ) -> MapperProperty[_T]: 

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

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

2535 

2536 return self._columntoproperty[column] 

2537 

2538 @property 

2539 def iterate_properties(self): 

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

2541 

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

2543 

2544 def _mappers_from_spec( 

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

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

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

2548 represents. 

2549 

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

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

2552 

2553 """ 

2554 if spec == "*": 

2555 mappers = list(self.self_and_descendants) 

2556 elif spec: 

2557 mapper_set = set() 

2558 for m in util.to_list(spec): 

2559 m = _class_to_mapper(m) 

2560 if not m.isa(self): 

2561 raise sa_exc.InvalidRequestError( 

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

2563 ) 

2564 

2565 if selectable is None: 

2566 mapper_set.update(m.iterate_to_root()) 

2567 else: 

2568 mapper_set.add(m) 

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

2570 else: 

2571 mappers = [] 

2572 

2573 if selectable is not None: 

2574 tables = set( 

2575 sql_util.find_tables(selectable, include_aliases=True) 

2576 ) 

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

2578 return mappers 

2579 

2580 def _selectable_from_mappers( 

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

2582 ) -> FromClause: 

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

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

2585 mapped tables. 

2586 

2587 """ 

2588 from_obj = self.persist_selectable 

2589 for m in mappers: 

2590 if m is self: 

2591 continue 

2592 if m.concrete: 

2593 raise sa_exc.InvalidRequestError( 

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

2595 "when concrete-inheriting mappers are used." 

2596 ) 

2597 elif not m.single: 

2598 if innerjoin: 

2599 from_obj = from_obj.join( 

2600 m.local_table, m.inherit_condition 

2601 ) 

2602 else: 

2603 from_obj = from_obj.outerjoin( 

2604 m.local_table, m.inherit_condition 

2605 ) 

2606 

2607 return from_obj 

2608 

2609 @HasMemoized.memoized_attribute 

2610 def _version_id_has_server_side_value(self) -> bool: 

2611 vid_col = self.version_id_col 

2612 

2613 if vid_col is None: 

2614 return False 

2615 

2616 elif not isinstance(vid_col, Column): 

2617 return True 

2618 else: 

2619 return vid_col.server_default is not None or ( 

2620 vid_col.default is not None 

2621 and ( 

2622 not vid_col.default.is_scalar 

2623 and not vid_col.default.is_callable 

2624 ) 

2625 ) 

2626 

2627 @HasMemoized.memoized_attribute 

2628 def _single_table_criterion(self): 

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

2630 return self.polymorphic_on._annotate( 

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

2632 ).in_( 

2633 [ 

2634 m.polymorphic_identity 

2635 for m in self.self_and_descendants 

2636 if not m.polymorphic_abstract 

2637 ] 

2638 ) 

2639 else: 

2640 return None 

2641 

2642 @HasMemoized.memoized_attribute 

2643 def _has_aliased_polymorphic_fromclause(self): 

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

2645 like a subquery. 

2646 

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

2648 if this is present. 

2649 

2650 """ 

2651 return self.with_polymorphic and isinstance( 

2652 self.with_polymorphic[1], 

2653 expression.AliasedReturnsRows, 

2654 ) 

2655 

2656 @HasMemoized.memoized_attribute 

2657 def _should_select_with_poly_adapter(self): 

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

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

2660 rows for mapped classes and subclasses against this Mapper. 

2661 

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

2663 for this condition. 

2664 

2665 """ 

2666 

2667 # this has been simplified as of #8456. 

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

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

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

2671 # 

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

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

2674 # flattened JOIN for with_polymorphic.) 

2675 # 

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

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

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

2679 # some kind or polymorphic_union. 

2680 # 

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

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

2683 # on it (such as test_join_from_polymorphic_explicit_aliased_three). 

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

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

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

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

2688 # legacy case we should probably disable. 

2689 # 

2690 # 

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

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

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

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

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

2696 # polymorphic base class. 

2697 # 

2698 return ( 

2699 self._has_aliased_polymorphic_fromclause 

2700 or self._requires_row_aliasing 

2701 or (self.base_mapper._has_aliased_polymorphic_fromclause) 

2702 or self.base_mapper._requires_row_aliasing 

2703 ) 

2704 

2705 @HasMemoized.memoized_attribute 

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

2707 self._check_configure() 

2708 

2709 if not self.with_polymorphic: 

2710 return [] 

2711 return self._mappers_from_spec(*self.with_polymorphic) 

2712 

2713 @HasMemoized.memoized_attribute 

2714 def _post_inspect(self): 

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

2716 

2717 E.g. when Query calls: 

2718 

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

2720 

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

2722 

2723 """ 

2724 self._check_configure() 

2725 

2726 @HasMemoized_ro_memoized_attribute 

2727 def _with_polymorphic_selectable(self) -> FromClause: 

2728 if not self.with_polymorphic: 

2729 return self.persist_selectable 

2730 

2731 spec, selectable = self.with_polymorphic 

2732 if selectable is not None: 

2733 return selectable 

2734 else: 

2735 return self._selectable_from_mappers( 

2736 self._mappers_from_spec(spec, selectable), False 

2737 ) 

2738 

2739 with_polymorphic_mappers = _with_polymorphic_mappers 

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

2741 default "polymorphic" query. 

2742 

2743 """ 

2744 

2745 @HasMemoized_ro_memoized_attribute 

2746 def _insert_cols_evaluating_none(self): 

2747 return { 

2748 table: frozenset( 

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

2750 ) 

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

2752 } 

2753 

2754 @HasMemoized.memoized_attribute 

2755 def _insert_cols_as_none(self): 

2756 return { 

2757 table: frozenset( 

2758 col.key 

2759 for col in columns 

2760 if not col.primary_key 

2761 and not col.server_default 

2762 and not col.default 

2763 and not col.type.should_evaluate_none 

2764 ) 

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

2766 } 

2767 

2768 @HasMemoized.memoized_attribute 

2769 def _propkey_to_col(self): 

2770 return { 

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

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

2773 } 

2774 

2775 @HasMemoized.memoized_attribute 

2776 def _pk_keys_by_table(self): 

2777 return { 

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

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

2780 } 

2781 

2782 @HasMemoized.memoized_attribute 

2783 def _pk_attr_keys_by_table(self): 

2784 return { 

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

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

2787 } 

2788 

2789 @HasMemoized.memoized_attribute 

2790 def _server_default_cols( 

2791 self, 

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

2793 return { 

2794 table: frozenset( 

2795 [ 

2796 col 

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

2798 if col.server_default is not None 

2799 or ( 

2800 col.default is not None 

2801 and col.default.is_clause_element 

2802 ) 

2803 ] 

2804 ) 

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

2806 } 

2807 

2808 @HasMemoized.memoized_attribute 

2809 def _server_onupdate_default_cols( 

2810 self, 

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

2812 return { 

2813 table: frozenset( 

2814 [ 

2815 col 

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

2817 if col.server_onupdate is not None 

2818 or ( 

2819 col.onupdate is not None 

2820 and col.onupdate.is_clause_element 

2821 ) 

2822 ] 

2823 ) 

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

2825 } 

2826 

2827 @HasMemoized.memoized_attribute 

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

2829 return { 

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

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

2832 } 

2833 

2834 @HasMemoized.memoized_attribute 

2835 def _server_onupdate_default_col_keys( 

2836 self, 

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

2838 return { 

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

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

2841 } 

2842 

2843 @HasMemoized.memoized_attribute 

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

2845 result: Set[str] = set() 

2846 

2847 col_to_property = self._columntoproperty 

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

2849 result.update( 

2850 col_to_property[col].key 

2851 for col in columns.intersection(col_to_property) 

2852 ) 

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

2854 result.update( 

2855 col_to_property[col].key 

2856 for col in columns.intersection(col_to_property) 

2857 ) 

2858 return result 

2859 

2860 @HasMemoized.memoized_instancemethod 

2861 def __clause_element__(self): 

2862 annotations: Dict[str, Any] = { 

2863 "entity_namespace": self, 

2864 "parententity": self, 

2865 "parentmapper": self, 

2866 } 

2867 if self.persist_selectable is not self.local_table: 

2868 # joined table inheritance, with polymorphic selectable, 

2869 # etc. 

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

2871 { 

2872 "entity_namespace": self, 

2873 "parententity": self, 

2874 "parentmapper": self, 

2875 } 

2876 )._set_propagate_attrs( 

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

2878 ) 

2879 

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

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

2882 ) 

2883 

2884 @util.memoized_property 

2885 def select_identity_token(self): 

2886 return ( 

2887 expression.null() 

2888 ._annotate( 

2889 { 

2890 "entity_namespace": self, 

2891 "parententity": self, 

2892 "parentmapper": self, 

2893 "identity_token": True, 

2894 } 

2895 ) 

2896 ._set_propagate_attrs( 

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

2898 ) 

2899 ) 

2900 

2901 @property 

2902 def selectable(self) -> FromClause: 

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

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

2905 

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

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

2908 full "polymorphic" selectable is returned. 

2909 

2910 """ 

2911 return self._with_polymorphic_selectable 

2912 

2913 def _with_polymorphic_args( 

2914 self, 

2915 spec: Any = None, 

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

2917 innerjoin: bool = False, 

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

2919 if selectable not in (None, False): 

2920 selectable = coercions.expect( 

2921 roles.StrictFromClauseRole, selectable, allow_select=True 

2922 ) 

2923 

2924 if self.with_polymorphic: 

2925 if not spec: 

2926 spec = self.with_polymorphic[0] 

2927 if selectable is False: 

2928 selectable = self.with_polymorphic[1] 

2929 elif selectable is False: 

2930 selectable = None 

2931 mappers = self._mappers_from_spec(spec, selectable) 

2932 if selectable is not None: 

2933 return mappers, selectable 

2934 else: 

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

2936 

2937 @HasMemoized.memoized_attribute 

2938 def _polymorphic_properties(self): 

2939 return list( 

2940 self._iterate_polymorphic_properties( 

2941 self._with_polymorphic_mappers 

2942 ) 

2943 ) 

2944 

2945 @property 

2946 def _all_column_expressions(self): 

2947 poly_properties = self._polymorphic_properties 

2948 adapter = self._polymorphic_adapter 

2949 

2950 return [ 

2951 adapter.columns[c] if adapter else c 

2952 for prop in poly_properties 

2953 if isinstance(prop, properties.ColumnProperty) 

2954 and prop._renders_in_subqueries 

2955 for c in prop.columns 

2956 ] 

2957 

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

2959 if polymorphic_mappers: 

2960 poly_properties = self._iterate_polymorphic_properties( 

2961 polymorphic_mappers 

2962 ) 

2963 else: 

2964 poly_properties = self._polymorphic_properties 

2965 

2966 return [ 

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

2968 for prop in poly_properties 

2969 if isinstance(prop, properties.ColumnProperty) 

2970 ] 

2971 

2972 @HasMemoized.memoized_attribute 

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

2974 if self._has_aliased_polymorphic_fromclause: 

2975 return orm_util.ORMAdapter( 

2976 orm_util._TraceAdaptRole.MAPPER_POLYMORPHIC_ADAPTER, 

2977 self, 

2978 selectable=self.selectable, 

2979 equivalents=self._equivalent_columns, 

2980 limit_on_entity=False, 

2981 ) 

2982 else: 

2983 return None 

2984 

2985 def _iterate_polymorphic_properties(self, mappers=None): 

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

2987 a SELECT.""" 

2988 if mappers is None: 

2989 mappers = self._with_polymorphic_mappers 

2990 

2991 if not mappers: 

2992 for c in self.iterate_properties: 

2993 yield c 

2994 else: 

2995 # in the polymorphic case, filter out discriminator columns 

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

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

2998 for c in util.unique_list( 

2999 chain( 

3000 *[ 

3001 list(mapper.iterate_properties) 

3002 for mapper in [self] + mappers 

3003 ] 

3004 ) 

3005 ): 

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

3007 self.polymorphic_on is None 

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

3009 ): 

3010 continue 

3011 yield c 

3012 

3013 @HasMemoized.memoized_attribute 

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

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

3016 associated this mapper. 

3017 

3018 This is an object that provides each property based on 

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

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

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

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

3023 column. The namespace object can also be iterated, 

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

3025 

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

3027 of this attribute which limit the types of properties 

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

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

3030 

3031 .. warning:: 

3032 

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

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

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

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

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

3038 accessing attributes dynamically, favor using the dict-access 

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

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

3041 

3042 .. seealso:: 

3043 

3044 :attr:`_orm.Mapper.all_orm_descriptors` 

3045 

3046 """ 

3047 

3048 self._check_configure() 

3049 return util.ReadOnlyProperties(self._props) 

3050 

3051 @HasMemoized.memoized_attribute 

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

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

3054 with the mapped class. 

3055 

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

3057 associated with the mapped class or its superclasses. 

3058 

3059 This namespace includes attributes that are mapped to the class 

3060 as well as attributes declared by extension modules. 

3061 It includes any Python descriptor type that inherits from 

3062 :class:`.InspectionAttr`. This includes 

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

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

3065 :class:`.AssociationProxy`. 

3066 

3067 To distinguish between mapped attributes and extension attributes, 

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

3069 to a constant that distinguishes between different extension types. 

3070 

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

3072 

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

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

3075 

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

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

3078 3 below. In Python 3.6 and above this ordering will be the 

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

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

3081 or the mapper. 

3082 

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

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

3085 class in which it first appeared. 

3086 

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

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

3089 

3090 .. versionchanged:: 1.3.19 ensured deterministic ordering for 

3091 :meth:`_orm.Mapper.all_orm_descriptors`. 

3092 

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

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

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

3096 referring to the collection of mapped properties via 

3097 :attr:`_orm.Mapper.attrs`. 

3098 

3099 .. warning:: 

3100 

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

3102 accessor namespace is an 

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

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

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

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

3107 accessing attributes dynamically, favor using the dict-access 

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

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

3110 collisions. 

3111 

3112 .. seealso:: 

3113 

3114 :attr:`_orm.Mapper.attrs` 

3115 

3116 """ 

3117 return util.ReadOnlyProperties( 

3118 dict(self.class_manager._all_sqla_attributes()) 

3119 ) 

3120 

3121 @HasMemoized.memoized_attribute 

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

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

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

3125 all synonyms that refer to primary key columns 

3126 

3127 """ 

3128 descriptor_props = util.preloaded.orm_descriptor_props 

3129 

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

3131 

3132 return { 

3133 syn.key: syn.name 

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

3135 if isinstance(syn, descriptor_props.SynonymProperty) 

3136 and syn.name in pk_keys 

3137 } 

3138 

3139 @HasMemoized.memoized_attribute 

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

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

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

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

3144 

3145 .. seealso:: 

3146 

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

3148 :class:`.MapperProperty` 

3149 objects. 

3150 

3151 """ 

3152 descriptor_props = util.preloaded.orm_descriptor_props 

3153 

3154 return self._filter_properties(descriptor_props.SynonymProperty) 

3155 

3156 @property 

3157 def entity_namespace(self): 

3158 return self.class_ 

3159 

3160 @HasMemoized.memoized_attribute 

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

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

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

3164 

3165 .. seealso:: 

3166 

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

3168 :class:`.MapperProperty` 

3169 objects. 

3170 

3171 """ 

3172 return self._filter_properties(properties.ColumnProperty) 

3173 

3174 @HasMemoized.memoized_attribute 

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

3176 def relationships( 

3177 self, 

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

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

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

3181 

3182 .. warning:: 

3183 

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

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

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

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

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

3189 accessing attributes dynamically, favor using the dict-access 

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

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

3192 collisions. 

3193 

3194 .. seealso:: 

3195 

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

3197 :class:`.MapperProperty` 

3198 objects. 

3199 

3200 """ 

3201 return self._filter_properties( 

3202 util.preloaded.orm_relationships.RelationshipProperty 

3203 ) 

3204 

3205 @HasMemoized.memoized_attribute 

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

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

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

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

3210 

3211 .. seealso:: 

3212 

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

3214 :class:`.MapperProperty` 

3215 objects. 

3216 

3217 """ 

3218 return self._filter_properties( 

3219 util.preloaded.orm_descriptor_props.CompositeProperty 

3220 ) 

3221 

3222 def _filter_properties( 

3223 self, type_: Type[_MP] 

3224 ) -> util.ReadOnlyProperties[_MP]: 

3225 self._check_configure() 

3226 return util.ReadOnlyProperties( 

3227 util.OrderedDict( 

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

3229 ) 

3230 ) 

3231 

3232 @HasMemoized.memoized_attribute 

3233 def _get_clause(self): 

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

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

3236 by primary key. 

3237 

3238 """ 

3239 params = [ 

3240 ( 

3241 primary_key, 

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

3243 ) 

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

3245 ] 

3246 return ( 

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

3248 util.column_dict(params), 

3249 ) 

3250 

3251 @HasMemoized.memoized_attribute 

3252 def _equivalent_columns(self) -> _EquivalentColumnMap: 

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

3254 the determination of column pairs that are equated to 

3255 one another based on inherit condition. This is designed 

3256 to work with the queries that util.polymorphic_union 

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

3258 the base table directly (including the subclass table columns 

3259 only). 

3260 

3261 The resulting structure is a dictionary of columns mapped 

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

3263 

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

3265 

3266 """ # noqa: E501 

3267 result: _EquivalentColumnMap = {} 

3268 

3269 def visit_binary(binary): 

3270 if binary.operator == operators.eq: 

3271 if binary.left in result: 

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

3273 else: 

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

3275 if binary.right in result: 

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

3277 else: 

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

3279 

3280 for mapper in self.base_mapper.self_and_descendants: 

3281 if mapper.inherit_condition is not None: 

3282 visitors.traverse( 

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

3284 ) 

3285 

3286 return result 

3287 

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

3289 if isinstance( 

3290 obj, 

3291 ( 

3292 _MappedAttribute, 

3293 instrumentation.ClassManager, 

3294 expression.ColumnElement, 

3295 ), 

3296 ): 

3297 return False 

3298 else: 

3299 return assigned_name not in self._dataclass_fields 

3300 

3301 @HasMemoized.memoized_attribute 

3302 def _dataclass_fields(self): 

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

3304 

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

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

3307 present on the class. 

3308 

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

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

3311 

3312 """ 

3313 

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

3315 return True 

3316 

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

3318 # either local or from an inherited class 

3319 # ignore dataclass field default values 

3320 if local: 

3321 if self.class_.__dict__.get( 

3322 assigned_name, None 

3323 ) is not None and self._is_userland_descriptor( 

3324 assigned_name, self.class_.__dict__[assigned_name] 

3325 ): 

3326 return True 

3327 else: 

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

3329 if attr is not None and self._is_userland_descriptor( 

3330 assigned_name, attr 

3331 ): 

3332 return True 

3333 

3334 if ( 

3335 self.include_properties is not None 

3336 and name not in self.include_properties 

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

3338 ): 

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

3340 return True 

3341 

3342 if self.exclude_properties is not None and ( 

3343 name in self.exclude_properties 

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

3345 ): 

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

3347 return True 

3348 

3349 return False 

3350 

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

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

3353 common inherited parent as this mapper.""" 

3354 

3355 return self.base_mapper is other.base_mapper 

3356 

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

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

3359 one. common parent but different branch 

3360 

3361 """ 

3362 return ( 

3363 self.base_mapper is other.base_mapper 

3364 and not self.isa(other) 

3365 and not other.isa(self) 

3366 ) 

3367 

3368 def _canload( 

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

3370 ) -> bool: 

3371 s = self.primary_mapper() 

3372 if self.polymorphic_on is not None or allow_subtypes: 

3373 return _state_mapper(state).isa(s) 

3374 else: 

3375 return _state_mapper(state) is s 

3376 

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

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

3379 

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

3381 while m and m is not other: 

3382 m = m.inherits 

3383 return bool(m) 

3384 

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

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

3387 while m: 

3388 yield m 

3389 m = m.inherits 

3390 

3391 @HasMemoized.memoized_attribute 

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

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

3394 

3395 This includes not just the immediately inheriting mappers but 

3396 all their inheriting mappers as well. 

3397 

3398 """ 

3399 descendants = [] 

3400 stack = deque([self]) 

3401 while stack: 

3402 item = stack.popleft() 

3403 descendants.append(item) 

3404 stack.extend(item._inheriting_mappers) 

3405 return util.WeakSequence(descendants) 

3406 

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

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

3409 all descendant mappers. 

3410 

3411 This includes not just the immediately inheriting mappers but 

3412 all their inheriting mappers as well. 

3413 

3414 To iterate through an entire hierarchy, use 

3415 ``mapper.base_mapper.polymorphic_iterator()``. 

3416 

3417 """ 

3418 return iter(self.self_and_descendants) 

3419 

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

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

3422 (class).""" 

3423 

3424 return self.class_manager.mapper 

3425 

3426 @property 

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

3428 return self.class_manager.mapper.base_mapper 

3429 

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

3431 pk_cols: Sequence[ColumnClause[Any]] = self.primary_key 

3432 if adapter: 

3433 pk_cols = [adapter.columns[c] for c in pk_cols] 

3434 rk = result.keys() 

3435 for col in pk_cols: 

3436 if col not in rk: 

3437 return False 

3438 else: 

3439 return True 

3440 

3441 def identity_key_from_row( 

3442 self, 

3443 row: Union[Row[Any], RowMapping], 

3444 identity_token: Optional[Any] = None, 

3445 adapter: Optional[ORMAdapter] = None, 

3446 ) -> _IdentityKeyType[_O]: 

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

3448 item from the identity map. 

3449 

3450 :param row: A :class:`.Row` or :class:`.RowMapping` produced from a 

3451 result set that selected from the ORM mapped primary key columns. 

3452 

3453 .. versionchanged:: 2.0 

3454 :class:`.Row` or :class:`.RowMapping` are accepted 

3455 for the "row" argument 

3456 

3457 """ 

3458 pk_cols: Sequence[ColumnClause[Any]] = self.primary_key 

3459 if adapter: 

3460 pk_cols = [adapter.columns[c] for c in pk_cols] 

3461 

3462 mapping: RowMapping 

3463 if hasattr(row, "_mapping"): 

3464 mapping = row._mapping 

3465 else: 

3466 mapping = row # type: ignore[assignment] 

3467 

3468 return ( 

3469 self._identity_class, 

3470 tuple(mapping[column] for column in pk_cols), 

3471 identity_token, 

3472 ) 

3473 

3474 def identity_key_from_primary_key( 

3475 self, 

3476 primary_key: Tuple[Any, ...], 

3477 identity_token: Optional[Any] = None, 

3478 ) -> _IdentityKeyType[_O]: 

3479 """Return an identity-map key for use in storing/retrieving an 

3480 item from an identity map. 

3481 

3482 :param primary_key: A list of values indicating the identifier. 

3483 

3484 """ 

3485 return ( 

3486 self._identity_class, 

3487 tuple(primary_key), 

3488 identity_token, 

3489 ) 

3490 

3491 def identity_key_from_instance(self, instance: _O) -> _IdentityKeyType[_O]: 

3492 """Return the identity key for the given instance, based on 

3493 its primary key attributes. 

3494 

3495 If the instance's state is expired, calling this method 

3496 will result in a database check to see if the object has been deleted. 

3497 If the row no longer exists, 

3498 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3499 

3500 This value is typically also found on the instance state under the 

3501 attribute name `key`. 

3502 

3503 """ 

3504 state = attributes.instance_state(instance) 

3505 return self._identity_key_from_state(state, PassiveFlag.PASSIVE_OFF) 

3506 

3507 def _identity_key_from_state( 

3508 self, 

3509 state: InstanceState[_O], 

3510 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3511 ) -> _IdentityKeyType[_O]: 

3512 dict_ = state.dict 

3513 manager = state.manager 

3514 return ( 

3515 self._identity_class, 

3516 tuple( 

3517 [ 

3518 manager[prop.key].impl.get(state, dict_, passive) 

3519 for prop in self._identity_key_props 

3520 ] 

3521 ), 

3522 state.identity_token, 

3523 ) 

3524 

3525 def primary_key_from_instance(self, instance: _O) -> Tuple[Any, ...]: 

3526 """Return the list of primary key values for the given 

3527 instance. 

3528 

3529 If the instance's state is expired, calling this method 

3530 will result in a database check to see if the object has been deleted. 

3531 If the row no longer exists, 

3532 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3533 

3534 """ 

3535 state = attributes.instance_state(instance) 

3536 identity_key = self._identity_key_from_state( 

3537 state, PassiveFlag.PASSIVE_OFF 

3538 ) 

3539 return identity_key[1] 

3540 

3541 @HasMemoized.memoized_attribute 

3542 def _persistent_sortkey_fn(self): 

3543 key_fns = [col.type.sort_key_function for col in self.primary_key] 

3544 

3545 if set(key_fns).difference([None]): 

3546 

3547 def key(state): 

3548 return tuple( 

3549 key_fn(val) if key_fn is not None else val 

3550 for key_fn, val in zip(key_fns, state.key[1]) 

3551 ) 

3552 

3553 else: 

3554 

3555 def key(state): 

3556 return state.key[1] 

3557 

3558 return key 

3559 

3560 @HasMemoized.memoized_attribute 

3561 def _identity_key_props(self): 

3562 return [self._columntoproperty[col] for col in self.primary_key] 

3563 

3564 @HasMemoized.memoized_attribute 

3565 def _all_pk_cols(self): 

3566 collection: Set[ColumnClause[Any]] = set() 

3567 for table in self.tables: 

3568 collection.update(self._pks_by_table[table]) 

3569 return collection 

3570 

3571 @HasMemoized.memoized_attribute 

3572 def _should_undefer_in_wildcard(self): 

3573 cols: Set[ColumnElement[Any]] = set(self.primary_key) 

3574 if self.polymorphic_on is not None: 

3575 cols.add(self.polymorphic_on) 

3576 return cols 

3577 

3578 @HasMemoized.memoized_attribute 

3579 def _primary_key_propkeys(self): 

3580 return {self._columntoproperty[col].key for col in self._all_pk_cols} 

3581 

3582 def _get_state_attr_by_column( 

3583 self, 

3584 state: InstanceState[_O], 

3585 dict_: _InstanceDict, 

3586 column: ColumnElement[Any], 

3587 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3588 ) -> Any: 

3589 prop = self._columntoproperty[column] 

3590 return state.manager[prop.key].impl.get(state, dict_, passive=passive) 

3591 

3592 def _set_committed_state_attr_by_column(self, state, dict_, column, value): 

3593 prop = self._columntoproperty[column] 

3594 state.manager[prop.key].impl.set_committed_value(state, dict_, value) 

3595 

3596 def _set_state_attr_by_column(self, state, dict_, column, value): 

3597 prop = self._columntoproperty[column] 

3598 state.manager[prop.key].impl.set(state, dict_, value, None) 

3599 

3600 def _get_committed_attr_by_column(self, obj, column): 

3601 state = attributes.instance_state(obj) 

3602 dict_ = attributes.instance_dict(obj) 

3603 return self._get_committed_state_attr_by_column( 

3604 state, dict_, column, passive=PassiveFlag.PASSIVE_OFF 

3605 ) 

3606 

3607 def _get_committed_state_attr_by_column( 

3608 self, state, dict_, column, passive=PassiveFlag.PASSIVE_RETURN_NO_VALUE 

3609 ): 

3610 prop = self._columntoproperty[column] 

3611 return state.manager[prop.key].impl.get_committed_value( 

3612 state, dict_, passive=passive 

3613 ) 

3614 

3615 def _optimized_get_statement(self, state, attribute_names): 

3616 """assemble a WHERE clause which retrieves a given state by primary 

3617 key, using a minimized set of tables. 

3618 

3619 Applies to a joined-table inheritance mapper where the 

3620 requested attribute names are only present on joined tables, 

3621 not the base table. The WHERE clause attempts to include 

3622 only those tables to minimize joins. 

3623 

3624 """ 

3625 props = self._props 

3626 

3627 col_attribute_names = set(attribute_names).intersection( 

3628 state.mapper.column_attrs.keys() 

3629 ) 

3630 tables: Set[FromClause] = set( 

3631 chain( 

3632 *[ 

3633 sql_util.find_tables(c, check_columns=True) 

3634 for key in col_attribute_names 

3635 for c in props[key].columns 

3636 ] 

3637 ) 

3638 ) 

3639 

3640 if self.base_mapper.local_table in tables: 

3641 return None 

3642 

3643 def visit_binary(binary): 

3644 leftcol = binary.left 

3645 rightcol = binary.right 

3646 if leftcol is None or rightcol is None: 

3647 return 

3648 

3649 if leftcol.table not in tables: 

3650 leftval = self._get_committed_state_attr_by_column( 

3651 state, 

3652 state.dict, 

3653 leftcol, 

3654 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3655 ) 

3656 if leftval in orm_util._none_set: 

3657 raise _OptGetColumnsNotAvailable() 

3658 binary.left = sql.bindparam( 

3659 None, leftval, type_=binary.right.type 

3660 ) 

3661 elif rightcol.table not in tables: 

3662 rightval = self._get_committed_state_attr_by_column( 

3663 state, 

3664 state.dict, 

3665 rightcol, 

3666 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3667 ) 

3668 if rightval in orm_util._none_set: 

3669 raise _OptGetColumnsNotAvailable() 

3670 binary.right = sql.bindparam( 

3671 None, rightval, type_=binary.right.type 

3672 ) 

3673 

3674 allconds: List[ColumnElement[bool]] = [] 

3675 

3676 start = False 

3677 

3678 # as of #7507, from the lowest base table on upwards, 

3679 # we include all intermediary tables. 

3680 

3681 for mapper in reversed(list(self.iterate_to_root())): 

3682 if mapper.local_table in tables: 

3683 start = True 

3684 elif not isinstance(mapper.local_table, expression.TableClause): 

3685 return None 

3686 if start and not mapper.single: 

3687 assert mapper.inherits 

3688 assert not mapper.concrete 

3689 assert mapper.inherit_condition is not None 

3690 allconds.append(mapper.inherit_condition) 

3691 tables.add(mapper.local_table) 

3692 

3693 # only the bottom table needs its criteria to be altered to fit 

3694 # the primary key ident - the rest of the tables upwards to the 

3695 # descendant-most class should all be present and joined to each 

3696 # other. 

3697 try: 

3698 _traversed = visitors.cloned_traverse( 

3699 allconds[0], {}, {"binary": visit_binary} 

3700 ) 

3701 except _OptGetColumnsNotAvailable: 

3702 return None 

3703 else: 

3704 allconds[0] = _traversed 

3705 

3706 cond = sql.and_(*allconds) 

3707 

3708 cols = [] 

3709 for key in col_attribute_names: 

3710 cols.extend(props[key].columns) 

3711 return ( 

3712 sql.select(*cols) 

3713 .where(cond) 

3714 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 

3715 ) 

3716 

3717 def _iterate_to_target_viawpoly(self, mapper): 

3718 if self.isa(mapper): 

3719 prev = self 

3720 for m in self.iterate_to_root(): 

3721 yield m 

3722 

3723 if m is not prev and prev not in m._with_polymorphic_mappers: 

3724 break 

3725 

3726 prev = m 

3727 if m is mapper: 

3728 break 

3729 

3730 @HasMemoized.memoized_attribute 

3731 def _would_selectinload_combinations_cache(self): 

3732 return {} 

3733 

3734 def _would_selectin_load_only_from_given_mapper(self, super_mapper): 

3735 """return True if this mapper would "selectin" polymorphic load based 

3736 on the given super mapper, and not from a setting from a subclass. 

3737 

3738 given:: 

3739 

3740 class A: ... 

3741 

3742 

3743 class B(A): 

3744 __mapper_args__ = {"polymorphic_load": "selectin"} 

3745 

3746 

3747 class C(B): ... 

3748 

3749 

3750 class D(B): 

3751 __mapper_args__ = {"polymorphic_load": "selectin"} 

3752 

3753 ``inspect(C)._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3754 returns True, because C does selectin loading because of B's setting. 

3755 

3756 OTOH, ``inspect(D) 

3757 ._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3758 returns False, because D does selectin loading because of its own 

3759 setting; when we are doing a selectin poly load from B, we want to 

3760 filter out D because it would already have its own selectin poly load 

3761 set up separately. 

3762 

3763 Added as part of #9373. 

3764 

3765 """ 

3766 cache = self._would_selectinload_combinations_cache 

3767 

3768 try: 

3769 return cache[super_mapper] 

3770 except KeyError: 

3771 pass 

3772 

3773 # assert that given object is a supermapper, meaning we already 

3774 # strong reference it directly or indirectly. this allows us 

3775 # to not worry that we are creating new strongrefs to unrelated 

3776 # mappers or other objects. 

3777 assert self.isa(super_mapper) 

3778 

3779 mapper = super_mapper 

3780 for m in self._iterate_to_target_viawpoly(mapper): 

3781 if m.polymorphic_load == "selectin": 

3782 retval = m is super_mapper 

3783 break 

3784 else: 

3785 retval = False 

3786 

3787 cache[super_mapper] = retval 

3788 return retval 

3789 

3790 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 

3791 if not enabled_via_opt: 

3792 # common case, takes place for all polymorphic loads 

3793 mapper = polymorphic_from 

3794 for m in self._iterate_to_target_viawpoly(mapper): 

3795 if m.polymorphic_load == "selectin": 

3796 return m 

3797 else: 

3798 # uncommon case, selectin load options were used 

3799 enabled_via_opt = set(enabled_via_opt) 

3800 enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 

3801 for entity in enabled_via_opt.union([polymorphic_from]): 

3802 mapper = entity.mapper 

3803 for m in self._iterate_to_target_viawpoly(mapper): 

3804 if ( 

3805 m.polymorphic_load == "selectin" 

3806 or m in enabled_via_opt_mappers 

3807 ): 

3808 return enabled_via_opt_mappers.get(m, m) 

3809 

3810 return None 

3811 

3812 @util.preload_module("sqlalchemy.orm.strategy_options") 

3813 def _subclass_load_via_in(self, entity, polymorphic_from): 

3814 """Assemble a that can load the columns local to 

3815 this subclass as a SELECT with IN. 

3816 

3817 """ 

3818 

3819 strategy_options = util.preloaded.orm_strategy_options 

3820 

3821 assert self.inherits 

3822 

3823 if self.polymorphic_on is not None: 

3824 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 

3825 keep_props = set([polymorphic_prop] + self._identity_key_props) 

3826 else: 

3827 keep_props = set(self._identity_key_props) 

3828 

3829 disable_opt = strategy_options.Load(entity) 

3830 enable_opt = strategy_options.Load(entity) 

3831 

3832 classes_to_include = {self} 

3833 m: Optional[Mapper[Any]] = self.inherits 

3834 while ( 

3835 m is not None 

3836 and m is not polymorphic_from 

3837 and m.polymorphic_load == "selectin" 

3838 ): 

3839 classes_to_include.add(m) 

3840 m = m.inherits 

3841 

3842 for prop in self.column_attrs + self.relationships: 

3843 # skip prop keys that are not instrumented on the mapped class. 

3844 # this is primarily the "_sa_polymorphic_on" property that gets 

3845 # created for an ad-hoc polymorphic_on SQL expression, issue #8704 

3846 if prop.key not in self.class_manager: 

3847 continue 

3848 

3849 if prop.parent in classes_to_include or prop in keep_props: 

3850 # "enable" options, to turn on the properties that we want to 

3851 # load by default (subject to options from the query) 

3852 if not isinstance(prop, StrategizedProperty): 

3853 continue 

3854 

3855 enable_opt = enable_opt._set_generic_strategy( 

3856 # convert string name to an attribute before passing 

3857 # to loader strategy. note this must be in terms 

3858 # of given entity, such as AliasedClass, etc. 

3859 (getattr(entity.entity_namespace, prop.key),), 

3860 dict(prop.strategy_key), 

3861 _reconcile_to_other=True, 

3862 ) 

3863 else: 

3864 # "disable" options, to turn off the properties from the 

3865 # superclass that we *don't* want to load, applied after 

3866 # the options from the query to override them 

3867 disable_opt = disable_opt._set_generic_strategy( 

3868 # convert string name to an attribute before passing 

3869 # to loader strategy. note this must be in terms 

3870 # of given entity, such as AliasedClass, etc. 

3871 (getattr(entity.entity_namespace, prop.key),), 

3872 {"do_nothing": True}, 

3873 _reconcile_to_other=False, 

3874 ) 

3875 

3876 primary_key = [ 

3877 sql_util._deep_annotate(pk, {"_orm_adapt": True}) 

3878 for pk in self.primary_key 

3879 ] 

3880 

3881 in_expr: ColumnElement[Any] 

3882 

3883 if len(primary_key) > 1: 

3884 in_expr = sql.tuple_(*primary_key) 

3885 else: 

3886 in_expr = primary_key[0] 

3887 

3888 if entity.is_aliased_class: 

3889 assert entity.mapper is self 

3890 

3891 q = sql.select(entity).set_label_style( 

3892 LABEL_STYLE_TABLENAME_PLUS_COL 

3893 ) 

3894 

3895 in_expr = entity._adapter.traverse(in_expr) 

3896 primary_key = [entity._adapter.traverse(k) for k in primary_key] 

3897 q = q.where( 

3898 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3899 ).order_by(*primary_key) 

3900 else: 

3901 q = sql.select(self).set_label_style( 

3902 LABEL_STYLE_TABLENAME_PLUS_COL 

3903 ) 

3904 q = q.where( 

3905 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3906 ).order_by(*primary_key) 

3907 

3908 return q, enable_opt, disable_opt 

3909 

3910 @HasMemoized.memoized_attribute 

3911 def _subclass_load_via_in_mapper(self): 

3912 # the default is loading this mapper against the basemost mapper 

3913 return self._subclass_load_via_in(self, self.base_mapper) 

3914 

3915 def cascade_iterator( 

3916 self, 

3917 type_: str, 

3918 state: InstanceState[_O], 

3919 halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None, 

3920 ) -> Iterator[ 

3921 Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict] 

3922 ]: 

3923 r"""Iterate each element and its mapper in an object graph, 

3924 for all relationships that meet the given cascade rule. 

3925 

3926 :param type\_: 

3927 The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 

3928 etc.). 

3929 

3930 .. note:: the ``"all"`` cascade is not accepted here. For a generic 

3931 object traversal function, see :ref:`faq_walk_objects`. 

3932 

3933 :param state: 

3934 The lead InstanceState. child items will be processed per 

3935 the relationships defined for this object's mapper. 

3936 

3937 :return: the method yields individual object instances. 

3938 

3939 .. seealso:: 

3940 

3941 :ref:`unitofwork_cascades` 

3942 

3943 :ref:`faq_walk_objects` - illustrates a generic function to 

3944 traverse all objects without relying on cascades. 

3945 

3946 """ 

3947 visited_states: Set[InstanceState[Any]] = set() 

3948 prp, mpp = object(), object() 

3949 

3950 assert state.mapper.isa(self) 

3951 

3952 # this is actually a recursive structure, fully typing it seems 

3953 # a little too difficult for what it's worth here 

3954 visitables: Deque[ 

3955 Tuple[ 

3956 Deque[Any], 

3957 object, 

3958 Optional[InstanceState[Any]], 

3959 Optional[_InstanceDict], 

3960 ] 

3961 ] 

3962 

3963 visitables = deque( 

3964 [(deque(state.mapper._props.values()), prp, state, state.dict)] 

3965 ) 

3966 

3967 while visitables: 

3968 iterator, item_type, parent_state, parent_dict = visitables[-1] 

3969 if not iterator: 

3970 visitables.pop() 

3971 continue 

3972 

3973 if item_type is prp: 

3974 prop = iterator.popleft() 

3975 if not prop.cascade or type_ not in prop.cascade: 

3976 continue 

3977 assert parent_state is not None 

3978 assert parent_dict is not None 

3979 queue = deque( 

3980 prop.cascade_iterator( 

3981 type_, 

3982 parent_state, 

3983 parent_dict, 

3984 visited_states, 

3985 halt_on, 

3986 ) 

3987 ) 

3988 if queue: 

3989 visitables.append((queue, mpp, None, None)) 

3990 elif item_type is mpp: 

3991 ( 

3992 instance, 

3993 instance_mapper, 

3994 corresponding_state, 

3995 corresponding_dict, 

3996 ) = iterator.popleft() 

3997 yield ( 

3998 instance, 

3999 instance_mapper, 

4000 corresponding_state, 

4001 corresponding_dict, 

4002 ) 

4003 visitables.append( 

4004 ( 

4005 deque(instance_mapper._props.values()), 

4006 prp, 

4007 corresponding_state, 

4008 corresponding_dict, 

4009 ) 

4010 ) 

4011 

4012 @HasMemoized.memoized_attribute 

4013 def _compiled_cache(self): 

4014 return util.LRUCache(self._compiled_cache_size) 

4015 

4016 @HasMemoized.memoized_attribute 

4017 def _multiple_persistence_tables(self): 

4018 return len(self.tables) > 1 

4019 

4020 @HasMemoized.memoized_attribute 

4021 def _sorted_tables(self): 

4022 table_to_mapper: Dict[TableClause, Mapper[Any]] = {} 

4023 

4024 for mapper in self.base_mapper.self_and_descendants: 

4025 for t in mapper.tables: 

4026 table_to_mapper.setdefault(t, mapper) 

4027 

4028 extra_dependencies = [] 

4029 for table, mapper in table_to_mapper.items(): 

4030 super_ = mapper.inherits 

4031 if super_: 

4032 extra_dependencies.extend( 

4033 [(super_table, table) for super_table in super_.tables] 

4034 ) 

4035 

4036 def skip(fk): 

4037 # attempt to skip dependencies that are not 

4038 # significant to the inheritance chain 

4039 # for two tables that are related by inheritance. 

4040 # while that dependency may be important, it's technically 

4041 # not what we mean to sort on here. 

4042 parent = table_to_mapper.get(fk.parent.table) 

4043 dep = table_to_mapper.get(fk.column.table) 

4044 if ( 

4045 parent is not None 

4046 and dep is not None 

4047 and dep is not parent 

4048 and dep.inherit_condition is not None 

4049 ): 

4050 cols = set(sql_util._find_columns(dep.inherit_condition)) 

4051 if parent.inherit_condition is not None: 

4052 cols = cols.union( 

4053 sql_util._find_columns(parent.inherit_condition) 

4054 ) 

4055 return fk.parent not in cols and fk.column not in cols 

4056 else: 

4057 return fk.parent not in cols 

4058 return False 

4059 

4060 sorted_ = sql_util.sort_tables( 

4061 table_to_mapper, 

4062 skip_fn=skip, 

4063 extra_dependencies=extra_dependencies, 

4064 ) 

4065 

4066 ret = util.OrderedDict() 

4067 for t in sorted_: 

4068 ret[t] = table_to_mapper[t] 

4069 return ret 

4070 

4071 def _memo(self, key: Any, callable_: Callable[[], _T]) -> _T: 

4072 if key in self._memoized_values: 

4073 return cast(_T, self._memoized_values[key]) 

4074 else: 

4075 self._memoized_values[key] = value = callable_() 

4076 return value 

4077 

4078 @util.memoized_property 

4079 def _table_to_equated(self): 

4080 """memoized map of tables to collections of columns to be 

4081 synchronized upwards to the base mapper.""" 

4082 

4083 result: util.defaultdict[ 

4084 Table, 

4085 List[ 

4086 Tuple[ 

4087 Mapper[Any], 

4088 List[Tuple[ColumnElement[Any], ColumnElement[Any]]], 

4089 ] 

4090 ], 

4091 ] = util.defaultdict(list) 

4092 

4093 def set_union(x, y): 

4094 return x.union(y) 

4095 

4096 for table in self._sorted_tables: 

4097 cols = set(table.c) 

4098 

4099 for m in self.iterate_to_root(): 

4100 if m._inherits_equated_pairs and cols.intersection( 

4101 reduce( 

4102 set_union, 

4103 [l.proxy_set for l, r in m._inherits_equated_pairs], 

4104 ) 

4105 ): 

4106 result[table].append((m, m._inherits_equated_pairs)) 

4107 

4108 return result 

4109 

4110 

4111class _OptGetColumnsNotAvailable(Exception): 

4112 pass 

4113 

4114 

4115def configure_mappers() -> None: 

4116 """Initialize the inter-mapper relationships of all mappers that 

4117 have been constructed thus far across all :class:`_orm.registry` 

4118 collections. 

4119 

4120 The configure step is used to reconcile and initialize the 

4121 :func:`_orm.relationship` linkages between mapped classes, as well as to 

4122 invoke configuration events such as the 

4123 :meth:`_orm.MapperEvents.before_configured` and 

4124 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM 

4125 extensions or user-defined extension hooks. 

4126 

4127 Mapper configuration is normally invoked automatically, the first time 

4128 mappings from a particular :class:`_orm.registry` are used, as well as 

4129 whenever mappings are used and additional not-yet-configured mappers have 

4130 been constructed. The automatic configuration process however is local only 

4131 to the :class:`_orm.registry` involving the target mapper and any related 

4132 :class:`_orm.registry` objects which it may depend on; this is 

4133 equivalent to invoking the :meth:`_orm.registry.configure` method 

4134 on a particular :class:`_orm.registry`. 

4135 

4136 By contrast, the :func:`_orm.configure_mappers` function will invoke the 

4137 configuration process on all :class:`_orm.registry` objects that 

4138 exist in memory, and may be useful for scenarios where many individual 

4139 :class:`_orm.registry` objects that are nonetheless interrelated are 

4140 in use. 

4141 

4142 .. versionchanged:: 1.4 

4143 

4144 As of SQLAlchemy 1.4.0b2, this function works on a 

4145 per-:class:`_orm.registry` basis, locating all :class:`_orm.registry` 

4146 objects present and invoking the :meth:`_orm.registry.configure` method 

4147 on each. The :meth:`_orm.registry.configure` method may be preferred to 

4148 limit the configuration of mappers to those local to a particular 

4149 :class:`_orm.registry` and/or declarative base class. 

4150 

4151 Points at which automatic configuration is invoked include when a mapped 

4152 class is instantiated into an instance, as well as when ORM queries 

4153 are emitted using :meth:`.Session.query` or :meth:`_orm.Session.execute` 

4154 with an ORM-enabled statement. 

4155 

4156 The mapper configure process, whether invoked by 

4157 :func:`_orm.configure_mappers` or from :meth:`_orm.registry.configure`, 

4158 provides several event hooks that can be used to augment the mapper 

4159 configuration step. These hooks include: 

4160 

4161 * :meth:`.MapperEvents.before_configured` - called once before 

4162 :func:`.configure_mappers` or :meth:`_orm.registry.configure` does any 

4163 work; this can be used to establish additional options, properties, or 

4164 related mappings before the operation proceeds. 

4165 

4166 * :meth:`.MapperEvents.mapper_configured` - called as each individual 

4167 :class:`_orm.Mapper` is configured within the process; will include all 

4168 mapper state except for backrefs set up by other mappers that are still 

4169 to be configured. 

4170 

4171 * :meth:`.MapperEvents.after_configured` - called once after 

4172 :func:`.configure_mappers` or :meth:`_orm.registry.configure` is 

4173 complete; at this stage, all :class:`_orm.Mapper` objects that fall 

4174 within the scope of the configuration operation will be fully configured. 

4175 Note that the calling application may still have other mappings that 

4176 haven't been produced yet, such as if they are in modules as yet 

4177 unimported, and may also have mappings that are still to be configured, 

4178 if they are in other :class:`_orm.registry` collections not part of the 

4179 current scope of configuration. 

4180 

4181 """ 

4182 

4183 _configure_registries(_all_registries(), cascade=True) 

4184 

4185 

4186def _configure_registries( 

4187 registries: Set[_RegistryType], cascade: bool 

4188) -> None: 

4189 for reg in registries: 

4190 if reg._new_mappers: 

4191 break 

4192 else: 

4193 return 

4194 

4195 with _CONFIGURE_MUTEX: 

4196 global _already_compiling 

4197 if _already_compiling: 

4198 return 

4199 _already_compiling = True 

4200 try: 

4201 # double-check inside mutex 

4202 for reg in registries: 

4203 if reg._new_mappers: 

4204 break 

4205 else: 

4206 return 

4207 

4208 Mapper.dispatch._for_class(Mapper).before_configured() # type: ignore # noqa: E501 

4209 # initialize properties on all mappers 

4210 # note that _mapper_registry is unordered, which 

4211 # may randomly conceal/reveal issues related to 

4212 # the order of mapper compilation 

4213 

4214 _do_configure_registries(registries, cascade) 

4215 finally: 

4216 _already_compiling = False 

4217 Mapper.dispatch._for_class(Mapper).after_configured() # type: ignore 

4218 

4219 

4220@util.preload_module("sqlalchemy.orm.decl_api") 

4221def _do_configure_registries( 

4222 registries: Set[_RegistryType], cascade: bool 

4223) -> None: 

4224 registry = util.preloaded.orm_decl_api.registry 

4225 

4226 orig = set(registries) 

4227 

4228 for reg in registry._recurse_with_dependencies(registries): 

4229 has_skip = False 

4230 

4231 for mapper in reg._mappers_to_configure(): 

4232 run_configure = None 

4233 

4234 for fn in mapper.dispatch.before_mapper_configured: 

4235 run_configure = fn(mapper, mapper.class_) 

4236 if run_configure is EXT_SKIP: 

4237 has_skip = True 

4238 break 

4239 if run_configure is EXT_SKIP: 

4240 continue 

4241 

4242 if getattr(mapper, "_configure_failed", False): 

4243 e = sa_exc.InvalidRequestError( 

4244 "One or more mappers failed to initialize - " 

4245 "can't proceed with initialization of other " 

4246 "mappers. Triggering mapper: '%s'. " 

4247 "Original exception was: %s" 

4248 % (mapper, mapper._configure_failed) 

4249 ) 

4250 e._configure_failed = mapper._configure_failed # type: ignore 

4251 raise e 

4252 

4253 if not mapper.configured: 

4254 try: 

4255 mapper._post_configure_properties() 

4256 mapper._expire_memoizations() 

4257 mapper.dispatch.mapper_configured(mapper, mapper.class_) 

4258 except Exception: 

4259 exc = sys.exc_info()[1] 

4260 if not hasattr(exc, "_configure_failed"): 

4261 mapper._configure_failed = exc 

4262 raise 

4263 if not has_skip: 

4264 reg._new_mappers = False 

4265 

4266 if not cascade and reg._dependencies.difference(orig): 

4267 raise sa_exc.InvalidRequestError( 

4268 "configure was called with cascade=False but " 

4269 "additional registries remain" 

4270 ) 

4271 

4272 

4273@util.preload_module("sqlalchemy.orm.decl_api") 

4274def _dispose_registries(registries: Set[_RegistryType], cascade: bool) -> None: 

4275 registry = util.preloaded.orm_decl_api.registry 

4276 

4277 orig = set(registries) 

4278 

4279 for reg in registry._recurse_with_dependents(registries): 

4280 if not cascade and reg._dependents.difference(orig): 

4281 raise sa_exc.InvalidRequestError( 

4282 "Registry has dependent registries that are not disposed; " 

4283 "pass cascade=True to clear these also" 

4284 ) 

4285 

4286 while reg._managers: 

4287 try: 

4288 manager, _ = reg._managers.popitem() 

4289 except KeyError: 

4290 # guard against race between while and popitem 

4291 pass 

4292 else: 

4293 reg._dispose_manager_and_mapper(manager) 

4294 

4295 reg._non_primary_mappers.clear() 

4296 reg._dependents.clear() 

4297 for dep in reg._dependencies: 

4298 dep._dependents.discard(reg) 

4299 reg._dependencies.clear() 

4300 # this wasn't done in the 1.3 clear_mappers() and in fact it 

4301 # was a bug, as it could cause configure_mappers() to invoke 

4302 # the "before_configured" event even though mappers had all been 

4303 # disposed. 

4304 reg._new_mappers = False 

4305 

4306 

4307def reconstructor(fn: _Fn) -> _Fn: 

4308 """Decorate a method as the 'reconstructor' hook. 

4309 

4310 Designates a single method as the "reconstructor", an ``__init__``-like 

4311 method that will be called by the ORM after the instance has been 

4312 loaded from the database or otherwise reconstituted. 

4313 

4314 .. tip:: 

4315 

4316 The :func:`_orm.reconstructor` decorator makes use of the 

4317 :meth:`_orm.InstanceEvents.load` event hook, which can be 

4318 used directly. 

4319 

4320 The reconstructor will be invoked with no arguments. Scalar 

4321 (non-collection) database-mapped attributes of the instance will 

4322 be available for use within the function. Eagerly-loaded 

4323 collections are generally not yet available and will usually only 

4324 contain the first element. ORM state changes made to objects at 

4325 this stage will not be recorded for the next flush() operation, so 

4326 the activity within a reconstructor should be conservative. 

4327 

4328 .. seealso:: 

4329 

4330 :meth:`.InstanceEvents.load` 

4331 

4332 """ 

4333 fn.__sa_reconstructor__ = True # type: ignore[attr-defined] 

4334 return fn 

4335 

4336 

4337def validates( 

4338 *names: str, include_removes: bool = False, include_backrefs: bool = True 

4339) -> Callable[[_Fn], _Fn]: 

4340 r"""Decorate a method as a 'validator' for one or more named properties. 

4341 

4342 Designates a method as a validator, a method which receives the 

4343 name of the attribute as well as a value to be assigned, or in the 

4344 case of a collection, the value to be added to the collection. 

4345 The function can then raise validation exceptions to halt the 

4346 process from continuing (where Python's built-in ``ValueError`` 

4347 and ``AssertionError`` exceptions are reasonable choices), or can 

4348 modify or replace the value before proceeding. The function should 

4349 otherwise return the given value. 

4350 

4351 Note that a validator for a collection **cannot** issue a load of that 

4352 collection within the validation routine - this usage raises 

4353 an assertion to avoid recursion overflows. This is a reentrant 

4354 condition which is not supported. 

4355 

4356 :param \*names: list of attribute names to be validated. 

4357 :param include_removes: if True, "remove" events will be 

4358 sent as well - the validation function must accept an additional 

4359 argument "is_remove" which will be a boolean. 

4360 

4361 :param include_backrefs: defaults to ``True``; if ``False``, the 

4362 validation function will not emit if the originator is an attribute 

4363 event related via a backref. This can be used for bi-directional 

4364 :func:`.validates` usage where only one validator should emit per 

4365 attribute operation. 

4366 

4367 .. versionchanged:: 2.0.16 This paramter inadvertently defaulted to 

4368 ``False`` for releases 2.0.0 through 2.0.15. Its correct default 

4369 of ``True`` is restored in 2.0.16. 

4370 

4371 .. seealso:: 

4372 

4373 :ref:`simple_validators` - usage examples for :func:`.validates` 

4374 

4375 """ 

4376 

4377 def wrap(fn: _Fn) -> _Fn: 

4378 fn.__sa_validators__ = names # type: ignore[attr-defined] 

4379 fn.__sa_validation_opts__ = { # type: ignore[attr-defined] 

4380 "include_removes": include_removes, 

4381 "include_backrefs": include_backrefs, 

4382 } 

4383 return fn 

4384 

4385 return wrap 

4386 

4387 

4388def _event_on_load(state, ctx): 

4389 instrumenting_mapper = state.manager.mapper 

4390 

4391 if instrumenting_mapper._reconstructor: 

4392 instrumenting_mapper._reconstructor(state.obj()) 

4393 

4394 

4395def _event_on_init(state, args, kwargs): 

4396 """Run init_instance hooks. 

4397 

4398 This also includes mapper compilation, normally not needed 

4399 here but helps with some piecemeal configuration 

4400 scenarios (such as in the ORM tutorial). 

4401 

4402 """ 

4403 

4404 instrumenting_mapper = state.manager.mapper 

4405 if instrumenting_mapper: 

4406 instrumenting_mapper._check_configure() 

4407 if instrumenting_mapper._set_polymorphic_identity: 

4408 instrumenting_mapper._set_polymorphic_identity(state) 

4409 

4410 

4411class _ColumnMapping(Dict["ColumnElement[Any]", "MapperProperty[Any]"]): 

4412 """Error reporting helper for mapper._columntoproperty.""" 

4413 

4414 __slots__ = ("mapper",) 

4415 

4416 def __init__(self, mapper): 

4417 # TODO: weakref would be a good idea here 

4418 self.mapper = mapper 

4419 

4420 def __missing__(self, column): 

4421 prop = self.mapper._props.get(column) 

4422 if prop: 

4423 raise orm_exc.UnmappedColumnError( 

4424 "Column '%s.%s' is not available, due to " 

4425 "conflicting property '%s':%r" 

4426 % (column.table.name, column.name, column.key, prop) 

4427 ) 

4428 raise orm_exc.UnmappedColumnError( 

4429 "No column %s is configured on mapper %s..." 

4430 % (column, self.mapper) 

4431 )