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

1429 statements  

1# orm/mapper.py 

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

3# <see AUTHORS file> 

4# 

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

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

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

8 

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

10 

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

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

13 

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

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

16 

17""" 

18 

19from __future__ import annotations 

20 

21from collections import deque 

22from functools import reduce 

23from itertools import chain 

24import sys 

25import threading 

26from typing import Any 

27from typing import Callable 

28from typing import cast 

29from typing import Collection 

30from typing import Deque 

31from typing import Dict 

32from typing import FrozenSet 

33from typing import Generic 

34from typing import Iterable 

35from typing import Iterator 

36from typing import List 

37from typing import Mapping 

38from typing import Optional 

39from typing import Sequence 

40from typing import Set 

41from typing import Tuple 

42from typing import Type 

43from typing import TYPE_CHECKING 

44from typing import TypeVar 

45from typing import Union 

46import weakref 

47 

48from . import attributes 

49from . import exc as orm_exc 

50from . import instrumentation 

51from . import loading 

52from . import properties 

53from . import util as orm_util 

54from ._typing import _O 

55from .base import _class_to_mapper 

56from .base import _parse_mapper_argument 

57from .base import _state_mapper 

58from .base import PassiveFlag 

59from .base import state_str 

60from .interfaces import _MappedAttribute 

61from .interfaces import EXT_SKIP 

62from .interfaces import InspectionAttr 

63from .interfaces import MapperProperty 

64from .interfaces import ORMEntityColumnsClauseRole 

65from .interfaces import ORMFromClauseRole 

66from .interfaces import StrategizedProperty 

67from .path_registry import PathRegistry 

68from .. import event 

69from .. import exc as sa_exc 

70from .. import inspection 

71from .. import log 

72from .. import schema 

73from .. import sql 

74from .. import util 

75from ..event import dispatcher 

76from ..event import EventTarget 

77from ..sql import base as sql_base 

78from ..sql import coercions 

79from ..sql import expression 

80from ..sql import operators 

81from ..sql import roles 

82from ..sql import TableClause 

83from ..sql import util as sql_util 

84from ..sql import visitors 

85from ..sql.cache_key import MemoizedHasCacheKey 

86from ..sql.elements import KeyedColumnElement 

87from ..sql.schema import Column 

88from ..sql.schema import Table 

89from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

90from ..util import HasMemoized 

91from ..util import HasMemoized_ro_memoized_attribute 

92from ..util.typing import Literal 

93 

94if TYPE_CHECKING: 

95 from ._typing import _IdentityKeyType 

96 from ._typing import _InstanceDict 

97 from ._typing import _ORMColumnExprArgument 

98 from ._typing import _RegistryType 

99 from .decl_api import registry 

100 from .dependency import DependencyProcessor 

101 from .descriptor_props import CompositeProperty 

102 from .descriptor_props import SynonymProperty 

103 from .events import MapperEvents 

104 from .instrumentation import ClassManager 

105 from .path_registry import CachingEntityRegistry 

106 from .properties import ColumnProperty 

107 from .relationships import RelationshipProperty 

108 from .state import InstanceState 

109 from .util import ORMAdapter 

110 from ..engine import Row 

111 from ..engine import RowMapping 

112 from ..sql._typing import _ColumnExpressionArgument 

113 from ..sql._typing import _EquivalentColumnMap 

114 from ..sql.base import ReadOnlyColumnCollection 

115 from ..sql.elements import ColumnClause 

116 from ..sql.elements import ColumnElement 

117 from ..sql.selectable import FromClause 

118 from ..util import OrderedSet 

119 

120 

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

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

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

124 

125 

126_WithPolymorphicArg = Union[ 

127 Literal["*"], 

128 Tuple[ 

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

130 Optional["FromClause"], 

131 ], 

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

133] 

134 

135 

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

137 weakref.WeakKeyDictionary() 

138) 

139 

140 

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

142 with _CONFIGURE_MUTEX: 

143 return set(_mapper_registries) 

144 

145 

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

147 for reg in _all_registries(): 

148 yield from reg._mappers_to_configure() 

149 

150 

151_already_compiling = False 

152 

153 

154# a constant returned by _get_attr_by_column to indicate 

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

156# column 

157NO_ATTRIBUTE = util.symbol("NO_ATTRIBUTE") 

158 

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

160_CONFIGURE_MUTEX = threading.RLock() 

161 

162 

163@inspection._self_inspects 

164@log.class_logger 

165class Mapper( 

166 ORMFromClauseRole, 

167 ORMEntityColumnsClauseRole[_O], 

168 MemoizedHasCacheKey, 

169 InspectionAttr, 

170 log.Identified, 

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

172 EventTarget, 

173 Generic[_O], 

174): 

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

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

177 proceed. 

178 

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

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

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

182 :ref:`orm_mapping_classes_toplevel`. 

183 

184 """ 

185 

186 dispatch: dispatcher[Mapper[_O]] 

187 

188 _dispose_called = False 

189 _configure_failed: Any = False 

190 _ready_for_configure = False 

191 

192 @util.deprecated_params( 

193 non_primary=( 

194 "1.3", 

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

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

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

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

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

200 ), 

201 ) 

202 def __init__( 

203 self, 

204 class_: Type[_O], 

205 local_table: Optional[FromClause] = None, 

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

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

208 non_primary: bool = False, 

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

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

211 inherit_foreign_keys: Optional[ 

212 Sequence[_ORMColumnExprArgument[Any]] 

213 ] = None, 

214 always_refresh: bool = False, 

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

216 version_id_generator: Optional[ 

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

218 ] = None, 

219 polymorphic_on: Optional[ 

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

221 ] = None, 

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

223 polymorphic_identity: Optional[Any] = None, 

224 concrete: bool = False, 

225 with_polymorphic: Optional[_WithPolymorphicArg] = None, 

226 polymorphic_abstract: bool = False, 

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

228 allow_partial_pks: bool = True, 

229 batch: bool = True, 

230 column_prefix: Optional[str] = None, 

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

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

233 passive_updates: bool = True, 

234 passive_deletes: bool = False, 

235 confirm_deleted_rows: bool = True, 

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

237 legacy_is_orphan: bool = False, 

238 _compiled_cache_size: int = 100, 

239 ): 

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

241 

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

243 is normally invoked through the 

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

245 :ref:`Declarative <orm_declarative_mapping>` or 

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

247 

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

249 removed; for a classical mapping configuration, use the 

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

251 

252 Parameters documented below may be passed to either the 

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

254 ``__mapper_args__`` declarative class attribute described at 

255 :ref:`orm_declarative_mapper_options`. 

256 

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

258 this argument is automatically passed as the declared class 

259 itself. 

260 

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

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

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

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

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

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

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

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

269 present. 

270 

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

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

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

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

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

276 inheritance scheme which uses 

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

278 

279 .. versionadded:: 2.0 

280 

281 .. seealso:: 

282 

283 :ref:`orm_inheritance_abstract_poly` 

284 

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

286 class will overwrite all data within object instances that already 

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

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

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

290 :meth:`_query.Query.populate_existing`. 

291 

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

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

294 possibly existing within the database. This affects whether a 

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

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

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

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

299 

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

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

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

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

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

305 

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

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

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

309 has partial NULL values will not be emitted. 

310 

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

312 of multiple entities can be batched together for efficiency. 

313 Setting to False indicates 

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

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

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

317 in between individual row persistence operations. 

318 

319 :param column_prefix: A string which will be prepended 

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

321 objects are automatically assigned as attributes to the 

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

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

324 dictionary. 

325 

326 This parameter is typically useful with imperative mappings 

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

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

329 ``user_id``, ``user_name``, and ``password``:: 

330 

331 class User(Base): 

332 __table__ = user_table 

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

334 

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

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

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

338 

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

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

341 approach to automating a naming scheme is to intercept the 

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

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

344 pattern. 

345 

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

347 table inheritance with its parent mapper. 

348 

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

350 

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

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

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

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

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

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

357 exception in a future release. 

358 

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

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

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

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

363 are needed immediately before the flush completes. 

364 

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

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

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

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

369 

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

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

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

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

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

375 attributes are not to be accessed in any case. 

376 

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

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

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

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

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

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

383 defaults will not be fetched. 

384 

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

386 :paramref:`.Mapper.eager_defaults` 

387 

388 .. seealso:: 

389 

390 :ref:`orm_server_defaults` 

391 

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

393 INSERTed at once using the 

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

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

396 feature to be very performant on supporting backends. 

397 

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

399 be excluded from mapping. 

400 

401 .. seealso:: 

402 

403 :ref:`include_exclude_cols` 

404 

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

406 names to map. 

407 

408 .. seealso:: 

409 

410 :ref:`include_exclude_cols` 

411 

412 :param inherits: A mapped class or the corresponding 

413 :class:`_orm.Mapper` 

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

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

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

417 is passed automatically as a result of the natural class 

418 hierarchy of the declared classes. 

419 

420 .. seealso:: 

421 

422 :ref:`inheritance_toplevel` 

423 

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

425 expression which will 

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

427 between the two tables. 

428 

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

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

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

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

433 

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

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

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

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

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

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

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

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

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

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

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

445 orphan object has been flushed yet or not. 

446 

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

448 for more detail on this change. 

449 

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

451 is in addition 

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

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

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

455 only. 

456 

457 .. seealso:: 

458 

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

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

461 

462 :param passive_deletes: Indicates DELETE behavior of foreign key 

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

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

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

466 on the superclass mapper. 

467 

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

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

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

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

472 superclass table, and not this table. 

473 

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

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

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

477 validate these attributes; note that the primary key columns 

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

479 the object as a whole. 

480 

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

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

483 to specify passive_deletes without this taking effect for 

484 all subclass mappers. 

485 

486 .. seealso:: 

487 

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

489 used with :func:`_orm.relationship` 

490 

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

492 CASCADE for joined-table inheritance mappers 

493 

494 :param passive_updates: Indicates UPDATE behavior of foreign key 

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

496 inheritance mapping. Defaults to ``True``. 

497 

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

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

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

501 on joined-table rows. 

502 

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

504 referential integrity and will not be issuing its own CASCADE 

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

506 emit an UPDATE statement for the dependent columns during a 

507 primary key change. 

508 

509 .. seealso:: 

510 

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

512 used with :func:`_orm.relationship` 

513 

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

515 CASCADE for joined-table inheritance mappers 

516 

517 :param polymorphic_load: Specifies "polymorphic loading" behavior 

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

519 table inheritance only). Valid values are: 

520 

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

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

523 in a SELECT query against the base. 

524 

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

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

527 the columns specific to this subclass. The SELECT uses 

528 IN to fetch multiple subclasses at once. 

529 

530 .. versionadded:: 1.2 

531 

532 .. seealso:: 

533 

534 :ref:`with_polymorphic_mapper_config` 

535 

536 :ref:`polymorphic_selectin` 

537 

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

539 SQL expression used to determine the target class for an 

540 incoming row, when inheriting classes are present. 

541 

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

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

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

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

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

547 

548 class Employee(Base): 

549 __tablename__ = "employee" 

550 

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

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

553 

554 __mapper_args__ = { 

555 "polymorphic_on": discriminator, 

556 "polymorphic_identity": "employee", 

557 } 

558 

559 It may also be specified 

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

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

562 approach:: 

563 

564 class Employee(Base): 

565 __tablename__ = "employee" 

566 

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

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

569 

570 __mapper_args__ = { 

571 "polymorphic_on": case( 

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

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

574 else_="employee", 

575 ), 

576 "polymorphic_identity": "employee", 

577 } 

578 

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

580 which is of particular use when using annotated column 

581 configurations:: 

582 

583 class Employee(Base): 

584 __tablename__ = "employee" 

585 

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

587 discriminator: Mapped[str] 

588 

589 __mapper_args__ = { 

590 "polymorphic_on": "discriminator", 

591 "polymorphic_identity": "employee", 

592 } 

593 

594 When setting ``polymorphic_on`` to reference an 

595 attribute or expression that's not present in the 

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

597 of the discriminator should be persisted to the database, 

598 the value of the 

599 discriminator is not automatically set on new 

600 instances; this must be handled by the user, 

601 either through manual means or via event listeners. 

602 A typical approach to establishing such a listener 

603 looks like:: 

604 

605 from sqlalchemy import event 

606 from sqlalchemy.orm import object_mapper 

607 

608 

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

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

611 mapper = object_mapper(instance) 

612 instance.discriminator = mapper.polymorphic_identity 

613 

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

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

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

617 in the database. 

618 

619 .. warning:: 

620 

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

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

623 columns are not yet supported. 

624 

625 .. seealso:: 

626 

627 :ref:`inheritance_toplevel` 

628 

629 :param polymorphic_identity: Specifies the value which 

630 identifies this particular class as returned by the column expression 

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

632 rows are received, the value corresponding to the 

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

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

635 reconstructed object. 

636 

637 .. seealso:: 

638 

639 :ref:`inheritance_toplevel` 

640 

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

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

643 persistence behavior of that attribute. Note that 

644 :class:`_schema.Column` 

645 objects present in 

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

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

648 When using Declarative, this argument is passed automatically, 

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

650 in the declared class body. 

651 

652 .. seealso:: 

653 

654 :ref:`orm_mapping_properties` - in the 

655 :ref:`orm_mapping_classes_toplevel` 

656 

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

658 objects, or alternatively string names of attribute names which 

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

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

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

662 can be overridden here. 

663 

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

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

666 

667 .. seealso:: 

668 

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

670 

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

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

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

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

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

676 version id, a 

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

678 thrown. 

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

680 unless ``version_id_generator`` specifies an alternative version 

681 generator. 

682 

683 .. seealso:: 

684 

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

686 and rationale. 

687 

688 :param version_id_generator: Define how new version ids should 

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

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

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

692 

693 def generate_version(version): 

694 return next_version 

695 

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

697 or programmatic versioning schemes outside of the version id 

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

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

700 of important points when using this option. 

701 

702 .. seealso:: 

703 

704 :ref:`custom_version_counter` 

705 

706 :ref:`server_side_version_counter` 

707 

708 

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

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

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

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

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

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

715 loaded immediately. The second tuple argument <selectable> 

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

717 classes. 

718 

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

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

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

722 indicating polymorphic loading styles. 

723 

724 .. seealso:: 

725 

726 :ref:`with_polymorphic_mapper_config` 

727 

728 """ 

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

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

731 self.class_.__module__, 

732 self.class_.__name__, 

733 ) 

734 

735 self._primary_key_argument = util.to_list(primary_key) 

736 self.non_primary = non_primary 

737 

738 self.always_refresh = always_refresh 

739 

740 if isinstance(version_id_col, MapperProperty): 

741 self.version_id_prop = version_id_col 

742 self.version_id_col = None 

743 else: 

744 self.version_id_col = ( 

745 coercions.expect( 

746 roles.ColumnArgumentOrKeyRole, 

747 version_id_col, 

748 argname="version_id_col", 

749 ) 

750 if version_id_col is not None 

751 else None 

752 ) 

753 

754 if version_id_generator is False: 

755 self.version_id_generator = False 

756 elif version_id_generator is None: 

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

758 else: 

759 self.version_id_generator = version_id_generator 

760 

761 self.concrete = concrete 

762 self.single = False 

763 

764 if inherits is not None: 

765 self.inherits = _parse_mapper_argument(inherits) 

766 else: 

767 self.inherits = None 

768 

769 if local_table is not None: 

770 self.local_table = coercions.expect( 

771 roles.StrictFromClauseRole, 

772 local_table, 

773 disable_inspection=True, 

774 argname="local_table", 

775 ) 

776 elif self.inherits: 

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

778 # .local_table need not be Optional 

779 self.local_table = self.inherits.local_table 

780 self.single = True 

781 else: 

782 raise sa_exc.ArgumentError( 

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

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

785 ) 

786 

787 if inherit_condition is not None: 

788 self.inherit_condition = coercions.expect( 

789 roles.OnClauseRole, inherit_condition 

790 ) 

791 else: 

792 self.inherit_condition = None 

793 

794 self.inherit_foreign_keys = inherit_foreign_keys 

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

796 self._delete_orphans = [] 

797 self.batch = batch 

798 self.eager_defaults = eager_defaults 

799 self.column_prefix = column_prefix 

800 

801 # interim - polymorphic_on is further refined in 

802 # _configure_polymorphic_setter 

803 self.polymorphic_on = ( 

804 coercions.expect( # type: ignore 

805 roles.ColumnArgumentOrKeyRole, 

806 polymorphic_on, 

807 argname="polymorphic_on", 

808 ) 

809 if polymorphic_on is not None 

810 else None 

811 ) 

812 self.polymorphic_abstract = polymorphic_abstract 

813 self._dependency_processors = [] 

814 self.validators = util.EMPTY_DICT 

815 self.passive_updates = passive_updates 

816 self.passive_deletes = passive_deletes 

817 self.legacy_is_orphan = legacy_is_orphan 

818 self._clause_adapter = None 

819 self._requires_row_aliasing = False 

820 self._inherits_equated_pairs = None 

821 self._memoized_values = {} 

822 self._compiled_cache_size = _compiled_cache_size 

823 self._reconstructor = None 

824 self.allow_partial_pks = allow_partial_pks 

825 

826 if self.inherits and not self.concrete: 

827 self.confirm_deleted_rows = False 

828 else: 

829 self.confirm_deleted_rows = confirm_deleted_rows 

830 

831 self._set_with_polymorphic(with_polymorphic) 

832 self.polymorphic_load = polymorphic_load 

833 

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

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

836 # the object instance for that row. 

837 self.polymorphic_identity = polymorphic_identity 

838 

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

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

841 # upon a select operation. 

842 if _polymorphic_map is None: 

843 self.polymorphic_map = {} 

844 else: 

845 self.polymorphic_map = _polymorphic_map 

846 

847 if include_properties is not None: 

848 self.include_properties = util.to_set(include_properties) 

849 else: 

850 self.include_properties = None 

851 if exclude_properties: 

852 self.exclude_properties = util.to_set(exclude_properties) 

853 else: 

854 self.exclude_properties = None 

855 

856 # prevent this mapper from being constructed 

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

858 # configure_mappers() until construction succeeds) 

859 with _CONFIGURE_MUTEX: 

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

861 class_, self 

862 ) 

863 self._configure_inheritance() 

864 self._configure_class_instrumentation() 

865 self._configure_properties() 

866 self._configure_polymorphic_setter() 

867 self._configure_pks() 

868 self.registry._flag_new_mapper(self) 

869 self._log("constructed") 

870 self._expire_memoizations() 

871 

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

873 

874 def _prefer_eager_defaults(self, dialect, table): 

875 if self.eager_defaults == "auto": 

876 if not table.implicit_returning: 

877 return False 

878 

879 return ( 

880 table in self._server_default_col_keys 

881 and dialect.insert_executemany_returning 

882 ) 

883 else: 

884 return self.eager_defaults 

885 

886 def _gen_cache_key(self, anon_map, bindparams): 

887 return (self,) 

888 

889 # ### BEGIN 

890 # ATTRIBUTE DECLARATIONS START HERE 

891 

892 is_mapper = True 

893 """Part of the inspection API.""" 

894 

895 represents_outer_join = False 

896 

897 registry: _RegistryType 

898 

899 @property 

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

901 """Part of the inspection API. 

902 

903 Returns self. 

904 

905 """ 

906 return self 

907 

908 @property 

909 def entity(self): 

910 r"""Part of the inspection API. 

911 

912 Returns self.class\_. 

913 

914 """ 

915 return self.class_ 

916 

917 class_: Type[_O] 

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

919 

920 _identity_class: Type[_O] 

921 

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

923 _dependency_processors: List[DependencyProcessor] 

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

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

926 _all_tables: Set[TableClause] 

927 _polymorphic_attr_key: Optional[str] 

928 

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

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

931 

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

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

934 

935 _columntoproperty: _ColumnMapping 

936 

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

938 _validate_polymorphic_identity: Optional[ 

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

940 ] 

941 

942 tables: Sequence[TableClause] 

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

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

945 is aware of. 

946 

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

948 :class:`_expression.Alias` 

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

950 :class:`_schema.Table` 

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

952 

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

954 Behavior is undefined if directly modified. 

955 

956 """ 

957 

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

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

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

961 

962 The dictionary contains string attribute names as keys 

963 mapped to the actual validation method. 

964 

965 """ 

966 

967 always_refresh: bool 

968 allow_partial_pks: bool 

969 version_id_col: Optional[ColumnElement[Any]] 

970 

971 with_polymorphic: Optional[ 

972 Tuple[ 

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

974 Optional[FromClause], 

975 ] 

976 ] 

977 

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

979 

980 local_table: FromClause 

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

982 :class:`_orm.Mapper` refers. 

983 

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

985 :class:`.FromClause`. 

986 

987 The "local" table is the 

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

989 managing from an attribute access and flush perspective. For 

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

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

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

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

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

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

996 

997 .. seealso:: 

998 

999 :attr:`_orm.Mapper.persist_selectable`. 

1000 

1001 :attr:`_orm.Mapper.selectable`. 

1002 

1003 """ 

1004 

1005 persist_selectable: FromClause 

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

1007 is mapped. 

1008 

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

1010 :class:`.FromClause`. 

1011 

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

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

1014 represents the inheriting class hierarchy overall in an inheritance 

1015 scenario. 

1016 

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

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

1019 alternate subquery used for selecting columns. 

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

1021 will be written on a persist operation. 

1022 

1023 .. seealso:: 

1024 

1025 :attr:`_orm.Mapper.selectable`. 

1026 

1027 :attr:`_orm.Mapper.local_table`. 

1028 

1029 """ 

1030 

1031 inherits: Optional[Mapper[Any]] 

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

1033 inherits from, if any. 

1034 

1035 """ 

1036 

1037 inherit_condition: Optional[ColumnElement[bool]] 

1038 

1039 configured: bool = False 

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

1041 

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

1043 Behavior is undefined if directly modified. 

1044 

1045 .. seealso:: 

1046 

1047 :func:`.configure_mappers`. 

1048 

1049 """ 

1050 

1051 concrete: bool 

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

1053 inheritance mapper. 

1054 

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

1056 Behavior is undefined if directly modified. 

1057 

1058 """ 

1059 

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

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

1062 objects 

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

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

1065 

1066 This list is against the selectable in 

1067 :attr:`_orm.Mapper.persist_selectable`. 

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

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

1070 :class:`_expression.Join`, the 

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

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

1073 

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

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

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

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

1078 

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

1080 Behavior is undefined if directly modified. 

1081 

1082 """ 

1083 

1084 class_manager: ClassManager[_O] 

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

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

1087 

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

1089 Behavior is undefined if directly modified. 

1090 

1091 """ 

1092 

1093 single: bool 

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

1095 inheritance mapper. 

1096 

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

1098 

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

1100 Behavior is undefined if directly modified. 

1101 

1102 """ 

1103 

1104 non_primary: bool 

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

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

1107 persistence management. 

1108 

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

1110 Behavior is undefined if directly modified. 

1111 

1112 """ 

1113 

1114 polymorphic_on: Optional[KeyedColumnElement[Any]] 

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

1116 ``polymorphic_on`` argument 

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

1118 

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

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

1121 :func:`.cast`. 

1122 

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

1124 Behavior is undefined if directly modified. 

1125 

1126 """ 

1127 

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

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

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

1131 

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

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

1134 

1135 An inheritance chain of mappers will all reference the same 

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

1137 result rows to target mappers. 

1138 

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

1140 Behavior is undefined if directly modified. 

1141 

1142 """ 

1143 

1144 polymorphic_identity: Optional[Any] 

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

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

1147 

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

1149 comparable to the type of column represented by 

1150 :attr:`_orm.Mapper.polymorphic_on`. 

1151 

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

1153 Behavior is undefined if directly modified. 

1154 

1155 """ 

1156 

1157 base_mapper: Mapper[Any] 

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

1159 

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

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

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

1163 objects in the inheritance chain. 

1164 

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

1166 Behavior is undefined if directly modified. 

1167 

1168 """ 

1169 

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

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

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

1173 

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

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

1176 except that only those columns included in 

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

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

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

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

1181 

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

1183 Behavior is undefined if directly modified. 

1184 

1185 """ 

1186 

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

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

1189 

1190 @util.non_memoized_property 

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

1192 def mapped_table(self): 

1193 return self.persist_selectable 

1194 

1195 @util.memoized_property 

1196 def _path_registry(self) -> CachingEntityRegistry: 

1197 return PathRegistry.per_mapper(self) 

1198 

1199 def _configure_inheritance(self): 

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

1201 being present.""" 

1202 

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

1204 self._inheriting_mappers = util.WeakSequence() 

1205 

1206 if self.inherits: 

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

1208 raise sa_exc.ArgumentError( 

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

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

1211 ) 

1212 

1213 self.dispatch._update(self.inherits.dispatch) 

1214 

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

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

1217 raise sa_exc.ArgumentError( 

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

1219 "only allowed from a %s mapper" 

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

1221 ) 

1222 

1223 if self.single: 

1224 self.persist_selectable = self.inherits.persist_selectable 

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

1226 if self.concrete: 

1227 self.persist_selectable = self.local_table 

1228 for mapper in self.iterate_to_root(): 

1229 if mapper.polymorphic_on is not None: 

1230 mapper._requires_row_aliasing = True 

1231 else: 

1232 if self.inherit_condition is None: 

1233 # figure out inherit condition from our table to the 

1234 # immediate table of the inherited mapper, not its 

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

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

1237 try: 

1238 self.inherit_condition = sql_util.join_condition( 

1239 self.inherits.local_table, self.local_table 

1240 ) 

1241 except sa_exc.NoForeignKeysError as nfe: 

1242 assert self.inherits.local_table is not None 

1243 assert self.local_table is not None 

1244 raise sa_exc.NoForeignKeysError( 

1245 "Can't determine the inherit condition " 

1246 "between inherited table '%s' and " 

1247 "inheriting " 

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

1249 "foreign key relationships established. " 

1250 "Please ensure the inheriting table has " 

1251 "a foreign key relationship to the " 

1252 "inherited " 

1253 "table, or provide an " 

1254 "'on clause' using " 

1255 "the 'inherit_condition' mapper argument." 

1256 % ( 

1257 self.inherits.local_table.description, 

1258 self.local_table.description, 

1259 ) 

1260 ) from nfe 

1261 except sa_exc.AmbiguousForeignKeysError as afe: 

1262 assert self.inherits.local_table is not None 

1263 assert self.local_table is not None 

1264 raise sa_exc.AmbiguousForeignKeysError( 

1265 "Can't determine the inherit condition " 

1266 "between inherited table '%s' and " 

1267 "inheriting " 

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

1269 "foreign key relationship established. " 

1270 "Please specify the 'on clause' using " 

1271 "the 'inherit_condition' mapper argument." 

1272 % ( 

1273 self.inherits.local_table.description, 

1274 self.local_table.description, 

1275 ) 

1276 ) from afe 

1277 assert self.inherits.persist_selectable is not None 

1278 self.persist_selectable = sql.join( 

1279 self.inherits.persist_selectable, 

1280 self.local_table, 

1281 self.inherit_condition, 

1282 ) 

1283 

1284 fks = util.to_set(self.inherit_foreign_keys) 

1285 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 

1286 self.persist_selectable.onclause, 

1287 consider_as_foreign_keys=fks, 

1288 ) 

1289 else: 

1290 self.persist_selectable = self.local_table 

1291 

1292 if self.polymorphic_identity is None: 

1293 self._identity_class = self.class_ 

1294 

1295 if ( 

1296 not self.polymorphic_abstract 

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

1298 ): 

1299 util.warn( 

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

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

1302 f"'polymorphic_on' column of " 

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

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

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

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

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

1308 "class unmapped when using Declarative, set the " 

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

1310 ) 

1311 elif self.concrete: 

1312 self._identity_class = self.class_ 

1313 else: 

1314 self._identity_class = self.inherits._identity_class 

1315 

1316 if self.version_id_col is None: 

1317 self.version_id_col = self.inherits.version_id_col 

1318 self.version_id_generator = self.inherits.version_id_generator 

1319 elif ( 

1320 self.inherits.version_id_col is not None 

1321 and self.version_id_col is not self.inherits.version_id_col 

1322 ): 

1323 util.warn( 

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

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

1326 "the inherited versioning column. " 

1327 "version_id_col should only be specified on " 

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

1329 % ( 

1330 self.version_id_col.description, 

1331 self.inherits.version_id_col.description, 

1332 ) 

1333 ) 

1334 

1335 self.polymorphic_map = self.inherits.polymorphic_map 

1336 self.batch = self.inherits.batch 

1337 self.inherits._inheriting_mappers.append(self) 

1338 self.base_mapper = self.inherits.base_mapper 

1339 self.passive_updates = self.inherits.passive_updates 

1340 self.passive_deletes = ( 

1341 self.inherits.passive_deletes or self.passive_deletes 

1342 ) 

1343 self._all_tables = self.inherits._all_tables 

1344 

1345 if self.polymorphic_identity is not None: 

1346 if self.polymorphic_identity in self.polymorphic_map: 

1347 util.warn( 

1348 "Reassigning polymorphic association for identity %r " 

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

1350 "value for polymorphic_identity." 

1351 % ( 

1352 self.polymorphic_identity, 

1353 self.polymorphic_map[self.polymorphic_identity], 

1354 self, 

1355 self.polymorphic_identity, 

1356 ) 

1357 ) 

1358 self.polymorphic_map[self.polymorphic_identity] = self 

1359 

1360 if self.polymorphic_load and self.concrete: 

1361 raise sa_exc.ArgumentError( 

1362 "polymorphic_load is not currently supported " 

1363 "with concrete table inheritance" 

1364 ) 

1365 if self.polymorphic_load == "inline": 

1366 self.inherits._add_with_polymorphic_subclass(self) 

1367 elif self.polymorphic_load == "selectin": 

1368 pass 

1369 elif self.polymorphic_load is not None: 

1370 raise sa_exc.ArgumentError( 

1371 "unknown argument for polymorphic_load: %r" 

1372 % self.polymorphic_load 

1373 ) 

1374 

1375 else: 

1376 self._all_tables = set() 

1377 self.base_mapper = self 

1378 assert self.local_table is not None 

1379 self.persist_selectable = self.local_table 

1380 if self.polymorphic_identity is not None: 

1381 self.polymorphic_map[self.polymorphic_identity] = self 

1382 self._identity_class = self.class_ 

1383 

1384 if self.persist_selectable is None: 

1385 raise sa_exc.ArgumentError( 

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

1387 % self 

1388 ) 

1389 

1390 def _set_with_polymorphic( 

1391 self, with_polymorphic: Optional[_WithPolymorphicArg] 

1392 ) -> None: 

1393 if with_polymorphic == "*": 

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

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

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

1397 self.with_polymorphic = cast( 

1398 """Tuple[ 

1399 Union[ 

1400 Literal["*"], 

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

1402 ], 

1403 Optional["FromClause"], 

1404 ]""", 

1405 with_polymorphic, 

1406 ) 

1407 else: 

1408 self.with_polymorphic = (with_polymorphic, None) 

1409 elif with_polymorphic is not None: 

1410 raise sa_exc.ArgumentError( 

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

1412 ) 

1413 else: 

1414 self.with_polymorphic = None 

1415 

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

1417 self.with_polymorphic = ( 

1418 self.with_polymorphic[0], 

1419 coercions.expect( 

1420 roles.StrictFromClauseRole, 

1421 self.with_polymorphic[1], 

1422 allow_select=True, 

1423 ), 

1424 ) 

1425 

1426 if self.configured: 

1427 self._expire_memoizations() 

1428 

1429 def _add_with_polymorphic_subclass(self, mapper): 

1430 subcl = mapper.class_ 

1431 if self.with_polymorphic is None: 

1432 self._set_with_polymorphic((subcl,)) 

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

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

1435 self._set_with_polymorphic( 

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

1437 ) 

1438 

1439 def _set_concrete_base(self, mapper): 

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

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

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

1443 

1444 assert self.concrete 

1445 assert not self.inherits 

1446 assert isinstance(mapper, Mapper) 

1447 self.inherits = mapper 

1448 self.inherits.polymorphic_map.update(self.polymorphic_map) 

1449 self.polymorphic_map = self.inherits.polymorphic_map 

1450 for mapper in self.iterate_to_root(): 

1451 if mapper.polymorphic_on is not None: 

1452 mapper._requires_row_aliasing = True 

1453 self.batch = self.inherits.batch 

1454 for mp in self.self_and_descendants: 

1455 mp.base_mapper = self.inherits.base_mapper 

1456 self.inherits._inheriting_mappers.append(self) 

1457 self.passive_updates = self.inherits.passive_updates 

1458 self._all_tables = self.inherits._all_tables 

1459 

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

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

1462 key, key, local=False, column=None 

1463 ): 

1464 self._adapt_inherited_property(key, prop, False) 

1465 

1466 def _set_polymorphic_on(self, polymorphic_on): 

1467 self.polymorphic_on = polymorphic_on 

1468 self._configure_polymorphic_setter(True) 

1469 

1470 def _configure_class_instrumentation(self): 

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

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

1473 given class and entity name. 

1474 

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

1476 name combination will return this mapper. Also decorate the 

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

1478 auto-session attachment logic. 

1479 

1480 """ 

1481 

1482 # we expect that declarative has applied the class manager 

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

1484 # this raises as of 2.0. 

1485 manager = attributes.opt_manager_of_class(self.class_) 

1486 

1487 if self.non_primary: 

1488 if not manager or not manager.is_mapped: 

1489 raise sa_exc.InvalidRequestError( 

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

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

1492 "Mapper." % self.class_ 

1493 ) 

1494 self.class_manager = manager 

1495 

1496 assert manager.registry is not None 

1497 self.registry = manager.registry 

1498 self._identity_class = manager.mapper._identity_class 

1499 manager.registry._add_non_primary_mapper(self) 

1500 return 

1501 

1502 if manager is None or not manager.registry: 

1503 raise sa_exc.InvalidRequestError( 

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

1505 "invoked directly outside of a declarative registry." 

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

1507 "function for a classical mapping." 

1508 ) 

1509 

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

1511 

1512 # this invokes the class_instrument event and sets up 

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

1514 # occur after the instrument_class event above. 

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

1516 # :( 

1517 

1518 manager = instrumentation.register_class( 

1519 self.class_, 

1520 mapper=self, 

1521 expired_attribute_loader=util.partial( 

1522 loading.load_scalar_attributes, self 

1523 ), 

1524 # finalize flag means instrument the __init__ method 

1525 # and call the class_instrument event 

1526 finalize=True, 

1527 ) 

1528 

1529 self.class_manager = manager 

1530 

1531 assert manager.registry is not None 

1532 self.registry = manager.registry 

1533 

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

1535 # e_name None or not. 

1536 if manager.mapper is None: 

1537 return 

1538 

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

1540 

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

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

1543 method = method._sa_original_init 

1544 if hasattr(method, "__func__"): 

1545 method = method.__func__ 

1546 if callable(method): 

1547 if hasattr(method, "__sa_reconstructor__"): 

1548 self._reconstructor = method 

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

1550 elif hasattr(method, "__sa_validators__"): 

1551 validation_opts = method.__sa_validation_opts__ 

1552 for name in method.__sa_validators__: 

1553 if name in self.validators: 

1554 raise sa_exc.InvalidRequestError( 

1555 "A validation function for mapped " 

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

1557 % (name, self) 

1558 ) 

1559 self.validators = self.validators.union( 

1560 {name: (method, validation_opts)} 

1561 ) 

1562 

1563 def _set_dispose_flags(self) -> None: 

1564 self.configured = True 

1565 self._ready_for_configure = True 

1566 self._dispose_called = True 

1567 

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

1569 

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

1571 try: 

1572 prop = self._props[key] 

1573 except KeyError as err: 

1574 raise sa_exc.ArgumentError( 

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

1576 "no attribute is mapped to this name." 

1577 ) from err 

1578 try: 

1579 expr = prop.expression 

1580 except AttributeError as ae: 

1581 raise sa_exc.ArgumentError( 

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

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

1584 ) from ae 

1585 if not isinstance(expr, Column): 

1586 raise sa_exc.ArgumentError( 

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

1588 "property does not refer to a single " 

1589 "mapped Column" 

1590 ) 

1591 return expr 

1592 

1593 def _configure_pks(self) -> None: 

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

1595 

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

1597 

1598 self._pks_by_table = {} 

1599 self._cols_by_table = {} 

1600 

1601 all_cols = util.column_set( 

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

1603 ) 

1604 

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

1606 

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

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

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

1610 # ordering is important since it determines the ordering of 

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

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

1613 fc.primary_key 

1614 ).intersection( 

1615 pk_cols 

1616 ) 

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

1618 all_cols 

1619 ) 

1620 

1621 if self._primary_key_argument: 

1622 coerced_pk_arg = [ 

1623 ( 

1624 self._str_arg_to_mapped_col("primary_key", c) 

1625 if isinstance(c, str) 

1626 else c 

1627 ) 

1628 for c in ( 

1629 coercions.expect( 

1630 roles.DDLConstraintColumnRole, 

1631 coerce_pk, 

1632 argname="primary_key", 

1633 ) 

1634 for coerce_pk in self._primary_key_argument 

1635 ) 

1636 ] 

1637 else: 

1638 coerced_pk_arg = None 

1639 

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

1641 # primary key mappings 

1642 if coerced_pk_arg: 

1643 for k in coerced_pk_arg: 

1644 if k.table not in self._pks_by_table: 

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

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

1647 

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

1649 elif ( 

1650 self.persist_selectable not in self._pks_by_table 

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

1652 ): 

1653 raise sa_exc.ArgumentError( 

1654 "Mapper %s could not assemble any primary " 

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

1656 % (self, self.persist_selectable.description) 

1657 ) 

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

1659 self.local_table, schema.Table 

1660 ): 

1661 util.warn( 

1662 "Could not assemble any primary " 

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

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

1665 % self.local_table.description 

1666 ) 

1667 

1668 if ( 

1669 self.inherits 

1670 and not self.concrete 

1671 and not self._primary_key_argument 

1672 ): 

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

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

1675 self.primary_key = self.inherits.primary_key 

1676 else: 

1677 # determine primary key from argument or persist_selectable pks 

1678 primary_key: Collection[ColumnElement[Any]] 

1679 

1680 if coerced_pk_arg: 

1681 primary_key = [ 

1682 cc if cc is not None else c 

1683 for cc, c in ( 

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

1685 for c in coerced_pk_arg 

1686 ) 

1687 ] 

1688 else: 

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

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

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

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

1693 primary_key = sql_util.reduce_columns( 

1694 self._pks_by_table[self.persist_selectable], 

1695 ignore_nonexistent_tables=True, 

1696 ) 

1697 

1698 if len(primary_key) == 0: 

1699 raise sa_exc.ArgumentError( 

1700 "Mapper %s could not assemble any primary " 

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

1702 % (self, self.persist_selectable.description) 

1703 ) 

1704 

1705 self.primary_key = tuple(primary_key) 

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

1707 

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

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

1710 self._readonly_props = { 

1711 self._columntoproperty[col] 

1712 for col in self._columntoproperty 

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

1714 and ( 

1715 not hasattr(col, "table") 

1716 or col.table not in self._cols_by_table 

1717 ) 

1718 } 

1719 

1720 def _configure_properties(self) -> None: 

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

1722 

1723 # object attribute names mapped to MapperProperty objects 

1724 self._props = util.OrderedDict() 

1725 

1726 # table columns mapped to MapperProperty 

1727 self._columntoproperty = _ColumnMapping(self) 

1728 

1729 explicit_col_props_by_column: Dict[ 

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

1731 ] = {} 

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

1733 

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

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

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

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

1738 if self._init_properties: 

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

1740 if not isinstance(prop_arg, MapperProperty): 

1741 possible_col_prop = self._make_prop_from_column( 

1742 key, prop_arg 

1743 ) 

1744 else: 

1745 possible_col_prop = prop_arg 

1746 

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

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

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

1750 # the Table. 

1751 

1752 _map_as_property_now = True 

1753 if isinstance(possible_col_prop, properties.ColumnProperty): 

1754 for given_col in possible_col_prop.columns: 

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

1756 _map_as_property_now = False 

1757 explicit_col_props_by_key[key] = possible_col_prop 

1758 explicit_col_props_by_column[given_col] = ( 

1759 key, 

1760 possible_col_prop, 

1761 ) 

1762 

1763 if _map_as_property_now: 

1764 self._configure_property( 

1765 key, 

1766 possible_col_prop, 

1767 init=False, 

1768 ) 

1769 

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

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

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

1773 if self.inherits: 

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

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

1776 continue 

1777 

1778 incoming_prop = explicit_col_props_by_key.get(key) 

1779 if incoming_prop: 

1780 new_prop = self._reconcile_prop_with_incoming_columns( 

1781 key, 

1782 inherited_prop, 

1783 warn_only=False, 

1784 incoming_prop=incoming_prop, 

1785 ) 

1786 explicit_col_props_by_key[key] = new_prop 

1787 

1788 for inc_col in incoming_prop.columns: 

1789 explicit_col_props_by_column[inc_col] = ( 

1790 key, 

1791 new_prop, 

1792 ) 

1793 elif key not in self._props: 

1794 self._adapt_inherited_property(key, inherited_prop, False) 

1795 

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

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

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

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

1800 # reconciliation against inherited columns occurs here also. 

1801 

1802 for column in self.persist_selectable.columns: 

1803 if column in explicit_col_props_by_column: 

1804 # column was explicitly passed to properties; configure 

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

1806 # Table / selectable 

1807 key, prop = explicit_col_props_by_column[column] 

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

1809 continue 

1810 

1811 elif column in self._columntoproperty: 

1812 continue 

1813 

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

1815 if self._should_exclude( 

1816 column.key, 

1817 column_key, 

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

1819 column=column, 

1820 ): 

1821 continue 

1822 

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

1824 # of the inheriting mapper 

1825 for mapper in self.iterate_to_root(): 

1826 if column in mapper._columntoproperty: 

1827 column_key = mapper._columntoproperty[column].key 

1828 

1829 self._configure_property( 

1830 column_key, 

1831 column, 

1832 init=False, 

1833 setparent=True, 

1834 ) 

1835 

1836 def _configure_polymorphic_setter(self, init=False): 

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

1838 'polymorphic_on' column, if applicable, and not 

1839 already generated by _configure_properties (which is typical). 

1840 

1841 Also create a setter function which will assign this 

1842 attribute to the value of the 'polymorphic_identity' 

1843 upon instance construction, also if applicable. This 

1844 routine will run when an instance is created. 

1845 

1846 """ 

1847 setter = False 

1848 polymorphic_key: Optional[str] = None 

1849 

1850 if self.polymorphic_on is not None: 

1851 setter = True 

1852 

1853 if isinstance(self.polymorphic_on, str): 

1854 # polymorphic_on specified as a string - link 

1855 # it to mapped ColumnProperty 

1856 try: 

1857 self.polymorphic_on = self._props[self.polymorphic_on] 

1858 except KeyError as err: 

1859 raise sa_exc.ArgumentError( 

1860 "Can't determine polymorphic_on " 

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

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

1863 ) from err 

1864 

1865 if self.polymorphic_on in self._columntoproperty: 

1866 # polymorphic_on is a column that is already mapped 

1867 # to a ColumnProperty 

1868 prop = self._columntoproperty[self.polymorphic_on] 

1869 elif isinstance(self.polymorphic_on, MapperProperty): 

1870 # polymorphic_on is directly a MapperProperty, 

1871 # ensure it's a ColumnProperty 

1872 if not isinstance( 

1873 self.polymorphic_on, properties.ColumnProperty 

1874 ): 

1875 raise sa_exc.ArgumentError( 

1876 "Only direct column-mapped " 

1877 "property or SQL expression " 

1878 "can be passed for polymorphic_on" 

1879 ) 

1880 prop = self.polymorphic_on 

1881 else: 

1882 # polymorphic_on is a Column or SQL expression and 

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

1884 # only present in the with_polymorphic selectable or 

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

1886 # hope is compatible with this mapper's persist_selectable 

1887 col = self.persist_selectable.corresponding_column( 

1888 self.polymorphic_on 

1889 ) 

1890 if col is None: 

1891 # polymorphic_on doesn't derive from any 

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

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

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

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

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

1897 # represented somehow in either persist_selectable or 

1898 # with_polymorphic. Otherwise as of 0.7.4 we 

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

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

1901 setter = False 

1902 instrument = False 

1903 col = self.polymorphic_on 

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

1905 self.with_polymorphic is None 

1906 or self.with_polymorphic[1] is None 

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

1908 is None 

1909 ): 

1910 raise sa_exc.InvalidRequestError( 

1911 "Could not map polymorphic_on column " 

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

1913 "loads will not function properly" 

1914 % col.description 

1915 ) 

1916 else: 

1917 # column/expression that polymorphic_on derives from 

1918 # is present in our mapped table 

1919 # and is probably mapped, but polymorphic_on itself 

1920 # is not. This happens when 

1921 # the polymorphic_on is only directly present in the 

1922 # with_polymorphic selectable, as when use 

1923 # polymorphic_union. 

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

1925 instrument = True 

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

1927 if key: 

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

1929 raise sa_exc.InvalidRequestError( 

1930 "Cannot exclude or override the " 

1931 "discriminator column %r" % key 

1932 ) 

1933 else: 

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

1935 key = col.key 

1936 

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

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

1939 

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

1941 # column in the property 

1942 self.polymorphic_on = prop.columns[0] 

1943 polymorphic_key = prop.key 

1944 else: 

1945 # no polymorphic_on was set. 

1946 # check inheriting mappers for one. 

1947 for mapper in self.iterate_to_root(): 

1948 # determine if polymorphic_on of the parent 

1949 # should be propagated here. If the col 

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

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

1952 # inheritance), we can use it 

1953 if mapper.polymorphic_on is not None: 

1954 if self.persist_selectable is mapper.persist_selectable: 

1955 self.polymorphic_on = mapper.polymorphic_on 

1956 else: 

1957 self.polymorphic_on = ( 

1958 self.persist_selectable 

1959 ).corresponding_column(mapper.polymorphic_on) 

1960 # we can use the parent mapper's _set_polymorphic_identity 

1961 # directly; it ensures the polymorphic_identity of the 

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

1963 if self.polymorphic_on is not None: 

1964 self._set_polymorphic_identity = ( 

1965 mapper._set_polymorphic_identity 

1966 ) 

1967 self._polymorphic_attr_key = ( 

1968 mapper._polymorphic_attr_key 

1969 ) 

1970 self._validate_polymorphic_identity = ( 

1971 mapper._validate_polymorphic_identity 

1972 ) 

1973 else: 

1974 self._set_polymorphic_identity = None 

1975 self._polymorphic_attr_key = None 

1976 return 

1977 

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

1979 raise sa_exc.InvalidRequestError( 

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

1981 "on a mapper hierarchy which includes the " 

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

1983 ) 

1984 

1985 if setter: 

1986 

1987 def _set_polymorphic_identity(state): 

1988 dict_ = state.dict 

1989 # TODO: what happens if polymorphic_on column attribute name 

1990 # does not match .key? 

1991 

1992 polymorphic_identity = ( 

1993 state.manager.mapper.polymorphic_identity 

1994 ) 

1995 if ( 

1996 polymorphic_identity is None 

1997 and state.manager.mapper.polymorphic_abstract 

1998 ): 

1999 raise sa_exc.InvalidRequestError( 

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

2001 "mapper is marked polymorphic_abstract=True" 

2002 ) 

2003 

2004 state.get_impl(polymorphic_key).set( 

2005 state, 

2006 dict_, 

2007 polymorphic_identity, 

2008 None, 

2009 ) 

2010 

2011 self._polymorphic_attr_key = polymorphic_key 

2012 

2013 def _validate_polymorphic_identity(mapper, state, dict_): 

2014 if ( 

2015 polymorphic_key in dict_ 

2016 and dict_[polymorphic_key] 

2017 not in mapper._acceptable_polymorphic_identities 

2018 ): 

2019 util.warn_limited( 

2020 "Flushing object %s with " 

2021 "incompatible polymorphic identity %r; the " 

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

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

2024 ) 

2025 

2026 self._set_polymorphic_identity = _set_polymorphic_identity 

2027 self._validate_polymorphic_identity = ( 

2028 _validate_polymorphic_identity 

2029 ) 

2030 else: 

2031 self._polymorphic_attr_key = None 

2032 self._set_polymorphic_identity = None 

2033 

2034 _validate_polymorphic_identity = None 

2035 

2036 @HasMemoized.memoized_attribute 

2037 def _version_id_prop(self): 

2038 if self.version_id_col is not None: 

2039 return self._columntoproperty[self.version_id_col] 

2040 else: 

2041 return None 

2042 

2043 @HasMemoized.memoized_attribute 

2044 def _acceptable_polymorphic_identities(self): 

2045 identities = set() 

2046 

2047 stack = deque([self]) 

2048 while stack: 

2049 item = stack.popleft() 

2050 if item.persist_selectable is self.persist_selectable: 

2051 identities.add(item.polymorphic_identity) 

2052 stack.extend(item._inheriting_mappers) 

2053 

2054 return identities 

2055 

2056 @HasMemoized.memoized_attribute 

2057 def _prop_set(self): 

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

2059 

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

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

2062 descriptor_props = util.preloaded.orm_descriptor_props 

2063 

2064 if not self.concrete: 

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

2066 elif key not in self._props: 

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

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

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

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

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

2072 # descriptors that might have side effects when invoked. 

2073 implementing_attribute = self.class_manager._get_class_attr_mro( 

2074 key, prop 

2075 ) 

2076 if implementing_attribute is prop or ( 

2077 isinstance( 

2078 implementing_attribute, attributes.InstrumentedAttribute 

2079 ) 

2080 and implementing_attribute._parententity is prop.parent 

2081 ): 

2082 self._configure_property( 

2083 key, 

2084 descriptor_props.ConcreteInheritedProperty(), 

2085 init=init, 

2086 setparent=True, 

2087 ) 

2088 

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

2090 def _configure_property( 

2091 self, 

2092 key: str, 

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

2094 *, 

2095 init: bool = True, 

2096 setparent: bool = True, 

2097 warn_for_existing: bool = False, 

2098 ) -> MapperProperty[Any]: 

2099 descriptor_props = util.preloaded.orm_descriptor_props 

2100 self._log( 

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

2102 ) 

2103 

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

2105 # ensure a Column is turned into a ColumnProperty. 

2106 # see #12858 

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

2108 

2109 if not isinstance(prop_arg, MapperProperty): 

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

2111 key, prop_arg, early_setup 

2112 ) 

2113 else: 

2114 prop = prop_arg 

2115 

2116 if early_setup: 

2117 return prop 

2118 

2119 if isinstance(prop, properties.ColumnProperty): 

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

2121 

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

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

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

2125 if col is None and self.inherits: 

2126 path = [self] 

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

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

2129 if col is not None: 

2130 for m2 in path: 

2131 m2.persist_selectable._refresh_for_new_column(col) 

2132 col = self.persist_selectable.corresponding_column( 

2133 prop.columns[0] 

2134 ) 

2135 break 

2136 path.append(m) 

2137 

2138 # subquery expression, column not present in the mapped 

2139 # selectable. 

2140 if col is None: 

2141 col = prop.columns[0] 

2142 

2143 # column is coming in after _readonly_props was 

2144 # initialized; check for 'readonly' 

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

2146 not hasattr(col, "table") 

2147 or col.table not in self._cols_by_table 

2148 ): 

2149 self._readonly_props.add(prop) 

2150 

2151 else: 

2152 # if column is coming in after _cols_by_table was 

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

2154 if ( 

2155 hasattr(self, "_cols_by_table") 

2156 and col.table in self._cols_by_table 

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

2158 ): 

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

2160 

2161 # if this properties.ColumnProperty represents the "polymorphic 

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

2163 # columns in SELECT statements. 

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

2165 prop._is_polymorphic_discriminator = ( 

2166 col is self.polymorphic_on 

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

2168 ) 

2169 

2170 if isinstance(col, expression.Label): 

2171 # new in 1.4, get column property against expressions 

2172 # to be addressable in subqueries 

2173 col.key = col._tq_key_label = key 

2174 

2175 self.columns.add(col, key) 

2176 

2177 for col in prop.columns: 

2178 for proxy_col in col.proxy_set: 

2179 self._columntoproperty[proxy_col] = prop 

2180 

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

2182 util.warn( 

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

2184 "assigned to attribute " 

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

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

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

2188 ) 

2189 

2190 prop.key = key 

2191 

2192 if setparent: 

2193 prop.set_parent(self, init) 

2194 

2195 if key in self._props and getattr( 

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

2197 ): 

2198 syn = self._props[key]._mapped_by_synonym 

2199 raise sa_exc.ArgumentError( 

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

2201 "a ColumnProperty already exists keyed to the name " 

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

2203 ) 

2204 

2205 # replacement cases 

2206 

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

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

2209 if ( 

2210 key in self._props 

2211 and not isinstance( 

2212 self._props[key], descriptor_props.ConcreteInheritedProperty 

2213 ) 

2214 and not isinstance(prop, descriptor_props.SynonymProperty) 

2215 ): 

2216 if warn_for_existing: 

2217 util.warn_deprecated( 

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

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

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

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

2222 "release", 

2223 "2.0", 

2224 ) 

2225 oldprop = self._props[key] 

2226 self._path_registry.pop(oldprop, None) 

2227 

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

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

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

2231 # get replaced. 

2232 elif ( 

2233 warn_for_existing 

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

2235 and not isinstance(prop, descriptor_props.SynonymProperty) 

2236 and not isinstance( 

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

2238 descriptor_props.ConcreteInheritedProperty, 

2239 ) 

2240 ): 

2241 util.warn_deprecated( 

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

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

2244 "attribute of the same name. " 

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

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

2247 "release", 

2248 "2.0", 

2249 ) 

2250 

2251 self._props[key] = prop 

2252 

2253 if not self.non_primary: 

2254 prop.instrument_class(self) 

2255 

2256 for mapper in self._inheriting_mappers: 

2257 mapper._adapt_inherited_property(key, prop, init) 

2258 

2259 if init: 

2260 prop.init() 

2261 prop.post_instrument_class(self) 

2262 

2263 if self.configured: 

2264 self._expire_memoizations() 

2265 

2266 return prop 

2267 

2268 def _make_prop_from_column( 

2269 self, 

2270 key: str, 

2271 column: Union[ 

2272 Sequence[KeyedColumnElement[Any]], KeyedColumnElement[Any] 

2273 ], 

2274 ) -> ColumnProperty[Any]: 

2275 columns = util.to_list(column) 

2276 mapped_column = [] 

2277 for c in columns: 

2278 mc = self.persist_selectable.corresponding_column(c) 

2279 if mc is None: 

2280 mc = self.local_table.corresponding_column(c) 

2281 if mc is not None: 

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

2283 # mapped table, this corresponds to adding a 

2284 # column after the fact to the local table. 

2285 # [ticket:1523] 

2286 self.persist_selectable._refresh_for_new_column(mc) 

2287 mc = self.persist_selectable.corresponding_column(c) 

2288 if mc is None: 

2289 raise sa_exc.ArgumentError( 

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

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

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

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

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

2295 ) 

2296 mapped_column.append(mc) 

2297 return properties.ColumnProperty(*mapped_column) 

2298 

2299 def _reconcile_prop_with_incoming_columns( 

2300 self, 

2301 key: str, 

2302 existing_prop: MapperProperty[Any], 

2303 warn_only: bool, 

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

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

2306 ) -> ColumnProperty[Any]: 

2307 if incoming_prop and ( 

2308 self.concrete 

2309 or not isinstance(existing_prop, properties.ColumnProperty) 

2310 ): 

2311 return incoming_prop 

2312 

2313 existing_column = existing_prop.columns[0] 

2314 

2315 if incoming_prop and existing_column in incoming_prop.columns: 

2316 return incoming_prop 

2317 

2318 if incoming_prop is None: 

2319 assert single_column is not None 

2320 incoming_column = single_column 

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

2322 else: 

2323 assert single_column is None 

2324 incoming_column = incoming_prop.columns[0] 

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

2326 

2327 if ( 

2328 ( 

2329 not self._inherits_equated_pairs 

2330 or (equated_pair_key not in self._inherits_equated_pairs) 

2331 ) 

2332 and not existing_column.shares_lineage(incoming_column) 

2333 and existing_column is not self.version_id_col 

2334 and incoming_column is not self.version_id_col 

2335 ): 

2336 msg = ( 

2337 "Implicitly combining column %s with column " 

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

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

2340 "explicitly." 

2341 % ( 

2342 existing_prop.columns[-1], 

2343 incoming_column, 

2344 key, 

2345 ) 

2346 ) 

2347 if warn_only: 

2348 util.warn(msg) 

2349 else: 

2350 raise sa_exc.InvalidRequestError(msg) 

2351 

2352 # existing properties.ColumnProperty from an inheriting 

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

2354 # breakpoint() 

2355 new_prop = existing_prop.copy() 

2356 

2357 new_prop.columns.insert(0, incoming_column) 

2358 self._log( 

2359 "inserting column to existing list " 

2360 "in properties.ColumnProperty %s", 

2361 key, 

2362 ) 

2363 return new_prop # type: ignore 

2364 

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

2366 def _property_from_column( 

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

2368 ) -> ColumnProperty[Any]: 

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

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

2371 

2372 descriptor_props = util.preloaded.orm_descriptor_props 

2373 

2374 if early_setup: 

2375 prop = None 

2376 else: 

2377 prop = self._props.get(key) 

2378 

2379 if isinstance(prop, properties.ColumnProperty): 

2380 return self._reconcile_prop_with_incoming_columns( 

2381 key, 

2382 prop, 

2383 single_column=column, 

2384 warn_only=prop.parent is not self, 

2385 ) 

2386 elif prop is None or isinstance( 

2387 prop, descriptor_props.ConcreteInheritedProperty 

2388 ): 

2389 return self._make_prop_from_column(key, column) 

2390 else: 

2391 raise sa_exc.ArgumentError( 

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

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

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

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

2396 "to remove all awareness of the column entirely " 

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

2398 "use the 'include_properties' or 'exclude_properties' " 

2399 "mapper arguments to control specifically which table " 

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

2401 ) 

2402 

2403 @util.langhelpers.tag_method_for_warnings( 

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

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

2406 "operation.", 

2407 sa_exc.SAWarning, 

2408 ) 

2409 def _check_configure(self) -> None: 

2410 if self.registry._new_mappers: 

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

2412 

2413 def _post_configure_properties(self) -> None: 

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

2415 attached to this mapper. 

2416 

2417 This is a deferred configuration step which is intended 

2418 to execute once all mappers have been constructed. 

2419 

2420 """ 

2421 

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

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

2424 for key, prop in l: 

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

2426 

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

2428 prop.init() 

2429 

2430 if prop._configure_finished: 

2431 prop.post_instrument_class(self) 

2432 

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

2434 self.configured = True 

2435 

2436 def add_properties(self, dict_of_properties): 

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

2438 using `add_property`. 

2439 

2440 """ 

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

2442 self.add_property(key, value) 

2443 

2444 def add_property( 

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

2446 ) -> None: 

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

2448 

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

2450 property to the initial properties dictionary sent to the 

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

2452 the given MapperProperty is configured immediately. 

2453 

2454 """ 

2455 prop = self._configure_property( 

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

2457 ) 

2458 assert isinstance(prop, MapperProperty) 

2459 self._init_properties[key] = prop 

2460 

2461 def _expire_memoizations(self) -> None: 

2462 for mapper in self.iterate_to_root(): 

2463 mapper._reset_memoizations() 

2464 

2465 @property 

2466 def _log_desc(self) -> str: 

2467 return ( 

2468 "(" 

2469 + self.class_.__name__ 

2470 + "|" 

2471 + ( 

2472 self.local_table is not None 

2473 and self.local_table.description 

2474 or str(self.local_table) 

2475 ) 

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

2477 + ")" 

2478 ) 

2479 

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

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

2482 

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

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

2485 

2486 def __repr__(self) -> str: 

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

2488 

2489 def __str__(self) -> str: 

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

2491 self.class_.__name__, 

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

2493 ( 

2494 self.local_table.description 

2495 if self.local_table is not None 

2496 else self.persist_selectable.description 

2497 ), 

2498 ) 

2499 

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

2501 orphan_possible = False 

2502 for mapper in self.iterate_to_root(): 

2503 for key, cls in mapper._delete_orphans: 

2504 orphan_possible = True 

2505 

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

2507 state, key, optimistic=state.has_identity 

2508 ) 

2509 

2510 if self.legacy_is_orphan and has_parent: 

2511 return False 

2512 elif not self.legacy_is_orphan and not has_parent: 

2513 return True 

2514 

2515 if self.legacy_is_orphan: 

2516 return orphan_possible 

2517 else: 

2518 return False 

2519 

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

2521 return key in self._props 

2522 

2523 def get_property( 

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

2525 ) -> MapperProperty[Any]: 

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

2527 

2528 if _configure_mappers: 

2529 self._check_configure() 

2530 

2531 try: 

2532 return self._props[key] 

2533 except KeyError as err: 

2534 raise sa_exc.InvalidRequestError( 

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

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

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

2538 ) from err 

2539 

2540 def get_property_by_column( 

2541 self, column: ColumnElement[_T] 

2542 ) -> MapperProperty[_T]: 

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

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

2545 

2546 return self._columntoproperty[column] 

2547 

2548 @property 

2549 def iterate_properties(self): 

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

2551 

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

2553 

2554 def _mappers_from_spec( 

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

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

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

2558 represents. 

2559 

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

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

2562 

2563 """ 

2564 if spec == "*": 

2565 mappers = list(self.self_and_descendants) 

2566 elif spec: 

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

2568 for m in util.to_list(spec): 

2569 m = _class_to_mapper(m) 

2570 if not m.isa(self): 

2571 raise sa_exc.InvalidRequestError( 

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

2573 ) 

2574 

2575 if selectable is None: 

2576 mapper_set.update(m.iterate_to_root()) 

2577 else: 

2578 mapper_set.add(m) 

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

2580 else: 

2581 mappers = [] 

2582 

2583 if selectable is not None: 

2584 tables = set( 

2585 sql_util.find_tables(selectable, include_aliases=True) 

2586 ) 

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

2588 return mappers 

2589 

2590 def _selectable_from_mappers( 

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

2592 ) -> FromClause: 

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

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

2595 mapped tables. 

2596 

2597 """ 

2598 from_obj = self.persist_selectable 

2599 for m in mappers: 

2600 if m is self: 

2601 continue 

2602 if m.concrete: 

2603 raise sa_exc.InvalidRequestError( 

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

2605 "when concrete-inheriting mappers are used." 

2606 ) 

2607 elif not m.single: 

2608 if innerjoin: 

2609 from_obj = from_obj.join( 

2610 m.local_table, m.inherit_condition 

2611 ) 

2612 else: 

2613 from_obj = from_obj.outerjoin( 

2614 m.local_table, m.inherit_condition 

2615 ) 

2616 

2617 return from_obj 

2618 

2619 @HasMemoized.memoized_attribute 

2620 def _version_id_has_server_side_value(self) -> bool: 

2621 vid_col = self.version_id_col 

2622 

2623 if vid_col is None: 

2624 return False 

2625 

2626 elif not isinstance(vid_col, Column): 

2627 return True 

2628 else: 

2629 return vid_col.server_default is not None or ( 

2630 vid_col.default is not None 

2631 and ( 

2632 not vid_col.default.is_scalar 

2633 and not vid_col.default.is_callable 

2634 ) 

2635 ) 

2636 

2637 @HasMemoized.memoized_attribute 

2638 def _single_table_criterion(self): 

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

2640 return self.polymorphic_on._annotate( 

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

2642 ).in_( 

2643 [ 

2644 m.polymorphic_identity 

2645 for m in self.self_and_descendants 

2646 if not m.polymorphic_abstract 

2647 ] 

2648 ) 

2649 else: 

2650 return None 

2651 

2652 @HasMemoized.memoized_attribute 

2653 def _has_aliased_polymorphic_fromclause(self): 

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

2655 like a subquery. 

2656 

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

2658 if this is present. 

2659 

2660 """ 

2661 return self.with_polymorphic and isinstance( 

2662 self.with_polymorphic[1], 

2663 expression.AliasedReturnsRows, 

2664 ) 

2665 

2666 @HasMemoized.memoized_attribute 

2667 def _should_select_with_poly_adapter(self): 

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

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

2670 rows for mapped classes and subclasses against this Mapper. 

2671 

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

2673 for this condition. 

2674 

2675 """ 

2676 

2677 # this has been simplified as of #8456. 

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

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

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

2681 # 

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

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

2684 # flattened JOIN for with_polymorphic.) 

2685 # 

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

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

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

2689 # some kind or polymorphic_union. 

2690 # 

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

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

2693 # on it (such as test_join_from_polymorphic_explicit_aliased_three). 

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

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

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

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

2698 # legacy case we should probably disable. 

2699 # 

2700 # 

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

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

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

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

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

2706 # polymorphic base class. 

2707 # 

2708 return ( 

2709 self._has_aliased_polymorphic_fromclause 

2710 or self._requires_row_aliasing 

2711 or (self.base_mapper._has_aliased_polymorphic_fromclause) 

2712 or self.base_mapper._requires_row_aliasing 

2713 ) 

2714 

2715 @HasMemoized.memoized_attribute 

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

2717 self._check_configure() 

2718 

2719 if not self.with_polymorphic: 

2720 return [] 

2721 return self._mappers_from_spec(*self.with_polymorphic) 

2722 

2723 @HasMemoized.memoized_attribute 

2724 def _post_inspect(self): 

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

2726 

2727 E.g. when Query calls: 

2728 

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

2730 

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

2732 

2733 """ 

2734 self._check_configure() 

2735 

2736 @HasMemoized_ro_memoized_attribute 

2737 def _with_polymorphic_selectable(self) -> FromClause: 

2738 if not self.with_polymorphic: 

2739 return self.persist_selectable 

2740 

2741 spec, selectable = self.with_polymorphic 

2742 if selectable is not None: 

2743 return selectable 

2744 else: 

2745 return self._selectable_from_mappers( 

2746 self._mappers_from_spec(spec, selectable), False 

2747 ) 

2748 

2749 with_polymorphic_mappers = _with_polymorphic_mappers 

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

2751 default "polymorphic" query. 

2752 

2753 """ 

2754 

2755 @HasMemoized_ro_memoized_attribute 

2756 def _insert_cols_evaluating_none(self): 

2757 return { 

2758 table: frozenset( 

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

2760 ) 

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

2762 } 

2763 

2764 @HasMemoized.memoized_attribute 

2765 def _insert_cols_as_none(self): 

2766 return { 

2767 table: frozenset( 

2768 col.key 

2769 for col in columns 

2770 if not col.primary_key 

2771 and not col.server_default 

2772 and not col.default 

2773 and not col.type.should_evaluate_none 

2774 ) 

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

2776 } 

2777 

2778 @HasMemoized.memoized_attribute 

2779 def _propkey_to_col(self): 

2780 return { 

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

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

2783 } 

2784 

2785 @HasMemoized.memoized_attribute 

2786 def _pk_keys_by_table(self): 

2787 return { 

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

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

2790 } 

2791 

2792 @HasMemoized.memoized_attribute 

2793 def _pk_attr_keys_by_table(self): 

2794 return { 

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

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

2797 } 

2798 

2799 @HasMemoized.memoized_attribute 

2800 def _server_default_cols( 

2801 self, 

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

2803 return { 

2804 table: frozenset( 

2805 [ 

2806 col 

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

2808 if col.server_default is not None 

2809 or ( 

2810 col.default is not None 

2811 and col.default.is_clause_element 

2812 ) 

2813 ] 

2814 ) 

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

2816 } 

2817 

2818 @HasMemoized.memoized_attribute 

2819 def _server_onupdate_default_cols( 

2820 self, 

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

2822 return { 

2823 table: frozenset( 

2824 [ 

2825 col 

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

2827 if col.server_onupdate is not None 

2828 or ( 

2829 col.onupdate is not None 

2830 and col.onupdate.is_clause_element 

2831 ) 

2832 ] 

2833 ) 

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

2835 } 

2836 

2837 @HasMemoized.memoized_attribute 

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

2839 return { 

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

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

2842 } 

2843 

2844 @HasMemoized.memoized_attribute 

2845 def _server_onupdate_default_col_keys( 

2846 self, 

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

2848 return { 

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

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

2851 } 

2852 

2853 @HasMemoized.memoized_attribute 

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

2855 result: Set[str] = set() 

2856 

2857 col_to_property = self._columntoproperty 

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

2859 result.update( 

2860 col_to_property[col].key 

2861 for col in columns.intersection(col_to_property) 

2862 ) 

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

2864 result.update( 

2865 col_to_property[col].key 

2866 for col in columns.intersection(col_to_property) 

2867 ) 

2868 return result 

2869 

2870 @HasMemoized.memoized_instancemethod 

2871 def __clause_element__(self): 

2872 annotations: Dict[str, Any] = { 

2873 "entity_namespace": self, 

2874 "parententity": self, 

2875 "parentmapper": self, 

2876 } 

2877 if self.persist_selectable is not self.local_table: 

2878 # joined table inheritance, with polymorphic selectable, 

2879 # etc. 

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

2881 { 

2882 "entity_namespace": self, 

2883 "parententity": self, 

2884 "parentmapper": self, 

2885 } 

2886 )._set_propagate_attrs( 

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

2888 ) 

2889 

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

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

2892 ) 

2893 

2894 @util.memoized_property 

2895 def select_identity_token(self): 

2896 return ( 

2897 expression.null() 

2898 ._annotate( 

2899 { 

2900 "entity_namespace": self, 

2901 "parententity": self, 

2902 "parentmapper": self, 

2903 "identity_token": True, 

2904 } 

2905 ) 

2906 ._set_propagate_attrs( 

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

2908 ) 

2909 ) 

2910 

2911 @property 

2912 def selectable(self) -> FromClause: 

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

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

2915 

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

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

2918 full "polymorphic" selectable is returned. 

2919 

2920 """ 

2921 return self._with_polymorphic_selectable 

2922 

2923 def _with_polymorphic_args( 

2924 self, 

2925 spec: Any = None, 

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

2927 innerjoin: bool = False, 

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

2929 if selectable not in (None, False): 

2930 selectable = coercions.expect( 

2931 roles.StrictFromClauseRole, selectable, allow_select=True 

2932 ) 

2933 

2934 if self.with_polymorphic: 

2935 if not spec: 

2936 spec = self.with_polymorphic[0] 

2937 if selectable is False: 

2938 selectable = self.with_polymorphic[1] 

2939 elif selectable is False: 

2940 selectable = None 

2941 mappers = self._mappers_from_spec(spec, selectable) 

2942 if selectable is not None: 

2943 return mappers, selectable 

2944 else: 

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

2946 

2947 @HasMemoized.memoized_attribute 

2948 def _polymorphic_properties(self): 

2949 return list( 

2950 self._iterate_polymorphic_properties( 

2951 self._with_polymorphic_mappers 

2952 ) 

2953 ) 

2954 

2955 @property 

2956 def _all_column_expressions(self): 

2957 poly_properties = self._polymorphic_properties 

2958 adapter = self._polymorphic_adapter 

2959 

2960 return [ 

2961 adapter.columns[c] if adapter else c 

2962 for prop in poly_properties 

2963 if isinstance(prop, properties.ColumnProperty) 

2964 and prop._renders_in_subqueries 

2965 for c in prop.columns 

2966 ] 

2967 

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

2969 if polymorphic_mappers: 

2970 poly_properties = self._iterate_polymorphic_properties( 

2971 polymorphic_mappers 

2972 ) 

2973 else: 

2974 poly_properties = self._polymorphic_properties 

2975 

2976 return [ 

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

2978 for prop in poly_properties 

2979 if isinstance(prop, properties.ColumnProperty) 

2980 ] 

2981 

2982 @HasMemoized.memoized_attribute 

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

2984 if self._has_aliased_polymorphic_fromclause: 

2985 return orm_util.ORMAdapter( 

2986 orm_util._TraceAdaptRole.MAPPER_POLYMORPHIC_ADAPTER, 

2987 self, 

2988 selectable=self.selectable, 

2989 equivalents=self._equivalent_columns, 

2990 limit_on_entity=False, 

2991 ) 

2992 else: 

2993 return None 

2994 

2995 def _iterate_polymorphic_properties(self, mappers=None): 

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

2997 a SELECT.""" 

2998 if mappers is None: 

2999 mappers = self._with_polymorphic_mappers 

3000 

3001 if not mappers: 

3002 for c in self.iterate_properties: 

3003 yield c 

3004 else: 

3005 # in the polymorphic case, filter out discriminator columns 

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

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

3008 for c in util.unique_list( 

3009 chain( 

3010 *[ 

3011 list(mapper.iterate_properties) 

3012 for mapper in [self] + mappers 

3013 ] 

3014 ) 

3015 ): 

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

3017 self.polymorphic_on is None 

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

3019 ): 

3020 continue 

3021 yield c 

3022 

3023 @HasMemoized.memoized_attribute 

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

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

3026 associated this mapper. 

3027 

3028 This is an object that provides each property based on 

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

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

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

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

3033 column. The namespace object can also be iterated, 

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

3035 

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

3037 of this attribute which limit the types of properties 

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

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

3040 

3041 .. warning:: 

3042 

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

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

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

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

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

3048 accessing attributes dynamically, favor using the dict-access 

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

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

3051 

3052 .. seealso:: 

3053 

3054 :attr:`_orm.Mapper.all_orm_descriptors` 

3055 

3056 """ 

3057 

3058 self._check_configure() 

3059 return util.ReadOnlyProperties(self._props) 

3060 

3061 @HasMemoized.memoized_attribute 

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

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

3064 with the mapped class. 

3065 

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

3067 associated with the mapped class or its superclasses. 

3068 

3069 This namespace includes attributes that are mapped to the class 

3070 as well as attributes declared by extension modules. 

3071 It includes any Python descriptor type that inherits from 

3072 :class:`.InspectionAttr`. This includes 

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

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

3075 :class:`.AssociationProxy`. 

3076 

3077 To distinguish between mapped attributes and extension attributes, 

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

3079 to a constant that distinguishes between different extension types. 

3080 

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

3082 

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

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

3085 

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

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

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

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

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

3091 or the mapper. 

3092 

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

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

3095 class in which it first appeared. 

3096 

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

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

3099 

3100 .. versionchanged:: 1.3.19 ensured deterministic ordering for 

3101 :meth:`_orm.Mapper.all_orm_descriptors`. 

3102 

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

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

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

3106 referring to the collection of mapped properties via 

3107 :attr:`_orm.Mapper.attrs`. 

3108 

3109 .. warning:: 

3110 

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

3112 accessor namespace is an 

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

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

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

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

3117 accessing attributes dynamically, favor using the dict-access 

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

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

3120 collisions. 

3121 

3122 .. seealso:: 

3123 

3124 :attr:`_orm.Mapper.attrs` 

3125 

3126 """ 

3127 return util.ReadOnlyProperties( 

3128 dict(self.class_manager._all_sqla_attributes()) 

3129 ) 

3130 

3131 @HasMemoized.memoized_attribute 

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

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

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

3135 all synonyms that refer to primary key columns 

3136 

3137 """ 

3138 descriptor_props = util.preloaded.orm_descriptor_props 

3139 

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

3141 

3142 return { 

3143 syn.key: syn.name 

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

3145 if isinstance(syn, descriptor_props.SynonymProperty) 

3146 and syn.name in pk_keys 

3147 } 

3148 

3149 @HasMemoized.memoized_attribute 

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

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

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

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

3154 

3155 .. seealso:: 

3156 

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

3158 :class:`.MapperProperty` 

3159 objects. 

3160 

3161 """ 

3162 descriptor_props = util.preloaded.orm_descriptor_props 

3163 

3164 return self._filter_properties(descriptor_props.SynonymProperty) 

3165 

3166 @property 

3167 def entity_namespace(self): 

3168 return self.class_ 

3169 

3170 @HasMemoized.memoized_attribute 

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

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

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

3174 

3175 .. seealso:: 

3176 

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

3178 :class:`.MapperProperty` 

3179 objects. 

3180 

3181 """ 

3182 return self._filter_properties(properties.ColumnProperty) 

3183 

3184 @HasMemoized.memoized_attribute 

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

3186 def relationships( 

3187 self, 

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

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

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

3191 

3192 .. warning:: 

3193 

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

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

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

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

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

3199 accessing attributes dynamically, favor using the dict-access 

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

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

3202 collisions. 

3203 

3204 .. seealso:: 

3205 

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

3207 :class:`.MapperProperty` 

3208 objects. 

3209 

3210 """ 

3211 return self._filter_properties( 

3212 util.preloaded.orm_relationships.RelationshipProperty 

3213 ) 

3214 

3215 @HasMemoized.memoized_attribute 

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

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

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

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

3220 

3221 .. seealso:: 

3222 

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

3224 :class:`.MapperProperty` 

3225 objects. 

3226 

3227 """ 

3228 return self._filter_properties( 

3229 util.preloaded.orm_descriptor_props.CompositeProperty 

3230 ) 

3231 

3232 def _filter_properties( 

3233 self, type_: Type[_MP] 

3234 ) -> util.ReadOnlyProperties[_MP]: 

3235 self._check_configure() 

3236 return util.ReadOnlyProperties( 

3237 util.OrderedDict( 

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

3239 ) 

3240 ) 

3241 

3242 @HasMemoized.memoized_attribute 

3243 def _get_clause(self): 

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

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

3246 by primary key. 

3247 

3248 """ 

3249 params = [ 

3250 ( 

3251 primary_key, 

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

3253 ) 

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

3255 ] 

3256 return ( 

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

3258 util.column_dict(params), 

3259 ) 

3260 

3261 @HasMemoized.memoized_attribute 

3262 def _equivalent_columns(self) -> _EquivalentColumnMap: 

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

3264 the determination of column pairs that are equated to 

3265 one another based on inherit condition. This is designed 

3266 to work with the queries that util.polymorphic_union 

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

3268 the base table directly (including the subclass table columns 

3269 only). 

3270 

3271 The resulting structure is a dictionary of columns mapped 

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

3273 

3274 {tablea.col1: {tableb.col1, tablec.col1}, tablea.col2: {tabled.col2}} 

3275 

3276 """ # noqa: E501 

3277 result: _EquivalentColumnMap = {} 

3278 

3279 def visit_binary(binary): 

3280 if binary.operator == operators.eq: 

3281 if binary.left in result: 

3282 result[binary.left].add(binary.right) 

3283 else: 

3284 result[binary.left] = {binary.right} 

3285 if binary.right in result: 

3286 result[binary.right].add(binary.left) 

3287 else: 

3288 result[binary.right] = {binary.left} 

3289 

3290 for mapper in self.base_mapper.self_and_descendants: 

3291 if mapper.inherit_condition is not None: 

3292 visitors.traverse( 

3293 mapper.inherit_condition, {}, {"binary": visit_binary} 

3294 ) 

3295 

3296 return result 

3297 

3298 def _is_userland_descriptor(self, assigned_name: str, obj: Any) -> bool: 

3299 if isinstance( 

3300 obj, 

3301 ( 

3302 _MappedAttribute, 

3303 instrumentation.ClassManager, 

3304 expression.ColumnElement, 

3305 ), 

3306 ): 

3307 return False 

3308 else: 

3309 return assigned_name not in self._dataclass_fields 

3310 

3311 @HasMemoized.memoized_attribute 

3312 def _dataclass_fields(self): 

3313 return [f.name for f in util.dataclass_fields(self.class_)] 

3314 

3315 def _should_exclude(self, name, assigned_name, local, column): 

3316 """determine whether a particular property should be implicitly 

3317 present on the class. 

3318 

3319 This occurs when properties are propagated from an inherited class, or 

3320 are applied from the columns present in the mapped table. 

3321 

3322 """ 

3323 

3324 if column is not None and sql_base._never_select_column(column): 

3325 return True 

3326 

3327 # check for class-bound attributes and/or descriptors, 

3328 # either local or from an inherited class 

3329 # ignore dataclass field default values 

3330 if local: 

3331 if self.class_.__dict__.get( 

3332 assigned_name, None 

3333 ) is not None and self._is_userland_descriptor( 

3334 assigned_name, self.class_.__dict__[assigned_name] 

3335 ): 

3336 return True 

3337 else: 

3338 attr = self.class_manager._get_class_attr_mro(assigned_name, None) 

3339 if attr is not None and self._is_userland_descriptor( 

3340 assigned_name, attr 

3341 ): 

3342 return True 

3343 

3344 if ( 

3345 self.include_properties is not None 

3346 and name not in self.include_properties 

3347 and (column is None or column not in self.include_properties) 

3348 ): 

3349 self._log("not including property %s" % (name)) 

3350 return True 

3351 

3352 if self.exclude_properties is not None and ( 

3353 name in self.exclude_properties 

3354 or (column is not None and column in self.exclude_properties) 

3355 ): 

3356 self._log("excluding property %s" % (name)) 

3357 return True 

3358 

3359 return False 

3360 

3361 def common_parent(self, other: Mapper[Any]) -> bool: 

3362 """Return true if the given mapper shares a 

3363 common inherited parent as this mapper.""" 

3364 

3365 return self.base_mapper is other.base_mapper 

3366 

3367 def is_sibling(self, other: Mapper[Any]) -> bool: 

3368 """return true if the other mapper is an inheriting sibling to this 

3369 one. common parent but different branch 

3370 

3371 """ 

3372 return ( 

3373 self.base_mapper is other.base_mapper 

3374 and not self.isa(other) 

3375 and not other.isa(self) 

3376 ) 

3377 

3378 def _canload( 

3379 self, state: InstanceState[Any], allow_subtypes: bool 

3380 ) -> bool: 

3381 s = self.primary_mapper() 

3382 if self.polymorphic_on is not None or allow_subtypes: 

3383 return _state_mapper(state).isa(s) 

3384 else: 

3385 return _state_mapper(state) is s 

3386 

3387 def isa(self, other: Mapper[Any]) -> bool: 

3388 """Return True if the this mapper inherits from the given mapper.""" 

3389 

3390 m: Optional[Mapper[Any]] = self 

3391 while m and m is not other: 

3392 m = m.inherits 

3393 return bool(m) 

3394 

3395 def iterate_to_root(self) -> Iterator[Mapper[Any]]: 

3396 m: Optional[Mapper[Any]] = self 

3397 while m: 

3398 yield m 

3399 m = m.inherits 

3400 

3401 @HasMemoized.memoized_attribute 

3402 def self_and_descendants(self) -> Sequence[Mapper[Any]]: 

3403 """The collection including this mapper and all descendant mappers. 

3404 

3405 This includes not just the immediately inheriting mappers but 

3406 all their inheriting mappers as well. 

3407 

3408 """ 

3409 descendants = [] 

3410 stack = deque([self]) 

3411 while stack: 

3412 item = stack.popleft() 

3413 descendants.append(item) 

3414 stack.extend(item._inheriting_mappers) 

3415 return util.WeakSequence(descendants) 

3416 

3417 def polymorphic_iterator(self) -> Iterator[Mapper[Any]]: 

3418 """Iterate through the collection including this mapper and 

3419 all descendant mappers. 

3420 

3421 This includes not just the immediately inheriting mappers but 

3422 all their inheriting mappers as well. 

3423 

3424 To iterate through an entire hierarchy, use 

3425 ``mapper.base_mapper.polymorphic_iterator()``. 

3426 

3427 """ 

3428 return iter(self.self_and_descendants) 

3429 

3430 def primary_mapper(self) -> Mapper[Any]: 

3431 """Return the primary mapper corresponding to this mapper's class key 

3432 (class).""" 

3433 

3434 return self.class_manager.mapper 

3435 

3436 @property 

3437 def primary_base_mapper(self) -> Mapper[Any]: 

3438 return self.class_manager.mapper.base_mapper 

3439 

3440 def _result_has_identity_key(self, result, adapter=None): 

3441 pk_cols: Sequence[ColumnElement[Any]] 

3442 if adapter is not None: 

3443 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3444 else: 

3445 pk_cols = self.primary_key 

3446 rk = result.keys() 

3447 for col in pk_cols: 

3448 if col not in rk: 

3449 return False 

3450 else: 

3451 return True 

3452 

3453 def identity_key_from_row( 

3454 self, 

3455 row: Union[Row[Any], RowMapping], 

3456 identity_token: Optional[Any] = None, 

3457 adapter: Optional[ORMAdapter] = None, 

3458 ) -> _IdentityKeyType[_O]: 

3459 """Return an identity-map key for use in storing/retrieving an 

3460 item from the identity map. 

3461 

3462 :param row: A :class:`.Row` or :class:`.RowMapping` produced from a 

3463 result set that selected from the ORM mapped primary key columns. 

3464 

3465 .. versionchanged:: 2.0 

3466 :class:`.Row` or :class:`.RowMapping` are accepted 

3467 for the "row" argument 

3468 

3469 """ 

3470 pk_cols: Sequence[ColumnElement[Any]] 

3471 if adapter is not None: 

3472 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3473 else: 

3474 pk_cols = self.primary_key 

3475 

3476 mapping: RowMapping 

3477 if hasattr(row, "_mapping"): 

3478 mapping = row._mapping 

3479 else: 

3480 mapping = row # type: ignore[assignment] 

3481 

3482 return ( 

3483 self._identity_class, 

3484 tuple(mapping[column] for column in pk_cols), 

3485 identity_token, 

3486 ) 

3487 

3488 def identity_key_from_primary_key( 

3489 self, 

3490 primary_key: Tuple[Any, ...], 

3491 identity_token: Optional[Any] = None, 

3492 ) -> _IdentityKeyType[_O]: 

3493 """Return an identity-map key for use in storing/retrieving an 

3494 item from an identity map. 

3495 

3496 :param primary_key: A list of values indicating the identifier. 

3497 

3498 """ 

3499 return ( 

3500 self._identity_class, 

3501 tuple(primary_key), 

3502 identity_token, 

3503 ) 

3504 

3505 def identity_key_from_instance(self, instance: _O) -> _IdentityKeyType[_O]: 

3506 """Return the identity key for the given instance, based on 

3507 its primary key attributes. 

3508 

3509 If the instance's state is expired, calling this method 

3510 will result in a database check to see if the object has been deleted. 

3511 If the row no longer exists, 

3512 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3513 

3514 This value is typically also found on the instance state under the 

3515 attribute name `key`. 

3516 

3517 """ 

3518 state = attributes.instance_state(instance) 

3519 return self._identity_key_from_state(state, PassiveFlag.PASSIVE_OFF) 

3520 

3521 def _identity_key_from_state( 

3522 self, 

3523 state: InstanceState[_O], 

3524 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3525 ) -> _IdentityKeyType[_O]: 

3526 dict_ = state.dict 

3527 manager = state.manager 

3528 return ( 

3529 self._identity_class, 

3530 tuple( 

3531 [ 

3532 manager[prop.key].impl.get(state, dict_, passive) 

3533 for prop in self._identity_key_props 

3534 ] 

3535 ), 

3536 state.identity_token, 

3537 ) 

3538 

3539 def primary_key_from_instance(self, instance: _O) -> Tuple[Any, ...]: 

3540 """Return the list of primary key values for the given 

3541 instance. 

3542 

3543 If the instance's state is expired, calling this method 

3544 will result in a database check to see if the object has been deleted. 

3545 If the row no longer exists, 

3546 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3547 

3548 """ 

3549 state = attributes.instance_state(instance) 

3550 identity_key = self._identity_key_from_state( 

3551 state, PassiveFlag.PASSIVE_OFF 

3552 ) 

3553 return identity_key[1] 

3554 

3555 @HasMemoized.memoized_attribute 

3556 def _persistent_sortkey_fn(self): 

3557 key_fns = [col.type.sort_key_function for col in self.primary_key] 

3558 

3559 if set(key_fns).difference([None]): 

3560 

3561 def key(state): 

3562 return tuple( 

3563 key_fn(val) if key_fn is not None else val 

3564 for key_fn, val in zip(key_fns, state.key[1]) 

3565 ) 

3566 

3567 else: 

3568 

3569 def key(state): 

3570 return state.key[1] 

3571 

3572 return key 

3573 

3574 @HasMemoized.memoized_attribute 

3575 def _identity_key_props(self): 

3576 return [self._columntoproperty[col] for col in self.primary_key] 

3577 

3578 @HasMemoized.memoized_attribute 

3579 def _all_pk_cols(self): 

3580 collection: Set[ColumnClause[Any]] = set() 

3581 for table in self.tables: 

3582 collection.update(self._pks_by_table[table]) 

3583 return collection 

3584 

3585 @HasMemoized.memoized_attribute 

3586 def _should_undefer_in_wildcard(self): 

3587 cols: Set[ColumnElement[Any]] = set(self.primary_key) 

3588 if self.polymorphic_on is not None: 

3589 cols.add(self.polymorphic_on) 

3590 return cols 

3591 

3592 @HasMemoized.memoized_attribute 

3593 def _primary_key_propkeys(self): 

3594 return {self._columntoproperty[col].key for col in self._all_pk_cols} 

3595 

3596 def _get_state_attr_by_column( 

3597 self, 

3598 state: InstanceState[_O], 

3599 dict_: _InstanceDict, 

3600 column: ColumnElement[Any], 

3601 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3602 ) -> Any: 

3603 prop = self._columntoproperty[column] 

3604 return state.manager[prop.key].impl.get(state, dict_, passive=passive) 

3605 

3606 def _set_committed_state_attr_by_column(self, state, dict_, column, value): 

3607 prop = self._columntoproperty[column] 

3608 state.manager[prop.key].impl.set_committed_value(state, dict_, value) 

3609 

3610 def _set_state_attr_by_column(self, state, dict_, column, value): 

3611 prop = self._columntoproperty[column] 

3612 state.manager[prop.key].impl.set(state, dict_, value, None) 

3613 

3614 def _get_committed_attr_by_column(self, obj, column): 

3615 state = attributes.instance_state(obj) 

3616 dict_ = attributes.instance_dict(obj) 

3617 return self._get_committed_state_attr_by_column( 

3618 state, dict_, column, passive=PassiveFlag.PASSIVE_OFF 

3619 ) 

3620 

3621 def _get_committed_state_attr_by_column( 

3622 self, state, dict_, column, passive=PassiveFlag.PASSIVE_RETURN_NO_VALUE 

3623 ): 

3624 prop = self._columntoproperty[column] 

3625 return state.manager[prop.key].impl.get_committed_value( 

3626 state, dict_, passive=passive 

3627 ) 

3628 

3629 def _optimized_get_statement(self, state, attribute_names): 

3630 """assemble a WHERE clause which retrieves a given state by primary 

3631 key, using a minimized set of tables. 

3632 

3633 Applies to a joined-table inheritance mapper where the 

3634 requested attribute names are only present on joined tables, 

3635 not the base table. The WHERE clause attempts to include 

3636 only those tables to minimize joins. 

3637 

3638 """ 

3639 props = self._props 

3640 

3641 col_attribute_names = set(attribute_names).intersection( 

3642 state.mapper.column_attrs.keys() 

3643 ) 

3644 tables: Set[FromClause] = set( 

3645 chain( 

3646 *[ 

3647 sql_util.find_tables(c, check_columns=True) 

3648 for key in col_attribute_names 

3649 for c in props[key].columns 

3650 ] 

3651 ) 

3652 ) 

3653 

3654 if self.base_mapper.local_table in tables: 

3655 return None 

3656 

3657 def visit_binary(binary): 

3658 leftcol = binary.left 

3659 rightcol = binary.right 

3660 if leftcol is None or rightcol is None: 

3661 return 

3662 

3663 if leftcol.table not in tables: 

3664 leftval = self._get_committed_state_attr_by_column( 

3665 state, 

3666 state.dict, 

3667 leftcol, 

3668 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3669 ) 

3670 if leftval in orm_util._none_set: 

3671 raise _OptGetColumnsNotAvailable() 

3672 binary.left = sql.bindparam( 

3673 None, leftval, type_=binary.right.type 

3674 ) 

3675 elif rightcol.table not in tables: 

3676 rightval = self._get_committed_state_attr_by_column( 

3677 state, 

3678 state.dict, 

3679 rightcol, 

3680 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3681 ) 

3682 if rightval in orm_util._none_set: 

3683 raise _OptGetColumnsNotAvailable() 

3684 binary.right = sql.bindparam( 

3685 None, rightval, type_=binary.right.type 

3686 ) 

3687 

3688 allconds: List[ColumnElement[bool]] = [] 

3689 

3690 start = False 

3691 

3692 # as of #7507, from the lowest base table on upwards, 

3693 # we include all intermediary tables. 

3694 

3695 for mapper in reversed(list(self.iterate_to_root())): 

3696 if mapper.local_table in tables: 

3697 start = True 

3698 elif not isinstance(mapper.local_table, expression.TableClause): 

3699 return None 

3700 if start and not mapper.single: 

3701 assert mapper.inherits 

3702 assert not mapper.concrete 

3703 assert mapper.inherit_condition is not None 

3704 allconds.append(mapper.inherit_condition) 

3705 tables.add(mapper.local_table) 

3706 

3707 # only the bottom table needs its criteria to be altered to fit 

3708 # the primary key ident - the rest of the tables upwards to the 

3709 # descendant-most class should all be present and joined to each 

3710 # other. 

3711 try: 

3712 _traversed = visitors.cloned_traverse( 

3713 allconds[0], {}, {"binary": visit_binary} 

3714 ) 

3715 except _OptGetColumnsNotAvailable: 

3716 return None 

3717 else: 

3718 allconds[0] = _traversed 

3719 

3720 cond = sql.and_(*allconds) 

3721 

3722 cols = [] 

3723 for key in col_attribute_names: 

3724 cols.extend(props[key].columns) 

3725 return ( 

3726 sql.select(*cols) 

3727 .where(cond) 

3728 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 

3729 ) 

3730 

3731 def _iterate_to_target_viawpoly(self, mapper): 

3732 if self.isa(mapper): 

3733 prev = self 

3734 for m in self.iterate_to_root(): 

3735 yield m 

3736 

3737 if m is not prev and prev not in m._with_polymorphic_mappers: 

3738 break 

3739 

3740 prev = m 

3741 if m is mapper: 

3742 break 

3743 

3744 @HasMemoized.memoized_attribute 

3745 def _would_selectinload_combinations_cache(self): 

3746 return {} 

3747 

3748 def _would_selectin_load_only_from_given_mapper(self, super_mapper): 

3749 """return True if this mapper would "selectin" polymorphic load based 

3750 on the given super mapper, and not from a setting from a subclass. 

3751 

3752 given:: 

3753 

3754 class A: ... 

3755 

3756 

3757 class B(A): 

3758 __mapper_args__ = {"polymorphic_load": "selectin"} 

3759 

3760 

3761 class C(B): ... 

3762 

3763 

3764 class D(B): 

3765 __mapper_args__ = {"polymorphic_load": "selectin"} 

3766 

3767 ``inspect(C)._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3768 returns True, because C does selectin loading because of B's setting. 

3769 

3770 OTOH, ``inspect(D) 

3771 ._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3772 returns False, because D does selectin loading because of its own 

3773 setting; when we are doing a selectin poly load from B, we want to 

3774 filter out D because it would already have its own selectin poly load 

3775 set up separately. 

3776 

3777 Added as part of #9373. 

3778 

3779 """ 

3780 cache = self._would_selectinload_combinations_cache 

3781 

3782 try: 

3783 return cache[super_mapper] 

3784 except KeyError: 

3785 pass 

3786 

3787 # assert that given object is a supermapper, meaning we already 

3788 # strong reference it directly or indirectly. this allows us 

3789 # to not worry that we are creating new strongrefs to unrelated 

3790 # mappers or other objects. 

3791 assert self.isa(super_mapper) 

3792 

3793 mapper = super_mapper 

3794 for m in self._iterate_to_target_viawpoly(mapper): 

3795 if m.polymorphic_load == "selectin": 

3796 retval = m is super_mapper 

3797 break 

3798 else: 

3799 retval = False 

3800 

3801 cache[super_mapper] = retval 

3802 return retval 

3803 

3804 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 

3805 if not enabled_via_opt: 

3806 # common case, takes place for all polymorphic loads 

3807 mapper = polymorphic_from 

3808 for m in self._iterate_to_target_viawpoly(mapper): 

3809 if m.polymorphic_load == "selectin": 

3810 return m 

3811 else: 

3812 # uncommon case, selectin load options were used 

3813 enabled_via_opt = set(enabled_via_opt) 

3814 enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 

3815 for entity in enabled_via_opt.union([polymorphic_from]): 

3816 mapper = entity.mapper 

3817 for m in self._iterate_to_target_viawpoly(mapper): 

3818 if ( 

3819 m.polymorphic_load == "selectin" 

3820 or m in enabled_via_opt_mappers 

3821 ): 

3822 return enabled_via_opt_mappers.get(m, m) 

3823 

3824 return None 

3825 

3826 @util.preload_module("sqlalchemy.orm.strategy_options") 

3827 def _subclass_load_via_in(self, entity, polymorphic_from): 

3828 """Assemble a that can load the columns local to 

3829 this subclass as a SELECT with IN. 

3830 

3831 """ 

3832 

3833 strategy_options = util.preloaded.orm_strategy_options 

3834 

3835 assert self.inherits 

3836 

3837 if self.polymorphic_on is not None: 

3838 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 

3839 keep_props = set([polymorphic_prop] + self._identity_key_props) 

3840 else: 

3841 keep_props = set(self._identity_key_props) 

3842 

3843 disable_opt = strategy_options.Load(entity) 

3844 enable_opt = strategy_options.Load(entity) 

3845 

3846 classes_to_include = {self} 

3847 m: Optional[Mapper[Any]] = self.inherits 

3848 while ( 

3849 m is not None 

3850 and m is not polymorphic_from 

3851 and m.polymorphic_load == "selectin" 

3852 ): 

3853 classes_to_include.add(m) 

3854 m = m.inherits 

3855 

3856 for prop in self.column_attrs + self.relationships: 

3857 # skip prop keys that are not instrumented on the mapped class. 

3858 # this is primarily the "_sa_polymorphic_on" property that gets 

3859 # created for an ad-hoc polymorphic_on SQL expression, issue #8704 

3860 if prop.key not in self.class_manager: 

3861 continue 

3862 

3863 if prop.parent in classes_to_include or prop in keep_props: 

3864 # "enable" options, to turn on the properties that we want to 

3865 # load by default (subject to options from the query) 

3866 if not isinstance(prop, StrategizedProperty): 

3867 continue 

3868 

3869 enable_opt = enable_opt._set_generic_strategy( 

3870 # convert string name to an attribute before passing 

3871 # to loader strategy. note this must be in terms 

3872 # of given entity, such as AliasedClass, etc. 

3873 (getattr(entity.entity_namespace, prop.key),), 

3874 dict(prop.strategy_key), 

3875 _reconcile_to_other=True, 

3876 ) 

3877 else: 

3878 # "disable" options, to turn off the properties from the 

3879 # superclass that we *don't* want to load, applied after 

3880 # the options from the query to override them 

3881 disable_opt = disable_opt._set_generic_strategy( 

3882 # convert string name to an attribute before passing 

3883 # to loader strategy. note this must be in terms 

3884 # of given entity, such as AliasedClass, etc. 

3885 (getattr(entity.entity_namespace, prop.key),), 

3886 {"do_nothing": True}, 

3887 _reconcile_to_other=False, 

3888 ) 

3889 

3890 primary_key = [ 

3891 sql_util._deep_annotate(pk, {"_orm_adapt": True}) 

3892 for pk in self.primary_key 

3893 ] 

3894 

3895 in_expr: ColumnElement[Any] 

3896 

3897 if len(primary_key) > 1: 

3898 in_expr = sql.tuple_(*primary_key) 

3899 else: 

3900 in_expr = primary_key[0] 

3901 

3902 if entity.is_aliased_class: 

3903 assert entity.mapper is self 

3904 

3905 q = sql.select(entity).set_label_style( 

3906 LABEL_STYLE_TABLENAME_PLUS_COL 

3907 ) 

3908 

3909 in_expr = entity._adapter.traverse(in_expr) 

3910 primary_key = [entity._adapter.traverse(k) for k in primary_key] 

3911 q = q.where( 

3912 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3913 ).order_by(*primary_key) 

3914 else: 

3915 q = sql.select(self).set_label_style( 

3916 LABEL_STYLE_TABLENAME_PLUS_COL 

3917 ) 

3918 q = q.where( 

3919 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3920 ).order_by(*primary_key) 

3921 

3922 return q, enable_opt, disable_opt 

3923 

3924 @HasMemoized.memoized_attribute 

3925 def _subclass_load_via_in_mapper(self): 

3926 # the default is loading this mapper against the basemost mapper 

3927 return self._subclass_load_via_in(self, self.base_mapper) 

3928 

3929 def cascade_iterator( 

3930 self, 

3931 type_: str, 

3932 state: InstanceState[_O], 

3933 halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None, 

3934 ) -> Iterator[ 

3935 Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict] 

3936 ]: 

3937 r"""Iterate each element and its mapper in an object graph, 

3938 for all relationships that meet the given cascade rule. 

3939 

3940 :param type\_: 

3941 The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 

3942 etc.). 

3943 

3944 .. note:: the ``"all"`` cascade is not accepted here. For a generic 

3945 object traversal function, see :ref:`faq_walk_objects`. 

3946 

3947 :param state: 

3948 The lead InstanceState. child items will be processed per 

3949 the relationships defined for this object's mapper. 

3950 

3951 :return: the method yields individual object instances. 

3952 

3953 .. seealso:: 

3954 

3955 :ref:`unitofwork_cascades` 

3956 

3957 :ref:`faq_walk_objects` - illustrates a generic function to 

3958 traverse all objects without relying on cascades. 

3959 

3960 """ 

3961 visited_states: Set[InstanceState[Any]] = set() 

3962 prp, mpp = object(), object() 

3963 

3964 assert state.mapper.isa(self) 

3965 

3966 # this is actually a recursive structure, fully typing it seems 

3967 # a little too difficult for what it's worth here 

3968 visitables: Deque[ 

3969 Tuple[ 

3970 Deque[Any], 

3971 object, 

3972 Optional[InstanceState[Any]], 

3973 Optional[_InstanceDict], 

3974 ] 

3975 ] 

3976 

3977 visitables = deque( 

3978 [(deque(state.mapper._props.values()), prp, state, state.dict)] 

3979 ) 

3980 

3981 while visitables: 

3982 iterator, item_type, parent_state, parent_dict = visitables[-1] 

3983 if not iterator: 

3984 visitables.pop() 

3985 continue 

3986 

3987 if item_type is prp: 

3988 prop = iterator.popleft() 

3989 if not prop.cascade or type_ not in prop.cascade: 

3990 continue 

3991 assert parent_state is not None 

3992 assert parent_dict is not None 

3993 queue = deque( 

3994 prop.cascade_iterator( 

3995 type_, 

3996 parent_state, 

3997 parent_dict, 

3998 visited_states, 

3999 halt_on, 

4000 ) 

4001 ) 

4002 if queue: 

4003 visitables.append((queue, mpp, None, None)) 

4004 elif item_type is mpp: 

4005 ( 

4006 instance, 

4007 instance_mapper, 

4008 corresponding_state, 

4009 corresponding_dict, 

4010 ) = iterator.popleft() 

4011 yield ( 

4012 instance, 

4013 instance_mapper, 

4014 corresponding_state, 

4015 corresponding_dict, 

4016 ) 

4017 visitables.append( 

4018 ( 

4019 deque(instance_mapper._props.values()), 

4020 prp, 

4021 corresponding_state, 

4022 corresponding_dict, 

4023 ) 

4024 ) 

4025 

4026 @HasMemoized.memoized_attribute 

4027 def _compiled_cache(self): 

4028 return util.LRUCache(self._compiled_cache_size) 

4029 

4030 @HasMemoized.memoized_attribute 

4031 def _multiple_persistence_tables(self): 

4032 return len(self.tables) > 1 

4033 

4034 @HasMemoized.memoized_attribute 

4035 def _sorted_tables(self): 

4036 table_to_mapper: Dict[TableClause, Mapper[Any]] = {} 

4037 

4038 for mapper in self.base_mapper.self_and_descendants: 

4039 for t in mapper.tables: 

4040 table_to_mapper.setdefault(t, mapper) 

4041 

4042 extra_dependencies = [] 

4043 for table, mapper in table_to_mapper.items(): 

4044 super_ = mapper.inherits 

4045 if super_: 

4046 extra_dependencies.extend( 

4047 [(super_table, table) for super_table in super_.tables] 

4048 ) 

4049 

4050 def skip(fk): 

4051 # attempt to skip dependencies that are not 

4052 # significant to the inheritance chain 

4053 # for two tables that are related by inheritance. 

4054 # while that dependency may be important, it's technically 

4055 # not what we mean to sort on here. 

4056 parent = table_to_mapper.get(fk.parent.table) 

4057 dep = table_to_mapper.get(fk.column.table) 

4058 if ( 

4059 parent is not None 

4060 and dep is not None 

4061 and dep is not parent 

4062 and dep.inherit_condition is not None 

4063 ): 

4064 cols = set(sql_util._find_columns(dep.inherit_condition)) 

4065 if parent.inherit_condition is not None: 

4066 cols = cols.union( 

4067 sql_util._find_columns(parent.inherit_condition) 

4068 ) 

4069 return fk.parent not in cols and fk.column not in cols 

4070 else: 

4071 return fk.parent not in cols 

4072 return False 

4073 

4074 sorted_ = sql_util.sort_tables( 

4075 table_to_mapper, 

4076 skip_fn=skip, 

4077 extra_dependencies=extra_dependencies, 

4078 ) 

4079 

4080 ret = util.OrderedDict() 

4081 for t in sorted_: 

4082 ret[t] = table_to_mapper[t] 

4083 return ret 

4084 

4085 def _memo(self, key: Any, callable_: Callable[[], _T]) -> _T: 

4086 if key in self._memoized_values: 

4087 return cast(_T, self._memoized_values[key]) 

4088 else: 

4089 self._memoized_values[key] = value = callable_() 

4090 return value 

4091 

4092 @util.memoized_property 

4093 def _table_to_equated(self): 

4094 """memoized map of tables to collections of columns to be 

4095 synchronized upwards to the base mapper.""" 

4096 

4097 result: util.defaultdict[ 

4098 Table, 

4099 List[ 

4100 Tuple[ 

4101 Mapper[Any], 

4102 List[Tuple[ColumnElement[Any], ColumnElement[Any]]], 

4103 ] 

4104 ], 

4105 ] = util.defaultdict(list) 

4106 

4107 def set_union(x, y): 

4108 return x.union(y) 

4109 

4110 for table in self._sorted_tables: 

4111 cols = set(table.c) 

4112 

4113 for m in self.iterate_to_root(): 

4114 if m._inherits_equated_pairs and cols.intersection( 

4115 reduce( 

4116 set_union, 

4117 [l.proxy_set for l, r in m._inherits_equated_pairs], 

4118 ) 

4119 ): 

4120 result[table].append((m, m._inherits_equated_pairs)) 

4121 

4122 return result 

4123 

4124 

4125class _OptGetColumnsNotAvailable(Exception): 

4126 pass 

4127 

4128 

4129def configure_mappers() -> None: 

4130 """Initialize the inter-mapper relationships of all mappers that 

4131 have been constructed thus far across all :class:`_orm.registry` 

4132 collections. 

4133 

4134 The configure step is used to reconcile and initialize the 

4135 :func:`_orm.relationship` linkages between mapped classes, as well as to 

4136 invoke configuration events such as the 

4137 :meth:`_orm.MapperEvents.before_configured` and 

4138 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM 

4139 extensions or user-defined extension hooks. 

4140 

4141 Mapper configuration is normally invoked automatically, the first time 

4142 mappings from a particular :class:`_orm.registry` are used, as well as 

4143 whenever mappings are used and additional not-yet-configured mappers have 

4144 been constructed. The automatic configuration process however is local only 

4145 to the :class:`_orm.registry` involving the target mapper and any related 

4146 :class:`_orm.registry` objects which it may depend on; this is 

4147 equivalent to invoking the :meth:`_orm.registry.configure` method 

4148 on a particular :class:`_orm.registry`. 

4149 

4150 By contrast, the :func:`_orm.configure_mappers` function will invoke the 

4151 configuration process on all :class:`_orm.registry` objects that 

4152 exist in memory, and may be useful for scenarios where many individual 

4153 :class:`_orm.registry` objects that are nonetheless interrelated are 

4154 in use. 

4155 

4156 .. versionchanged:: 1.4 

4157 

4158 As of SQLAlchemy 1.4.0b2, this function works on a 

4159 per-:class:`_orm.registry` basis, locating all :class:`_orm.registry` 

4160 objects present and invoking the :meth:`_orm.registry.configure` method 

4161 on each. The :meth:`_orm.registry.configure` method may be preferred to 

4162 limit the configuration of mappers to those local to a particular 

4163 :class:`_orm.registry` and/or declarative base class. 

4164 

4165 Points at which automatic configuration is invoked include when a mapped 

4166 class is instantiated into an instance, as well as when ORM queries 

4167 are emitted using :meth:`.Session.query` or :meth:`_orm.Session.execute` 

4168 with an ORM-enabled statement. 

4169 

4170 The mapper configure process, whether invoked by 

4171 :func:`_orm.configure_mappers` or from :meth:`_orm.registry.configure`, 

4172 provides several event hooks that can be used to augment the mapper 

4173 configuration step. These hooks include: 

4174 

4175 * :meth:`.MapperEvents.before_configured` - called once before 

4176 :func:`.configure_mappers` or :meth:`_orm.registry.configure` does any 

4177 work; this can be used to establish additional options, properties, or 

4178 related mappings before the operation proceeds. 

4179 

4180 * :meth:`.MapperEvents.mapper_configured` - called as each individual 

4181 :class:`_orm.Mapper` is configured within the process; will include all 

4182 mapper state except for backrefs set up by other mappers that are still 

4183 to be configured. 

4184 

4185 * :meth:`.MapperEvents.after_configured` - called once after 

4186 :func:`.configure_mappers` or :meth:`_orm.registry.configure` is 

4187 complete; at this stage, all :class:`_orm.Mapper` objects that fall 

4188 within the scope of the configuration operation will be fully configured. 

4189 Note that the calling application may still have other mappings that 

4190 haven't been produced yet, such as if they are in modules as yet 

4191 unimported, and may also have mappings that are still to be configured, 

4192 if they are in other :class:`_orm.registry` collections not part of the 

4193 current scope of configuration. 

4194 

4195 """ 

4196 

4197 _configure_registries(_all_registries(), cascade=True) 

4198 

4199 

4200def _configure_registries( 

4201 registries: Set[_RegistryType], cascade: bool 

4202) -> None: 

4203 for reg in registries: 

4204 if reg._new_mappers: 

4205 break 

4206 else: 

4207 return 

4208 

4209 with _CONFIGURE_MUTEX: 

4210 global _already_compiling 

4211 if _already_compiling: 

4212 return 

4213 _already_compiling = True 

4214 try: 

4215 # double-check inside mutex 

4216 for reg in registries: 

4217 if reg._new_mappers: 

4218 break 

4219 else: 

4220 return 

4221 

4222 Mapper.dispatch._for_class(Mapper).before_configured() # type: ignore # noqa: E501 

4223 # initialize properties on all mappers 

4224 # note that _mapper_registry is unordered, which 

4225 # may randomly conceal/reveal issues related to 

4226 # the order of mapper compilation 

4227 

4228 _do_configure_registries(registries, cascade) 

4229 finally: 

4230 _already_compiling = False 

4231 Mapper.dispatch._for_class(Mapper).after_configured() # type: ignore 

4232 

4233 

4234@util.preload_module("sqlalchemy.orm.decl_api") 

4235def _do_configure_registries( 

4236 registries: Set[_RegistryType], cascade: bool 

4237) -> None: 

4238 registry = util.preloaded.orm_decl_api.registry 

4239 

4240 orig = set(registries) 

4241 

4242 for reg in registry._recurse_with_dependencies(registries): 

4243 has_skip = False 

4244 

4245 for mapper in reg._mappers_to_configure(): 

4246 run_configure = None 

4247 

4248 for fn in mapper.dispatch.before_mapper_configured: 

4249 run_configure = fn(mapper, mapper.class_) 

4250 if run_configure is EXT_SKIP: 

4251 has_skip = True 

4252 break 

4253 if run_configure is EXT_SKIP: 

4254 continue 

4255 

4256 if getattr(mapper, "_configure_failed", False): 

4257 e = sa_exc.InvalidRequestError( 

4258 "One or more mappers failed to initialize - " 

4259 "can't proceed with initialization of other " 

4260 "mappers. Triggering mapper: '%s'. " 

4261 "Original exception was: %s" 

4262 % (mapper, mapper._configure_failed) 

4263 ) 

4264 e._configure_failed = mapper._configure_failed # type: ignore 

4265 raise e 

4266 

4267 if not mapper.configured: 

4268 try: 

4269 mapper._post_configure_properties() 

4270 mapper._expire_memoizations() 

4271 mapper.dispatch.mapper_configured(mapper, mapper.class_) 

4272 except Exception: 

4273 exc = sys.exc_info()[1] 

4274 if not hasattr(exc, "_configure_failed"): 

4275 mapper._configure_failed = exc 

4276 raise 

4277 if not has_skip: 

4278 reg._new_mappers = False 

4279 

4280 if not cascade and reg._dependencies.difference(orig): 

4281 raise sa_exc.InvalidRequestError( 

4282 "configure was called with cascade=False but " 

4283 "additional registries remain" 

4284 ) 

4285 

4286 

4287@util.preload_module("sqlalchemy.orm.decl_api") 

4288def _dispose_registries(registries: Set[_RegistryType], cascade: bool) -> None: 

4289 registry = util.preloaded.orm_decl_api.registry 

4290 

4291 orig = set(registries) 

4292 

4293 for reg in registry._recurse_with_dependents(registries): 

4294 if not cascade and reg._dependents.difference(orig): 

4295 raise sa_exc.InvalidRequestError( 

4296 "Registry has dependent registries that are not disposed; " 

4297 "pass cascade=True to clear these also" 

4298 ) 

4299 

4300 while reg._managers: 

4301 try: 

4302 manager, _ = reg._managers.popitem() 

4303 except KeyError: 

4304 # guard against race between while and popitem 

4305 pass 

4306 else: 

4307 reg._dispose_manager_and_mapper(manager) 

4308 

4309 reg._non_primary_mappers.clear() 

4310 reg._dependents.clear() 

4311 for dep in reg._dependencies: 

4312 dep._dependents.discard(reg) 

4313 reg._dependencies.clear() 

4314 # this wasn't done in the 1.3 clear_mappers() and in fact it 

4315 # was a bug, as it could cause configure_mappers() to invoke 

4316 # the "before_configured" event even though mappers had all been 

4317 # disposed. 

4318 reg._new_mappers = False 

4319 

4320 

4321def reconstructor(fn: _Fn) -> _Fn: 

4322 """Decorate a method as the 'reconstructor' hook. 

4323 

4324 Designates a single method as the "reconstructor", an ``__init__``-like 

4325 method that will be called by the ORM after the instance has been 

4326 loaded from the database or otherwise reconstituted. 

4327 

4328 .. tip:: 

4329 

4330 The :func:`_orm.reconstructor` decorator makes use of the 

4331 :meth:`_orm.InstanceEvents.load` event hook, which can be 

4332 used directly. 

4333 

4334 The reconstructor will be invoked with no arguments. Scalar 

4335 (non-collection) database-mapped attributes of the instance will 

4336 be available for use within the function. Eagerly-loaded 

4337 collections are generally not yet available and will usually only 

4338 contain the first element. ORM state changes made to objects at 

4339 this stage will not be recorded for the next flush() operation, so 

4340 the activity within a reconstructor should be conservative. 

4341 

4342 .. seealso:: 

4343 

4344 :meth:`.InstanceEvents.load` 

4345 

4346 """ 

4347 fn.__sa_reconstructor__ = True # type: ignore[attr-defined] 

4348 return fn 

4349 

4350 

4351def validates( 

4352 *names: str, include_removes: bool = False, include_backrefs: bool = True 

4353) -> Callable[[_Fn], _Fn]: 

4354 r"""Decorate a method as a 'validator' for one or more named properties. 

4355 

4356 Designates a method as a validator, a method which receives the 

4357 name of the attribute as well as a value to be assigned, or in the 

4358 case of a collection, the value to be added to the collection. 

4359 The function can then raise validation exceptions to halt the 

4360 process from continuing (where Python's built-in ``ValueError`` 

4361 and ``AssertionError`` exceptions are reasonable choices), or can 

4362 modify or replace the value before proceeding. The function should 

4363 otherwise return the given value. 

4364 

4365 Note that a validator for a collection **cannot** issue a load of that 

4366 collection within the validation routine - this usage raises 

4367 an assertion to avoid recursion overflows. This is a reentrant 

4368 condition which is not supported. 

4369 

4370 :param \*names: list of attribute names to be validated. 

4371 :param include_removes: if True, "remove" events will be 

4372 sent as well - the validation function must accept an additional 

4373 argument "is_remove" which will be a boolean. 

4374 

4375 :param include_backrefs: defaults to ``True``; if ``False``, the 

4376 validation function will not emit if the originator is an attribute 

4377 event related via a backref. This can be used for bi-directional 

4378 :func:`.validates` usage where only one validator should emit per 

4379 attribute operation. 

4380 

4381 .. versionchanged:: 2.0.16 This parameter inadvertently defaulted to 

4382 ``False`` for releases 2.0.0 through 2.0.15. Its correct default 

4383 of ``True`` is restored in 2.0.16. 

4384 

4385 .. seealso:: 

4386 

4387 :ref:`simple_validators` - usage examples for :func:`.validates` 

4388 

4389 """ 

4390 

4391 def wrap(fn: _Fn) -> _Fn: 

4392 fn.__sa_validators__ = names # type: ignore[attr-defined] 

4393 fn.__sa_validation_opts__ = { # type: ignore[attr-defined] 

4394 "include_removes": include_removes, 

4395 "include_backrefs": include_backrefs, 

4396 } 

4397 return fn 

4398 

4399 return wrap 

4400 

4401 

4402def _event_on_load(state, ctx): 

4403 instrumenting_mapper = state.manager.mapper 

4404 

4405 if instrumenting_mapper._reconstructor: 

4406 instrumenting_mapper._reconstructor(state.obj()) 

4407 

4408 

4409def _event_on_init(state, args, kwargs): 

4410 """Run init_instance hooks. 

4411 

4412 This also includes mapper compilation, normally not needed 

4413 here but helps with some piecemeal configuration 

4414 scenarios (such as in the ORM tutorial). 

4415 

4416 """ 

4417 

4418 instrumenting_mapper = state.manager.mapper 

4419 if instrumenting_mapper: 

4420 instrumenting_mapper._check_configure() 

4421 if instrumenting_mapper._set_polymorphic_identity: 

4422 instrumenting_mapper._set_polymorphic_identity(state) 

4423 

4424 

4425class _ColumnMapping(Dict["ColumnElement[Any]", "MapperProperty[Any]"]): 

4426 """Error reporting helper for mapper._columntoproperty.""" 

4427 

4428 __slots__ = ("mapper",) 

4429 

4430 def __init__(self, mapper): 

4431 # TODO: weakref would be a good idea here 

4432 self.mapper = mapper 

4433 

4434 def __missing__(self, column): 

4435 prop = self.mapper._props.get(column) 

4436 if prop: 

4437 raise orm_exc.UnmappedColumnError( 

4438 "Column '%s.%s' is not available, due to " 

4439 "conflicting property '%s':%r" 

4440 % (column.table.name, column.name, column.key, prop) 

4441 ) 

4442 raise orm_exc.UnmappedColumnError( 

4443 "No column %s is configured on mapper %s..." 

4444 % (column, self.mapper) 

4445 )