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-2025 the SQLAlchemy authors and contributors 

3# <see AUTHORS file> 

4# 

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

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

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

8 

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

10 

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

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

13 

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

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

16 

17""" 

18from __future__ import annotations 

19 

20from collections import deque 

21from functools import reduce 

22from itertools import chain 

23import sys 

24import threading 

25from typing import Any 

26from typing import Callable 

27from typing import cast 

28from typing import Collection 

29from typing import Deque 

30from typing import Dict 

31from typing import FrozenSet 

32from typing import Generic 

33from typing import Iterable 

34from typing import Iterator 

35from typing import List 

36from typing import Mapping 

37from typing import Optional 

38from typing import Sequence 

39from typing import Set 

40from typing import Tuple 

41from typing import Type 

42from typing import TYPE_CHECKING 

43from typing import TypeVar 

44from typing import Union 

45import weakref 

46 

47from . import attributes 

48from . import exc as orm_exc 

49from . import instrumentation 

50from . import loading 

51from . import properties 

52from . import util as orm_util 

53from ._typing import _O 

54from .base import _class_to_mapper 

55from .base import _parse_mapper_argument 

56from .base import _state_mapper 

57from .base import PassiveFlag 

58from .base import state_str 

59from .interfaces import _MappedAttribute 

60from .interfaces import EXT_SKIP 

61from .interfaces import InspectionAttr 

62from .interfaces import MapperProperty 

63from .interfaces import ORMEntityColumnsClauseRole 

64from .interfaces import ORMFromClauseRole 

65from .interfaces import StrategizedProperty 

66from .path_registry import PathRegistry 

67from .. import event 

68from .. import exc as sa_exc 

69from .. import inspection 

70from .. import log 

71from .. import schema 

72from .. import sql 

73from .. import util 

74from ..event import dispatcher 

75from ..event import EventTarget 

76from ..sql import base as sql_base 

77from ..sql import coercions 

78from ..sql import expression 

79from ..sql import operators 

80from ..sql import roles 

81from ..sql import TableClause 

82from ..sql import util as sql_util 

83from ..sql import visitors 

84from ..sql.cache_key import MemoizedHasCacheKey 

85from ..sql.elements import KeyedColumnElement 

86from ..sql.schema import Column 

87from ..sql.schema import Table 

88from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

89from ..util import HasMemoized 

90from ..util import HasMemoized_ro_memoized_attribute 

91from ..util.typing import Literal 

92 

93if TYPE_CHECKING: 

94 from ._typing import _IdentityKeyType 

95 from ._typing import _InstanceDict 

96 from ._typing import _ORMColumnExprArgument 

97 from ._typing import _RegistryType 

98 from .decl_api import registry 

99 from .dependency import DependencyProcessor 

100 from .descriptor_props import CompositeProperty 

101 from .descriptor_props import SynonymProperty 

102 from .events import MapperEvents 

103 from .instrumentation import ClassManager 

104 from .path_registry import CachingEntityRegistry 

105 from .properties import ColumnProperty 

106 from .relationships import RelationshipProperty 

107 from .state import InstanceState 

108 from .util import ORMAdapter 

109 from ..engine import Row 

110 from ..engine import RowMapping 

111 from ..sql._typing import _ColumnExpressionArgument 

112 from ..sql._typing import _EquivalentColumnMap 

113 from ..sql.base import ReadOnlyColumnCollection 

114 from ..sql.elements import ColumnClause 

115 from ..sql.elements import ColumnElement 

116 from ..sql.selectable import FromClause 

117 from ..util import OrderedSet 

118 

119 

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

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

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

123 

124 

125_WithPolymorphicArg = Union[ 

126 Literal["*"], 

127 Tuple[ 

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

129 Optional["FromClause"], 

130 ], 

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

132] 

133 

134 

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

136 weakref.WeakKeyDictionary() 

137) 

138 

139 

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

141 with _CONFIGURE_MUTEX: 

142 return set(_mapper_registries) 

143 

144 

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

146 for reg in _all_registries(): 

147 yield from reg._mappers_to_configure() 

148 

149 

150_already_compiling = False 

151 

152 

153# a constant returned by _get_attr_by_column to indicate 

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

155# column 

156NO_ATTRIBUTE = util.symbol("NO_ATTRIBUTE") 

157 

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

159_CONFIGURE_MUTEX = threading.RLock() 

160 

161 

162@inspection._self_inspects 

163@log.class_logger 

164class Mapper( 

165 ORMFromClauseRole, 

166 ORMEntityColumnsClauseRole[_O], 

167 MemoizedHasCacheKey, 

168 InspectionAttr, 

169 log.Identified, 

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

171 EventTarget, 

172 Generic[_O], 

173): 

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

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

176 proceed. 

177 

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

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

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

181 :ref:`orm_mapping_classes_toplevel`. 

182 

183 """ 

184 

185 dispatch: dispatcher[Mapper[_O]] 

186 

187 _dispose_called = False 

188 _configure_failed: Any = False 

189 _ready_for_configure = False 

190 

191 @util.deprecated_params( 

192 non_primary=( 

193 "1.3", 

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

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

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

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

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

199 ), 

200 ) 

201 def __init__( 

202 self, 

203 class_: Type[_O], 

204 local_table: Optional[FromClause] = None, 

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

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

207 non_primary: bool = False, 

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

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

210 inherit_foreign_keys: Optional[ 

211 Sequence[_ORMColumnExprArgument[Any]] 

212 ] = None, 

213 always_refresh: bool = False, 

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

215 version_id_generator: Optional[ 

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

217 ] = None, 

218 polymorphic_on: Optional[ 

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

220 ] = None, 

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

222 polymorphic_identity: Optional[Any] = None, 

223 concrete: bool = False, 

224 with_polymorphic: Optional[_WithPolymorphicArg] = None, 

225 polymorphic_abstract: bool = False, 

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

227 allow_partial_pks: bool = True, 

228 batch: bool = True, 

229 column_prefix: Optional[str] = None, 

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

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

232 passive_updates: bool = True, 

233 passive_deletes: bool = False, 

234 confirm_deleted_rows: bool = True, 

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

236 legacy_is_orphan: bool = False, 

237 _compiled_cache_size: int = 100, 

238 ): 

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

240 

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

242 is normally invoked through the 

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

244 :ref:`Declarative <orm_declarative_mapping>` or 

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

246 

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

248 removed; for a classical mapping configuration, use the 

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

250 

251 Parameters documented below may be passed to either the 

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

253 ``__mapper_args__`` declarative class attribute described at 

254 :ref:`orm_declarative_mapper_options`. 

255 

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

257 this argument is automatically passed as the declared class 

258 itself. 

259 

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

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

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

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

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

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

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

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

268 present. 

269 

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

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

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

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

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

275 inheritance scheme which uses 

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

277 

278 .. versionadded:: 2.0 

279 

280 .. seealso:: 

281 

282 :ref:`orm_inheritance_abstract_poly` 

283 

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

285 class will overwrite all data within object instances that already 

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

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

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

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

290 

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

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

293 possibly existing within the database. This affects whether a 

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

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

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

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

298 

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

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

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

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

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

304 

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

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

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

308 has partial NULL values will not be emitted. 

309 

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

311 of multiple entities can be batched together for efficiency. 

312 Setting to False indicates 

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

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

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

316 in between individual row persistence operations. 

317 

318 :param column_prefix: A string which will be prepended 

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

320 objects are automatically assigned as attributes to the 

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

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

323 dictionary. 

324 

325 This parameter is typically useful with imperative mappings 

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

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

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

329 

330 class User(Base): 

331 __table__ = user_table 

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

333 

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

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

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

337 

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

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

340 approach to automating a naming scheme is to intercept the 

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

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

343 pattern. 

344 

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

346 table inheritance with its parent mapper. 

347 

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

349 

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

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

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

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

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

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

356 exception in a future release. 

357 

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

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

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

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

362 are needed immediately before the flush completes. 

363 

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

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

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

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

368 

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

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

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

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

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

374 attributes are not to be accessed in any case. 

375 

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

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

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

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

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

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

382 defaults will not be fetched. 

383 

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

385 :paramref:`.Mapper.eager_defaults` 

386 

387 .. seealso:: 

388 

389 :ref:`orm_server_defaults` 

390 

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

392 INSERTed at once using the 

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

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

395 feature to be very performant on supporting backends. 

396 

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

398 be excluded from mapping. 

399 

400 .. seealso:: 

401 

402 :ref:`include_exclude_cols` 

403 

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

405 names to map. 

406 

407 .. seealso:: 

408 

409 :ref:`include_exclude_cols` 

410 

411 :param inherits: A mapped class or the corresponding 

412 :class:`_orm.Mapper` 

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

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

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

416 is passed automatically as a result of the natural class 

417 hierarchy of the declared classes. 

418 

419 .. seealso:: 

420 

421 :ref:`inheritance_toplevel` 

422 

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

424 expression which will 

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

426 between the two tables. 

427 

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

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

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

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

432 

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

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

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

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

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

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

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

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

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

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

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

444 orphan object has been flushed yet or not. 

445 

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

447 for more detail on this change. 

448 

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

450 is in addition 

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

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

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

454 only. 

455 

456 .. seealso:: 

457 

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

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

460 

461 :param passive_deletes: Indicates DELETE behavior of foreign key 

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

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

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

465 on the superclass mapper. 

466 

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

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

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

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

471 superclass table, and not this table. 

472 

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

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

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

476 validate these attributes; note that the primary key columns 

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

478 the object as a whole. 

479 

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

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

482 to specify passive_deletes without this taking effect for 

483 all subclass mappers. 

484 

485 .. seealso:: 

486 

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

488 used with :func:`_orm.relationship` 

489 

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

491 CASCADE for joined-table inheritance mappers 

492 

493 :param passive_updates: Indicates UPDATE behavior of foreign key 

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

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

496 

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

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

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

500 on joined-table rows. 

501 

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

503 referential integrity and will not be issuing its own CASCADE 

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

505 emit an UPDATE statement for the dependent columns during a 

506 primary key change. 

507 

508 .. seealso:: 

509 

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

511 used with :func:`_orm.relationship` 

512 

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

514 CASCADE for joined-table inheritance mappers 

515 

516 :param polymorphic_load: Specifies "polymorphic loading" behavior 

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

518 table inheritance only). Valid values are: 

519 

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

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

522 in a SELECT query against the base. 

523 

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

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

526 the columns specific to this subclass. The SELECT uses 

527 IN to fetch multiple subclasses at once. 

528 

529 .. versionadded:: 1.2 

530 

531 .. seealso:: 

532 

533 :ref:`with_polymorphic_mapper_config` 

534 

535 :ref:`polymorphic_selectin` 

536 

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

538 SQL expression used to determine the target class for an 

539 incoming row, when inheriting classes are present. 

540 

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

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

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

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

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

546 

547 class Employee(Base): 

548 __tablename__ = "employee" 

549 

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

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

552 

553 __mapper_args__ = { 

554 "polymorphic_on": discriminator, 

555 "polymorphic_identity": "employee", 

556 } 

557 

558 It may also be specified 

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

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

561 approach:: 

562 

563 class Employee(Base): 

564 __tablename__ = "employee" 

565 

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

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

568 

569 __mapper_args__ = { 

570 "polymorphic_on": case( 

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

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

573 else_="employee", 

574 ), 

575 "polymorphic_identity": "employee", 

576 } 

577 

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

579 which is of particular use when using annotated column 

580 configurations:: 

581 

582 class Employee(Base): 

583 __tablename__ = "employee" 

584 

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

586 discriminator: Mapped[str] 

587 

588 __mapper_args__ = { 

589 "polymorphic_on": "discriminator", 

590 "polymorphic_identity": "employee", 

591 } 

592 

593 When setting ``polymorphic_on`` to reference an 

594 attribute or expression that's not present in the 

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

596 of the discriminator should be persisted to the database, 

597 the value of the 

598 discriminator is not automatically set on new 

599 instances; this must be handled by the user, 

600 either through manual means or via event listeners. 

601 A typical approach to establishing such a listener 

602 looks like:: 

603 

604 from sqlalchemy import event 

605 from sqlalchemy.orm import object_mapper 

606 

607 

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

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

610 mapper = object_mapper(instance) 

611 instance.discriminator = mapper.polymorphic_identity 

612 

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

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

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

616 in the database. 

617 

618 .. warning:: 

619 

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

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

622 columns are not yet supported. 

623 

624 .. seealso:: 

625 

626 :ref:`inheritance_toplevel` 

627 

628 :param polymorphic_identity: Specifies the value which 

629 identifies this particular class as returned by the column expression 

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

631 rows are received, the value corresponding to the 

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

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

634 reconstructed object. 

635 

636 .. seealso:: 

637 

638 :ref:`inheritance_toplevel` 

639 

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

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

642 persistence behavior of that attribute. Note that 

643 :class:`_schema.Column` 

644 objects present in 

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

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

647 When using Declarative, this argument is passed automatically, 

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

649 in the declared class body. 

650 

651 .. seealso:: 

652 

653 :ref:`orm_mapping_properties` - in the 

654 :ref:`orm_mapping_classes_toplevel` 

655 

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

657 objects, or alternatively string names of attribute names which 

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

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

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

661 can be overridden here. 

662 

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

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

665 

666 .. seealso:: 

667 

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

669 

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

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

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

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

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

675 version id, a 

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

677 thrown. 

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

679 unless ``version_id_generator`` specifies an alternative version 

680 generator. 

681 

682 .. seealso:: 

683 

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

685 and rationale. 

686 

687 :param version_id_generator: Define how new version ids should 

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

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

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

691 

692 def generate_version(version): 

693 return next_version 

694 

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

696 or programmatic versioning schemes outside of the version id 

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

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

699 of important points when using this option. 

700 

701 .. seealso:: 

702 

703 :ref:`custom_version_counter` 

704 

705 :ref:`server_side_version_counter` 

706 

707 

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

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

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

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

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

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

714 loaded immediately. The second tuple argument <selectable> 

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

716 classes. 

717 

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

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

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

721 indicating polymorphic loading styles. 

722 

723 .. seealso:: 

724 

725 :ref:`with_polymorphic_mapper_config` 

726 

727 """ 

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

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

730 self.class_.__module__, 

731 self.class_.__name__, 

732 ) 

733 

734 self._primary_key_argument = util.to_list(primary_key) 

735 self.non_primary = non_primary 

736 

737 self.always_refresh = always_refresh 

738 

739 if isinstance(version_id_col, MapperProperty): 

740 self.version_id_prop = version_id_col 

741 self.version_id_col = None 

742 else: 

743 self.version_id_col = ( 

744 coercions.expect( 

745 roles.ColumnArgumentOrKeyRole, 

746 version_id_col, 

747 argname="version_id_col", 

748 ) 

749 if version_id_col is not None 

750 else None 

751 ) 

752 

753 if version_id_generator is False: 

754 self.version_id_generator = False 

755 elif version_id_generator is None: 

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

757 else: 

758 self.version_id_generator = version_id_generator 

759 

760 self.concrete = concrete 

761 self.single = False 

762 

763 if inherits is not None: 

764 self.inherits = _parse_mapper_argument(inherits) 

765 else: 

766 self.inherits = None 

767 

768 if local_table is not None: 

769 self.local_table = coercions.expect( 

770 roles.StrictFromClauseRole, 

771 local_table, 

772 disable_inspection=True, 

773 argname="local_table", 

774 ) 

775 elif self.inherits: 

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

777 # .local_table need not be Optional 

778 self.local_table = self.inherits.local_table 

779 self.single = True 

780 else: 

781 raise sa_exc.ArgumentError( 

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

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

784 ) 

785 

786 if inherit_condition is not None: 

787 self.inherit_condition = coercions.expect( 

788 roles.OnClauseRole, inherit_condition 

789 ) 

790 else: 

791 self.inherit_condition = None 

792 

793 self.inherit_foreign_keys = inherit_foreign_keys 

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

795 self._delete_orphans = [] 

796 self.batch = batch 

797 self.eager_defaults = eager_defaults 

798 self.column_prefix = column_prefix 

799 

800 # interim - polymorphic_on is further refined in 

801 # _configure_polymorphic_setter 

802 self.polymorphic_on = ( 

803 coercions.expect( # type: ignore 

804 roles.ColumnArgumentOrKeyRole, 

805 polymorphic_on, 

806 argname="polymorphic_on", 

807 ) 

808 if polymorphic_on is not None 

809 else None 

810 ) 

811 self.polymorphic_abstract = polymorphic_abstract 

812 self._dependency_processors = [] 

813 self.validators = util.EMPTY_DICT 

814 self.passive_updates = passive_updates 

815 self.passive_deletes = passive_deletes 

816 self.legacy_is_orphan = legacy_is_orphan 

817 self._clause_adapter = None 

818 self._requires_row_aliasing = False 

819 self._inherits_equated_pairs = None 

820 self._memoized_values = {} 

821 self._compiled_cache_size = _compiled_cache_size 

822 self._reconstructor = None 

823 self.allow_partial_pks = allow_partial_pks 

824 

825 if self.inherits and not self.concrete: 

826 self.confirm_deleted_rows = False 

827 else: 

828 self.confirm_deleted_rows = confirm_deleted_rows 

829 

830 self._set_with_polymorphic(with_polymorphic) 

831 self.polymorphic_load = polymorphic_load 

832 

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

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

835 # the object instance for that row. 

836 self.polymorphic_identity = polymorphic_identity 

837 

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

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

840 # upon a select operation. 

841 if _polymorphic_map is None: 

842 self.polymorphic_map = {} 

843 else: 

844 self.polymorphic_map = _polymorphic_map 

845 

846 if include_properties is not None: 

847 self.include_properties = util.to_set(include_properties) 

848 else: 

849 self.include_properties = None 

850 if exclude_properties: 

851 self.exclude_properties = util.to_set(exclude_properties) 

852 else: 

853 self.exclude_properties = None 

854 

855 # prevent this mapper from being constructed 

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

857 # configure_mappers() until construction succeeds) 

858 with _CONFIGURE_MUTEX: 

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

860 class_, self 

861 ) 

862 self._configure_inheritance() 

863 self._configure_class_instrumentation() 

864 self._configure_properties() 

865 self._configure_polymorphic_setter() 

866 self._configure_pks() 

867 self.registry._flag_new_mapper(self) 

868 self._log("constructed") 

869 self._expire_memoizations() 

870 

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

872 

873 def _prefer_eager_defaults(self, dialect, table): 

874 if self.eager_defaults == "auto": 

875 if not table.implicit_returning: 

876 return False 

877 

878 return ( 

879 table in self._server_default_col_keys 

880 and dialect.insert_executemany_returning 

881 ) 

882 else: 

883 return self.eager_defaults 

884 

885 def _gen_cache_key(self, anon_map, bindparams): 

886 return (self,) 

887 

888 # ### BEGIN 

889 # ATTRIBUTE DECLARATIONS START HERE 

890 

891 is_mapper = True 

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

893 

894 represents_outer_join = False 

895 

896 registry: _RegistryType 

897 

898 @property 

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

900 """Part of the inspection API. 

901 

902 Returns self. 

903 

904 """ 

905 return self 

906 

907 @property 

908 def entity(self): 

909 r"""Part of the inspection API. 

910 

911 Returns self.class\_. 

912 

913 """ 

914 return self.class_ 

915 

916 class_: Type[_O] 

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

918 

919 _identity_class: Type[_O] 

920 

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

922 _dependency_processors: List[DependencyProcessor] 

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

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

925 _all_tables: Set[TableClause] 

926 _polymorphic_attr_key: Optional[str] 

927 

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

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

930 

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

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

933 

934 _columntoproperty: _ColumnMapping 

935 

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

937 _validate_polymorphic_identity: Optional[ 

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

939 ] 

940 

941 tables: Sequence[TableClause] 

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

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

944 is aware of. 

945 

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

947 :class:`_expression.Alias` 

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

949 :class:`_schema.Table` 

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

951 

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

953 Behavior is undefined if directly modified. 

954 

955 """ 

956 

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

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

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

960 

961 The dictionary contains string attribute names as keys 

962 mapped to the actual validation method. 

963 

964 """ 

965 

966 always_refresh: bool 

967 allow_partial_pks: bool 

968 version_id_col: Optional[ColumnElement[Any]] 

969 

970 with_polymorphic: Optional[ 

971 Tuple[ 

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

973 Optional[FromClause], 

974 ] 

975 ] 

976 

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

978 

979 local_table: FromClause 

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

981 :class:`_orm.Mapper` refers. 

982 

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

984 :class:`.FromClause`. 

985 

986 The "local" table is the 

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

988 managing from an attribute access and flush perspective. For 

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

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

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

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

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

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

995 

996 .. seealso:: 

997 

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

999 

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

1001 

1002 """ 

1003 

1004 persist_selectable: FromClause 

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

1006 is mapped. 

1007 

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

1009 :class:`.FromClause`. 

1010 

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

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

1013 represents the inheriting class hierarchy overall in an inheritance 

1014 scenario. 

1015 

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

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

1018 alternate subquery used for selecting columns. 

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

1020 will be written on a persist operation. 

1021 

1022 .. seealso:: 

1023 

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

1025 

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

1027 

1028 """ 

1029 

1030 inherits: Optional[Mapper[Any]] 

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

1032 inherits from, if any. 

1033 

1034 """ 

1035 

1036 inherit_condition: Optional[ColumnElement[bool]] 

1037 

1038 configured: bool = False 

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

1040 

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

1042 Behavior is undefined if directly modified. 

1043 

1044 .. seealso:: 

1045 

1046 :func:`.configure_mappers`. 

1047 

1048 """ 

1049 

1050 concrete: bool 

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

1052 inheritance mapper. 

1053 

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

1055 Behavior is undefined if directly modified. 

1056 

1057 """ 

1058 

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

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

1061 objects 

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

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

1064 

1065 This list is against the selectable in 

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

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

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

1069 :class:`_expression.Join`, the 

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

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

1072 

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

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

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

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

1077 

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

1079 Behavior is undefined if directly modified. 

1080 

1081 """ 

1082 

1083 class_manager: ClassManager[_O] 

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

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

1086 

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

1088 Behavior is undefined if directly modified. 

1089 

1090 """ 

1091 

1092 single: bool 

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

1094 inheritance mapper. 

1095 

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

1097 

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

1099 Behavior is undefined if directly modified. 

1100 

1101 """ 

1102 

1103 non_primary: bool 

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

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

1106 persistence management. 

1107 

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

1109 Behavior is undefined if directly modified. 

1110 

1111 """ 

1112 

1113 polymorphic_on: Optional[KeyedColumnElement[Any]] 

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

1115 ``polymorphic_on`` argument 

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

1117 

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

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

1120 :func:`.cast`. 

1121 

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

1123 Behavior is undefined if directly modified. 

1124 

1125 """ 

1126 

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

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

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

1130 

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

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

1133 

1134 An inheritance chain of mappers will all reference the same 

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

1136 result rows to target mappers. 

1137 

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

1139 Behavior is undefined if directly modified. 

1140 

1141 """ 

1142 

1143 polymorphic_identity: Optional[Any] 

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

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

1146 

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

1148 comparable to the type of column represented by 

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

1150 

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

1152 Behavior is undefined if directly modified. 

1153 

1154 """ 

1155 

1156 base_mapper: Mapper[Any] 

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

1158 

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

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

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

1162 objects in the inheritance chain. 

1163 

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

1165 Behavior is undefined if directly modified. 

1166 

1167 """ 

1168 

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

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

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

1172 

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

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

1175 except that only those columns included in 

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

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

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

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

1180 

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

1182 Behavior is undefined if directly modified. 

1183 

1184 """ 

1185 

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

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

1188 

1189 @util.non_memoized_property 

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

1191 def mapped_table(self): 

1192 return self.persist_selectable 

1193 

1194 @util.memoized_property 

1195 def _path_registry(self) -> CachingEntityRegistry: 

1196 return PathRegistry.per_mapper(self) 

1197 

1198 def _configure_inheritance(self): 

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

1200 being present.""" 

1201 

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

1203 self._inheriting_mappers = util.WeakSequence() 

1204 

1205 if self.inherits: 

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

1207 raise sa_exc.ArgumentError( 

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

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

1210 ) 

1211 

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

1213 

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

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

1216 raise sa_exc.ArgumentError( 

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

1218 "only allowed from a %s mapper" 

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

1220 ) 

1221 

1222 if self.single: 

1223 self.persist_selectable = self.inherits.persist_selectable 

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

1225 if self.concrete: 

1226 self.persist_selectable = self.local_table 

1227 for mapper in self.iterate_to_root(): 

1228 if mapper.polymorphic_on is not None: 

1229 mapper._requires_row_aliasing = True 

1230 else: 

1231 if self.inherit_condition is None: 

1232 # figure out inherit condition from our table to the 

1233 # immediate table of the inherited mapper, not its 

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

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

1236 try: 

1237 self.inherit_condition = sql_util.join_condition( 

1238 self.inherits.local_table, self.local_table 

1239 ) 

1240 except sa_exc.NoForeignKeysError as nfe: 

1241 assert self.inherits.local_table is not None 

1242 assert self.local_table is not None 

1243 raise sa_exc.NoForeignKeysError( 

1244 "Can't determine the inherit condition " 

1245 "between inherited table '%s' and " 

1246 "inheriting " 

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

1248 "foreign key relationships established. " 

1249 "Please ensure the inheriting table has " 

1250 "a foreign key relationship to the " 

1251 "inherited " 

1252 "table, or provide an " 

1253 "'on clause' using " 

1254 "the 'inherit_condition' mapper argument." 

1255 % ( 

1256 self.inherits.local_table.description, 

1257 self.local_table.description, 

1258 ) 

1259 ) from nfe 

1260 except sa_exc.AmbiguousForeignKeysError as afe: 

1261 assert self.inherits.local_table is not None 

1262 assert self.local_table is not None 

1263 raise sa_exc.AmbiguousForeignKeysError( 

1264 "Can't determine the inherit condition " 

1265 "between inherited table '%s' and " 

1266 "inheriting " 

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

1268 "foreign key relationship established. " 

1269 "Please specify the 'on clause' using " 

1270 "the 'inherit_condition' mapper argument." 

1271 % ( 

1272 self.inherits.local_table.description, 

1273 self.local_table.description, 

1274 ) 

1275 ) from afe 

1276 assert self.inherits.persist_selectable is not None 

1277 self.persist_selectable = sql.join( 

1278 self.inherits.persist_selectable, 

1279 self.local_table, 

1280 self.inherit_condition, 

1281 ) 

1282 

1283 fks = util.to_set(self.inherit_foreign_keys) 

1284 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 

1285 self.persist_selectable.onclause, 

1286 consider_as_foreign_keys=fks, 

1287 ) 

1288 else: 

1289 self.persist_selectable = self.local_table 

1290 

1291 if self.polymorphic_identity is None: 

1292 self._identity_class = self.class_ 

1293 

1294 if ( 

1295 not self.polymorphic_abstract 

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

1297 ): 

1298 util.warn( 

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

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

1301 f"'polymorphic_on' column of " 

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

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

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

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

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

1307 "class unmapped when using Declarative, set the " 

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

1309 ) 

1310 elif self.concrete: 

1311 self._identity_class = self.class_ 

1312 else: 

1313 self._identity_class = self.inherits._identity_class 

1314 

1315 if self.version_id_col is None: 

1316 self.version_id_col = self.inherits.version_id_col 

1317 self.version_id_generator = self.inherits.version_id_generator 

1318 elif ( 

1319 self.inherits.version_id_col is not None 

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

1321 ): 

1322 util.warn( 

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

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

1325 "the inherited versioning column. " 

1326 "version_id_col should only be specified on " 

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

1328 % ( 

1329 self.version_id_col.description, 

1330 self.inherits.version_id_col.description, 

1331 ) 

1332 ) 

1333 

1334 self.polymorphic_map = self.inherits.polymorphic_map 

1335 self.batch = self.inherits.batch 

1336 self.inherits._inheriting_mappers.append(self) 

1337 self.base_mapper = self.inherits.base_mapper 

1338 self.passive_updates = self.inherits.passive_updates 

1339 self.passive_deletes = ( 

1340 self.inherits.passive_deletes or self.passive_deletes 

1341 ) 

1342 self._all_tables = self.inherits._all_tables 

1343 

1344 if self.polymorphic_identity is not None: 

1345 if self.polymorphic_identity in self.polymorphic_map: 

1346 util.warn( 

1347 "Reassigning polymorphic association for identity %r " 

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

1349 "value for polymorphic_identity." 

1350 % ( 

1351 self.polymorphic_identity, 

1352 self.polymorphic_map[self.polymorphic_identity], 

1353 self, 

1354 self.polymorphic_identity, 

1355 ) 

1356 ) 

1357 self.polymorphic_map[self.polymorphic_identity] = self 

1358 

1359 if self.polymorphic_load and self.concrete: 

1360 raise sa_exc.ArgumentError( 

1361 "polymorphic_load is not currently supported " 

1362 "with concrete table inheritance" 

1363 ) 

1364 if self.polymorphic_load == "inline": 

1365 self.inherits._add_with_polymorphic_subclass(self) 

1366 elif self.polymorphic_load == "selectin": 

1367 pass 

1368 elif self.polymorphic_load is not None: 

1369 raise sa_exc.ArgumentError( 

1370 "unknown argument for polymorphic_load: %r" 

1371 % self.polymorphic_load 

1372 ) 

1373 

1374 else: 

1375 self._all_tables = set() 

1376 self.base_mapper = self 

1377 assert self.local_table is not None 

1378 self.persist_selectable = self.local_table 

1379 if self.polymorphic_identity is not None: 

1380 self.polymorphic_map[self.polymorphic_identity] = self 

1381 self._identity_class = self.class_ 

1382 

1383 if self.persist_selectable is None: 

1384 raise sa_exc.ArgumentError( 

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

1386 % self 

1387 ) 

1388 

1389 def _set_with_polymorphic( 

1390 self, with_polymorphic: Optional[_WithPolymorphicArg] 

1391 ) -> None: 

1392 if with_polymorphic == "*": 

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

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

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

1396 self.with_polymorphic = cast( 

1397 """Tuple[ 

1398 Union[ 

1399 Literal["*"], 

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

1401 ], 

1402 Optional["FromClause"], 

1403 ]""", 

1404 with_polymorphic, 

1405 ) 

1406 else: 

1407 self.with_polymorphic = (with_polymorphic, None) 

1408 elif with_polymorphic is not None: 

1409 raise sa_exc.ArgumentError( 

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

1411 ) 

1412 else: 

1413 self.with_polymorphic = None 

1414 

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

1416 self.with_polymorphic = ( 

1417 self.with_polymorphic[0], 

1418 coercions.expect( 

1419 roles.StrictFromClauseRole, 

1420 self.with_polymorphic[1], 

1421 allow_select=True, 

1422 ), 

1423 ) 

1424 

1425 if self.configured: 

1426 self._expire_memoizations() 

1427 

1428 def _add_with_polymorphic_subclass(self, mapper): 

1429 subcl = mapper.class_ 

1430 if self.with_polymorphic is None: 

1431 self._set_with_polymorphic((subcl,)) 

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

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

1434 self._set_with_polymorphic( 

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

1436 ) 

1437 

1438 def _set_concrete_base(self, mapper): 

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

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

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

1442 

1443 assert self.concrete 

1444 assert not self.inherits 

1445 assert isinstance(mapper, Mapper) 

1446 self.inherits = mapper 

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

1448 self.polymorphic_map = self.inherits.polymorphic_map 

1449 for mapper in self.iterate_to_root(): 

1450 if mapper.polymorphic_on is not None: 

1451 mapper._requires_row_aliasing = True 

1452 self.batch = self.inherits.batch 

1453 for mp in self.self_and_descendants: 

1454 mp.base_mapper = self.inherits.base_mapper 

1455 self.inherits._inheriting_mappers.append(self) 

1456 self.passive_updates = self.inherits.passive_updates 

1457 self._all_tables = self.inherits._all_tables 

1458 

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

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

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

1462 ): 

1463 self._adapt_inherited_property(key, prop, False) 

1464 

1465 def _set_polymorphic_on(self, polymorphic_on): 

1466 self.polymorphic_on = polymorphic_on 

1467 self._configure_polymorphic_setter(True) 

1468 

1469 def _configure_class_instrumentation(self): 

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

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

1472 given class and entity name. 

1473 

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

1475 name combination will return this mapper. Also decorate the 

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

1477 auto-session attachment logic. 

1478 

1479 """ 

1480 

1481 # we expect that declarative has applied the class manager 

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

1483 # this raises as of 2.0. 

1484 manager = attributes.opt_manager_of_class(self.class_) 

1485 

1486 if self.non_primary: 

1487 if not manager or not manager.is_mapped: 

1488 raise sa_exc.InvalidRequestError( 

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

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

1491 "Mapper." % self.class_ 

1492 ) 

1493 self.class_manager = manager 

1494 

1495 assert manager.registry is not None 

1496 self.registry = manager.registry 

1497 self._identity_class = manager.mapper._identity_class 

1498 manager.registry._add_non_primary_mapper(self) 

1499 return 

1500 

1501 if manager is None or not manager.registry: 

1502 raise sa_exc.InvalidRequestError( 

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

1504 "invoked directly outside of a declarative registry." 

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

1506 "function for a classical mapping." 

1507 ) 

1508 

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

1510 

1511 # this invokes the class_instrument event and sets up 

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

1513 # occur after the instrument_class event above. 

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

1515 # :( 

1516 

1517 manager = instrumentation.register_class( 

1518 self.class_, 

1519 mapper=self, 

1520 expired_attribute_loader=util.partial( 

1521 loading.load_scalar_attributes, self 

1522 ), 

1523 # finalize flag means instrument the __init__ method 

1524 # and call the class_instrument event 

1525 finalize=True, 

1526 ) 

1527 

1528 self.class_manager = manager 

1529 

1530 assert manager.registry is not None 

1531 self.registry = manager.registry 

1532 

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

1534 # e_name None or not. 

1535 if manager.mapper is None: 

1536 return 

1537 

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

1539 

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

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

1542 method = method._sa_original_init 

1543 if hasattr(method, "__func__"): 

1544 method = method.__func__ 

1545 if callable(method): 

1546 if hasattr(method, "__sa_reconstructor__"): 

1547 self._reconstructor = method 

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

1549 elif hasattr(method, "__sa_validators__"): 

1550 validation_opts = method.__sa_validation_opts__ 

1551 for name in method.__sa_validators__: 

1552 if name in self.validators: 

1553 raise sa_exc.InvalidRequestError( 

1554 "A validation function for mapped " 

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

1556 % (name, self) 

1557 ) 

1558 self.validators = self.validators.union( 

1559 {name: (method, validation_opts)} 

1560 ) 

1561 

1562 def _set_dispose_flags(self) -> None: 

1563 self.configured = True 

1564 self._ready_for_configure = True 

1565 self._dispose_called = True 

1566 

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

1568 

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

1570 try: 

1571 prop = self._props[key] 

1572 except KeyError as err: 

1573 raise sa_exc.ArgumentError( 

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

1575 "no attribute is mapped to this name." 

1576 ) from err 

1577 try: 

1578 expr = prop.expression 

1579 except AttributeError as ae: 

1580 raise sa_exc.ArgumentError( 

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

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

1583 ) from ae 

1584 if not isinstance(expr, Column): 

1585 raise sa_exc.ArgumentError( 

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

1587 "property does not refer to a single " 

1588 "mapped Column" 

1589 ) 

1590 return expr 

1591 

1592 def _configure_pks(self) -> None: 

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

1594 

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

1596 

1597 self._pks_by_table = {} 

1598 self._cols_by_table = {} 

1599 

1600 all_cols = util.column_set( 

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

1602 ) 

1603 

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

1605 

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

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

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

1609 # ordering is important since it determines the ordering of 

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

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

1612 fc.primary_key 

1613 ).intersection( 

1614 pk_cols 

1615 ) 

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

1617 all_cols 

1618 ) 

1619 

1620 if self._primary_key_argument: 

1621 coerced_pk_arg = [ 

1622 ( 

1623 self._str_arg_to_mapped_col("primary_key", c) 

1624 if isinstance(c, str) 

1625 else c 

1626 ) 

1627 for c in ( 

1628 coercions.expect( 

1629 roles.DDLConstraintColumnRole, 

1630 coerce_pk, 

1631 argname="primary_key", 

1632 ) 

1633 for coerce_pk in self._primary_key_argument 

1634 ) 

1635 ] 

1636 else: 

1637 coerced_pk_arg = None 

1638 

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

1640 # primary key mappings 

1641 if coerced_pk_arg: 

1642 for k in coerced_pk_arg: 

1643 if k.table not in self._pks_by_table: 

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

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

1646 

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

1648 elif ( 

1649 self.persist_selectable not in self._pks_by_table 

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

1651 ): 

1652 raise sa_exc.ArgumentError( 

1653 "Mapper %s could not assemble any primary " 

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

1655 % (self, self.persist_selectable.description) 

1656 ) 

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

1658 self.local_table, schema.Table 

1659 ): 

1660 util.warn( 

1661 "Could not assemble any primary " 

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

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

1664 % self.local_table.description 

1665 ) 

1666 

1667 if ( 

1668 self.inherits 

1669 and not self.concrete 

1670 and not self._primary_key_argument 

1671 ): 

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

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

1674 self.primary_key = self.inherits.primary_key 

1675 else: 

1676 # determine primary key from argument or persist_selectable pks 

1677 primary_key: Collection[ColumnElement[Any]] 

1678 

1679 if coerced_pk_arg: 

1680 primary_key = [ 

1681 cc if cc is not None else c 

1682 for cc, c in ( 

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

1684 for c in coerced_pk_arg 

1685 ) 

1686 ] 

1687 else: 

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

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

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

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

1692 primary_key = sql_util.reduce_columns( 

1693 self._pks_by_table[self.persist_selectable], 

1694 ignore_nonexistent_tables=True, 

1695 ) 

1696 

1697 if len(primary_key) == 0: 

1698 raise sa_exc.ArgumentError( 

1699 "Mapper %s could not assemble any primary " 

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

1701 % (self, self.persist_selectable.description) 

1702 ) 

1703 

1704 self.primary_key = tuple(primary_key) 

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

1706 

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

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

1709 self._readonly_props = { 

1710 self._columntoproperty[col] 

1711 for col in self._columntoproperty 

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

1713 and ( 

1714 not hasattr(col, "table") 

1715 or col.table not in self._cols_by_table 

1716 ) 

1717 } 

1718 

1719 def _configure_properties(self) -> None: 

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

1721 

1722 # object attribute names mapped to MapperProperty objects 

1723 self._props = util.OrderedDict() 

1724 

1725 # table columns mapped to MapperProperty 

1726 self._columntoproperty = _ColumnMapping(self) 

1727 

1728 explicit_col_props_by_column: Dict[ 

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

1730 ] = {} 

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

1732 

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

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

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

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

1737 if self._init_properties: 

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

1739 if not isinstance(prop_arg, MapperProperty): 

1740 possible_col_prop = self._make_prop_from_column( 

1741 key, prop_arg 

1742 ) 

1743 else: 

1744 possible_col_prop = prop_arg 

1745 

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

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

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

1749 # the Table. 

1750 

1751 _map_as_property_now = True 

1752 if isinstance(possible_col_prop, properties.ColumnProperty): 

1753 for given_col in possible_col_prop.columns: 

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

1755 _map_as_property_now = False 

1756 explicit_col_props_by_key[key] = possible_col_prop 

1757 explicit_col_props_by_column[given_col] = ( 

1758 key, 

1759 possible_col_prop, 

1760 ) 

1761 

1762 if _map_as_property_now: 

1763 self._configure_property( 

1764 key, 

1765 possible_col_prop, 

1766 init=False, 

1767 ) 

1768 

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

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

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

1772 if self.inherits: 

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

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

1775 continue 

1776 

1777 incoming_prop = explicit_col_props_by_key.get(key) 

1778 if incoming_prop: 

1779 new_prop = self._reconcile_prop_with_incoming_columns( 

1780 key, 

1781 inherited_prop, 

1782 warn_only=False, 

1783 incoming_prop=incoming_prop, 

1784 ) 

1785 explicit_col_props_by_key[key] = new_prop 

1786 

1787 for inc_col in incoming_prop.columns: 

1788 explicit_col_props_by_column[inc_col] = ( 

1789 key, 

1790 new_prop, 

1791 ) 

1792 elif key not in self._props: 

1793 self._adapt_inherited_property(key, inherited_prop, False) 

1794 

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

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

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

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

1799 # reconciliation against inherited columns occurs here also. 

1800 

1801 for column in self.persist_selectable.columns: 

1802 if column in explicit_col_props_by_column: 

1803 # column was explicitly passed to properties; configure 

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

1805 # Table / selectable 

1806 key, prop = explicit_col_props_by_column[column] 

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

1808 continue 

1809 

1810 elif column in self._columntoproperty: 

1811 continue 

1812 

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

1814 if self._should_exclude( 

1815 column.key, 

1816 column_key, 

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

1818 column=column, 

1819 ): 

1820 continue 

1821 

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

1823 # of the inheriting mapper 

1824 for mapper in self.iterate_to_root(): 

1825 if column in mapper._columntoproperty: 

1826 column_key = mapper._columntoproperty[column].key 

1827 

1828 self._configure_property( 

1829 column_key, 

1830 column, 

1831 init=False, 

1832 setparent=True, 

1833 ) 

1834 

1835 def _configure_polymorphic_setter(self, init=False): 

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

1837 'polymorphic_on' column, if applicable, and not 

1838 already generated by _configure_properties (which is typical). 

1839 

1840 Also create a setter function which will assign this 

1841 attribute to the value of the 'polymorphic_identity' 

1842 upon instance construction, also if applicable. This 

1843 routine will run when an instance is created. 

1844 

1845 """ 

1846 setter = False 

1847 polymorphic_key: Optional[str] = None 

1848 

1849 if self.polymorphic_on is not None: 

1850 setter = True 

1851 

1852 if isinstance(self.polymorphic_on, str): 

1853 # polymorphic_on specified as a string - link 

1854 # it to mapped ColumnProperty 

1855 try: 

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

1857 except KeyError as err: 

1858 raise sa_exc.ArgumentError( 

1859 "Can't determine polymorphic_on " 

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

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

1862 ) from err 

1863 

1864 if self.polymorphic_on in self._columntoproperty: 

1865 # polymorphic_on is a column that is already mapped 

1866 # to a ColumnProperty 

1867 prop = self._columntoproperty[self.polymorphic_on] 

1868 elif isinstance(self.polymorphic_on, MapperProperty): 

1869 # polymorphic_on is directly a MapperProperty, 

1870 # ensure it's a ColumnProperty 

1871 if not isinstance( 

1872 self.polymorphic_on, properties.ColumnProperty 

1873 ): 

1874 raise sa_exc.ArgumentError( 

1875 "Only direct column-mapped " 

1876 "property or SQL expression " 

1877 "can be passed for polymorphic_on" 

1878 ) 

1879 prop = self.polymorphic_on 

1880 else: 

1881 # polymorphic_on is a Column or SQL expression and 

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

1883 # only present in the with_polymorphic selectable or 

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

1885 # hope is compatible with this mapper's persist_selectable 

1886 col = self.persist_selectable.corresponding_column( 

1887 self.polymorphic_on 

1888 ) 

1889 if col is None: 

1890 # polymorphic_on doesn't derive from any 

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

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

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

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

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

1896 # represented somehow in either persist_selectable or 

1897 # with_polymorphic. Otherwise as of 0.7.4 we 

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

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

1900 setter = False 

1901 instrument = False 

1902 col = self.polymorphic_on 

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

1904 self.with_polymorphic is None 

1905 or self.with_polymorphic[1] is None 

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

1907 is None 

1908 ): 

1909 raise sa_exc.InvalidRequestError( 

1910 "Could not map polymorphic_on column " 

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

1912 "loads will not function properly" 

1913 % col.description 

1914 ) 

1915 else: 

1916 # column/expression that polymorphic_on derives from 

1917 # is present in our mapped table 

1918 # and is probably mapped, but polymorphic_on itself 

1919 # is not. This happens when 

1920 # the polymorphic_on is only directly present in the 

1921 # with_polymorphic selectable, as when use 

1922 # polymorphic_union. 

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

1924 instrument = True 

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

1926 if key: 

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

1928 raise sa_exc.InvalidRequestError( 

1929 "Cannot exclude or override the " 

1930 "discriminator column %r" % key 

1931 ) 

1932 else: 

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

1934 key = col.key 

1935 

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

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

1938 

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

1940 # column in the property 

1941 self.polymorphic_on = prop.columns[0] 

1942 polymorphic_key = prop.key 

1943 else: 

1944 # no polymorphic_on was set. 

1945 # check inheriting mappers for one. 

1946 for mapper in self.iterate_to_root(): 

1947 # determine if polymorphic_on of the parent 

1948 # should be propagated here. If the col 

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

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

1951 # inheritance), we can use it 

1952 if mapper.polymorphic_on is not None: 

1953 if self.persist_selectable is mapper.persist_selectable: 

1954 self.polymorphic_on = mapper.polymorphic_on 

1955 else: 

1956 self.polymorphic_on = ( 

1957 self.persist_selectable 

1958 ).corresponding_column(mapper.polymorphic_on) 

1959 # we can use the parent mapper's _set_polymorphic_identity 

1960 # directly; it ensures the polymorphic_identity of the 

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

1962 if self.polymorphic_on is not None: 

1963 self._set_polymorphic_identity = ( 

1964 mapper._set_polymorphic_identity 

1965 ) 

1966 self._polymorphic_attr_key = ( 

1967 mapper._polymorphic_attr_key 

1968 ) 

1969 self._validate_polymorphic_identity = ( 

1970 mapper._validate_polymorphic_identity 

1971 ) 

1972 else: 

1973 self._set_polymorphic_identity = None 

1974 self._polymorphic_attr_key = None 

1975 return 

1976 

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

1978 raise sa_exc.InvalidRequestError( 

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

1980 "on a mapper hierarchy which includes the " 

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

1982 ) 

1983 

1984 if setter: 

1985 

1986 def _set_polymorphic_identity(state): 

1987 dict_ = state.dict 

1988 # TODO: what happens if polymorphic_on column attribute name 

1989 # does not match .key? 

1990 

1991 polymorphic_identity = ( 

1992 state.manager.mapper.polymorphic_identity 

1993 ) 

1994 if ( 

1995 polymorphic_identity is None 

1996 and state.manager.mapper.polymorphic_abstract 

1997 ): 

1998 raise sa_exc.InvalidRequestError( 

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

2000 "mapper is marked polymorphic_abstract=True" 

2001 ) 

2002 

2003 state.get_impl(polymorphic_key).set( 

2004 state, 

2005 dict_, 

2006 polymorphic_identity, 

2007 None, 

2008 ) 

2009 

2010 self._polymorphic_attr_key = polymorphic_key 

2011 

2012 def _validate_polymorphic_identity(mapper, state, dict_): 

2013 if ( 

2014 polymorphic_key in dict_ 

2015 and dict_[polymorphic_key] 

2016 not in mapper._acceptable_polymorphic_identities 

2017 ): 

2018 util.warn_limited( 

2019 "Flushing object %s with " 

2020 "incompatible polymorphic identity %r; the " 

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

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

2023 ) 

2024 

2025 self._set_polymorphic_identity = _set_polymorphic_identity 

2026 self._validate_polymorphic_identity = ( 

2027 _validate_polymorphic_identity 

2028 ) 

2029 else: 

2030 self._polymorphic_attr_key = None 

2031 self._set_polymorphic_identity = None 

2032 

2033 _validate_polymorphic_identity = None 

2034 

2035 @HasMemoized.memoized_attribute 

2036 def _version_id_prop(self): 

2037 if self.version_id_col is not None: 

2038 return self._columntoproperty[self.version_id_col] 

2039 else: 

2040 return None 

2041 

2042 @HasMemoized.memoized_attribute 

2043 def _acceptable_polymorphic_identities(self): 

2044 identities = set() 

2045 

2046 stack = deque([self]) 

2047 while stack: 

2048 item = stack.popleft() 

2049 if item.persist_selectable is self.persist_selectable: 

2050 identities.add(item.polymorphic_identity) 

2051 stack.extend(item._inheriting_mappers) 

2052 

2053 return identities 

2054 

2055 @HasMemoized.memoized_attribute 

2056 def _prop_set(self): 

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

2058 

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

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

2061 descriptor_props = util.preloaded.orm_descriptor_props 

2062 

2063 if not self.concrete: 

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

2065 elif key not in self._props: 

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

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

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

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

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

2071 # descriptors that might have side effects when invoked. 

2072 implementing_attribute = self.class_manager._get_class_attr_mro( 

2073 key, prop 

2074 ) 

2075 if implementing_attribute is prop or ( 

2076 isinstance( 

2077 implementing_attribute, attributes.InstrumentedAttribute 

2078 ) 

2079 and implementing_attribute._parententity is prop.parent 

2080 ): 

2081 self._configure_property( 

2082 key, 

2083 descriptor_props.ConcreteInheritedProperty(), 

2084 init=init, 

2085 setparent=True, 

2086 ) 

2087 

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

2089 def _configure_property( 

2090 self, 

2091 key: str, 

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

2093 *, 

2094 init: bool = True, 

2095 setparent: bool = True, 

2096 warn_for_existing: bool = False, 

2097 ) -> MapperProperty[Any]: 

2098 descriptor_props = util.preloaded.orm_descriptor_props 

2099 self._log( 

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

2101 ) 

2102 

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

2104 # ensure a Column is turned into a ColumnProperty. 

2105 # see #12858 

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

2107 

2108 if not isinstance(prop_arg, MapperProperty): 

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

2110 key, prop_arg, early_setup 

2111 ) 

2112 else: 

2113 prop = prop_arg 

2114 

2115 if early_setup: 

2116 return prop 

2117 

2118 if isinstance(prop, properties.ColumnProperty): 

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

2120 

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

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

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

2124 if col is None and self.inherits: 

2125 path = [self] 

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

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

2128 if col is not None: 

2129 for m2 in path: 

2130 m2.persist_selectable._refresh_for_new_column(col) 

2131 col = self.persist_selectable.corresponding_column( 

2132 prop.columns[0] 

2133 ) 

2134 break 

2135 path.append(m) 

2136 

2137 # subquery expression, column not present in the mapped 

2138 # selectable. 

2139 if col is None: 

2140 col = prop.columns[0] 

2141 

2142 # column is coming in after _readonly_props was 

2143 # initialized; check for 'readonly' 

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

2145 not hasattr(col, "table") 

2146 or col.table not in self._cols_by_table 

2147 ): 

2148 self._readonly_props.add(prop) 

2149 

2150 else: 

2151 # if column is coming in after _cols_by_table was 

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

2153 if ( 

2154 hasattr(self, "_cols_by_table") 

2155 and col.table in self._cols_by_table 

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

2157 ): 

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

2159 

2160 # if this properties.ColumnProperty represents the "polymorphic 

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

2162 # columns in SELECT statements. 

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

2164 prop._is_polymorphic_discriminator = ( 

2165 col is self.polymorphic_on 

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

2167 ) 

2168 

2169 if isinstance(col, expression.Label): 

2170 # new in 1.4, get column property against expressions 

2171 # to be addressable in subqueries 

2172 col.key = col._tq_key_label = key 

2173 

2174 self.columns.add(col, key) 

2175 

2176 for col in prop.columns: 

2177 for proxy_col in col.proxy_set: 

2178 self._columntoproperty[proxy_col] = prop 

2179 

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

2181 util.warn( 

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

2183 "assigned to attribute " 

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

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

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

2187 ) 

2188 

2189 prop.key = key 

2190 

2191 if setparent: 

2192 prop.set_parent(self, init) 

2193 

2194 if key in self._props and getattr( 

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

2196 ): 

2197 syn = self._props[key]._mapped_by_synonym 

2198 raise sa_exc.ArgumentError( 

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

2200 "a ColumnProperty already exists keyed to the name " 

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

2202 ) 

2203 

2204 # replacement cases 

2205 

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

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

2208 if ( 

2209 key in self._props 

2210 and not isinstance( 

2211 self._props[key], descriptor_props.ConcreteInheritedProperty 

2212 ) 

2213 and not isinstance(prop, descriptor_props.SynonymProperty) 

2214 ): 

2215 if warn_for_existing: 

2216 util.warn_deprecated( 

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

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

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

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

2221 "release", 

2222 "2.0", 

2223 ) 

2224 oldprop = self._props[key] 

2225 self._path_registry.pop(oldprop, None) 

2226 

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

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

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

2230 # get replaced. 

2231 elif ( 

2232 warn_for_existing 

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

2234 and not isinstance(prop, descriptor_props.SynonymProperty) 

2235 and not isinstance( 

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

2237 descriptor_props.ConcreteInheritedProperty, 

2238 ) 

2239 ): 

2240 util.warn_deprecated( 

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

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

2243 "attribute of the same name. " 

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

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

2246 "release", 

2247 "2.0", 

2248 ) 

2249 

2250 self._props[key] = prop 

2251 

2252 if not self.non_primary: 

2253 prop.instrument_class(self) 

2254 

2255 for mapper in self._inheriting_mappers: 

2256 mapper._adapt_inherited_property(key, prop, init) 

2257 

2258 if init: 

2259 prop.init() 

2260 prop.post_instrument_class(self) 

2261 

2262 if self.configured: 

2263 self._expire_memoizations() 

2264 

2265 return prop 

2266 

2267 def _make_prop_from_column( 

2268 self, 

2269 key: str, 

2270 column: Union[ 

2271 Sequence[KeyedColumnElement[Any]], KeyedColumnElement[Any] 

2272 ], 

2273 ) -> ColumnProperty[Any]: 

2274 columns = util.to_list(column) 

2275 mapped_column = [] 

2276 for c in columns: 

2277 mc = self.persist_selectable.corresponding_column(c) 

2278 if mc is None: 

2279 mc = self.local_table.corresponding_column(c) 

2280 if mc is not None: 

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

2282 # mapped table, this corresponds to adding a 

2283 # column after the fact to the local table. 

2284 # [ticket:1523] 

2285 self.persist_selectable._refresh_for_new_column(mc) 

2286 mc = self.persist_selectable.corresponding_column(c) 

2287 if mc is None: 

2288 raise sa_exc.ArgumentError( 

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

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

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

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

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

2294 ) 

2295 mapped_column.append(mc) 

2296 return properties.ColumnProperty(*mapped_column) 

2297 

2298 def _reconcile_prop_with_incoming_columns( 

2299 self, 

2300 key: str, 

2301 existing_prop: MapperProperty[Any], 

2302 warn_only: bool, 

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

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

2305 ) -> ColumnProperty[Any]: 

2306 if incoming_prop and ( 

2307 self.concrete 

2308 or not isinstance(existing_prop, properties.ColumnProperty) 

2309 ): 

2310 return incoming_prop 

2311 

2312 existing_column = existing_prop.columns[0] 

2313 

2314 if incoming_prop and existing_column in incoming_prop.columns: 

2315 return incoming_prop 

2316 

2317 if incoming_prop is None: 

2318 assert single_column is not None 

2319 incoming_column = single_column 

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

2321 else: 

2322 assert single_column is None 

2323 incoming_column = incoming_prop.columns[0] 

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

2325 

2326 if ( 

2327 ( 

2328 not self._inherits_equated_pairs 

2329 or (equated_pair_key not in self._inherits_equated_pairs) 

2330 ) 

2331 and not existing_column.shares_lineage(incoming_column) 

2332 and existing_column is not self.version_id_col 

2333 and incoming_column is not self.version_id_col 

2334 ): 

2335 msg = ( 

2336 "Implicitly combining column %s with column " 

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

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

2339 "explicitly." 

2340 % ( 

2341 existing_prop.columns[-1], 

2342 incoming_column, 

2343 key, 

2344 ) 

2345 ) 

2346 if warn_only: 

2347 util.warn(msg) 

2348 else: 

2349 raise sa_exc.InvalidRequestError(msg) 

2350 

2351 # existing properties.ColumnProperty from an inheriting 

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

2353 # breakpoint() 

2354 new_prop = existing_prop.copy() 

2355 

2356 new_prop.columns.insert(0, incoming_column) 

2357 self._log( 

2358 "inserting column to existing list " 

2359 "in properties.ColumnProperty %s", 

2360 key, 

2361 ) 

2362 return new_prop # type: ignore 

2363 

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

2365 def _property_from_column( 

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

2367 ) -> ColumnProperty[Any]: 

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

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

2370 

2371 descriptor_props = util.preloaded.orm_descriptor_props 

2372 

2373 if early_setup: 

2374 prop = None 

2375 else: 

2376 prop = self._props.get(key) 

2377 

2378 if isinstance(prop, properties.ColumnProperty): 

2379 return self._reconcile_prop_with_incoming_columns( 

2380 key, 

2381 prop, 

2382 single_column=column, 

2383 warn_only=prop.parent is not self, 

2384 ) 

2385 elif prop is None or isinstance( 

2386 prop, descriptor_props.ConcreteInheritedProperty 

2387 ): 

2388 return self._make_prop_from_column(key, column) 

2389 else: 

2390 raise sa_exc.ArgumentError( 

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

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

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

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

2395 "to remove all awareness of the column entirely " 

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

2397 "use the 'include_properties' or 'exclude_properties' " 

2398 "mapper arguments to control specifically which table " 

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

2400 ) 

2401 

2402 @util.langhelpers.tag_method_for_warnings( 

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

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

2405 "operation.", 

2406 sa_exc.SAWarning, 

2407 ) 

2408 def _check_configure(self) -> None: 

2409 if self.registry._new_mappers: 

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

2411 

2412 def _post_configure_properties(self) -> None: 

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

2414 attached to this mapper. 

2415 

2416 This is a deferred configuration step which is intended 

2417 to execute once all mappers have been constructed. 

2418 

2419 """ 

2420 

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

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

2423 for key, prop in l: 

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

2425 

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

2427 prop.init() 

2428 

2429 if prop._configure_finished: 

2430 prop.post_instrument_class(self) 

2431 

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

2433 self.configured = True 

2434 

2435 def add_properties(self, dict_of_properties): 

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

2437 using `add_property`. 

2438 

2439 """ 

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

2441 self.add_property(key, value) 

2442 

2443 def add_property( 

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

2445 ) -> None: 

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

2447 

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

2449 property to the initial properties dictionary sent to the 

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

2451 the given MapperProperty is configured immediately. 

2452 

2453 """ 

2454 prop = self._configure_property( 

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

2456 ) 

2457 assert isinstance(prop, MapperProperty) 

2458 self._init_properties[key] = prop 

2459 

2460 def _expire_memoizations(self) -> None: 

2461 for mapper in self.iterate_to_root(): 

2462 mapper._reset_memoizations() 

2463 

2464 @property 

2465 def _log_desc(self) -> str: 

2466 return ( 

2467 "(" 

2468 + self.class_.__name__ 

2469 + "|" 

2470 + ( 

2471 self.local_table is not None 

2472 and self.local_table.description 

2473 or str(self.local_table) 

2474 ) 

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

2476 + ")" 

2477 ) 

2478 

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

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

2481 

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

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

2484 

2485 def __repr__(self) -> str: 

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

2487 

2488 def __str__(self) -> str: 

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

2490 self.class_.__name__, 

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

2492 ( 

2493 self.local_table.description 

2494 if self.local_table is not None 

2495 else self.persist_selectable.description 

2496 ), 

2497 ) 

2498 

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

2500 orphan_possible = False 

2501 for mapper in self.iterate_to_root(): 

2502 for key, cls in mapper._delete_orphans: 

2503 orphan_possible = True 

2504 

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

2506 state, key, optimistic=state.has_identity 

2507 ) 

2508 

2509 if self.legacy_is_orphan and has_parent: 

2510 return False 

2511 elif not self.legacy_is_orphan and not has_parent: 

2512 return True 

2513 

2514 if self.legacy_is_orphan: 

2515 return orphan_possible 

2516 else: 

2517 return False 

2518 

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

2520 return key in self._props 

2521 

2522 def get_property( 

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

2524 ) -> MapperProperty[Any]: 

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

2526 

2527 if _configure_mappers: 

2528 self._check_configure() 

2529 

2530 try: 

2531 return self._props[key] 

2532 except KeyError as err: 

2533 raise sa_exc.InvalidRequestError( 

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

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

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

2537 ) from err 

2538 

2539 def get_property_by_column( 

2540 self, column: ColumnElement[_T] 

2541 ) -> MapperProperty[_T]: 

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

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

2544 

2545 return self._columntoproperty[column] 

2546 

2547 @property 

2548 def iterate_properties(self): 

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

2550 

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

2552 

2553 def _mappers_from_spec( 

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

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

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

2557 represents. 

2558 

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

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

2561 

2562 """ 

2563 if spec == "*": 

2564 mappers = list(self.self_and_descendants) 

2565 elif spec: 

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

2567 for m in util.to_list(spec): 

2568 m = _class_to_mapper(m) 

2569 if not m.isa(self): 

2570 raise sa_exc.InvalidRequestError( 

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

2572 ) 

2573 

2574 if selectable is None: 

2575 mapper_set.update(m.iterate_to_root()) 

2576 else: 

2577 mapper_set.add(m) 

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

2579 else: 

2580 mappers = [] 

2581 

2582 if selectable is not None: 

2583 tables = set( 

2584 sql_util.find_tables(selectable, include_aliases=True) 

2585 ) 

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

2587 return mappers 

2588 

2589 def _selectable_from_mappers( 

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

2591 ) -> FromClause: 

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

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

2594 mapped tables. 

2595 

2596 """ 

2597 from_obj = self.persist_selectable 

2598 for m in mappers: 

2599 if m is self: 

2600 continue 

2601 if m.concrete: 

2602 raise sa_exc.InvalidRequestError( 

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

2604 "when concrete-inheriting mappers are used." 

2605 ) 

2606 elif not m.single: 

2607 if innerjoin: 

2608 from_obj = from_obj.join( 

2609 m.local_table, m.inherit_condition 

2610 ) 

2611 else: 

2612 from_obj = from_obj.outerjoin( 

2613 m.local_table, m.inherit_condition 

2614 ) 

2615 

2616 return from_obj 

2617 

2618 @HasMemoized.memoized_attribute 

2619 def _version_id_has_server_side_value(self) -> bool: 

2620 vid_col = self.version_id_col 

2621 

2622 if vid_col is None: 

2623 return False 

2624 

2625 elif not isinstance(vid_col, Column): 

2626 return True 

2627 else: 

2628 return vid_col.server_default is not None or ( 

2629 vid_col.default is not None 

2630 and ( 

2631 not vid_col.default.is_scalar 

2632 and not vid_col.default.is_callable 

2633 ) 

2634 ) 

2635 

2636 @HasMemoized.memoized_attribute 

2637 def _single_table_criterion(self): 

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

2639 return self.polymorphic_on._annotate( 

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

2641 ).in_( 

2642 [ 

2643 m.polymorphic_identity 

2644 for m in self.self_and_descendants 

2645 if not m.polymorphic_abstract 

2646 ] 

2647 ) 

2648 else: 

2649 return None 

2650 

2651 @HasMemoized.memoized_attribute 

2652 def _has_aliased_polymorphic_fromclause(self): 

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

2654 like a subquery. 

2655 

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

2657 if this is present. 

2658 

2659 """ 

2660 return self.with_polymorphic and isinstance( 

2661 self.with_polymorphic[1], 

2662 expression.AliasedReturnsRows, 

2663 ) 

2664 

2665 @HasMemoized.memoized_attribute 

2666 def _should_select_with_poly_adapter(self): 

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

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

2669 rows for mapped classes and subclasses against this Mapper. 

2670 

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

2672 for this condition. 

2673 

2674 """ 

2675 

2676 # this has been simplified as of #8456. 

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

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

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

2680 # 

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

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

2683 # flattened JOIN for with_polymorphic.) 

2684 # 

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

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

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

2688 # some kind or polymorphic_union. 

2689 # 

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

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

2692 # on it (such as test_join_from_polymorphic_explicit_aliased_three). 

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

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

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

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

2697 # legacy case we should probably disable. 

2698 # 

2699 # 

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

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

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

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

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

2705 # polymorphic base class. 

2706 # 

2707 return ( 

2708 self._has_aliased_polymorphic_fromclause 

2709 or self._requires_row_aliasing 

2710 or (self.base_mapper._has_aliased_polymorphic_fromclause) 

2711 or self.base_mapper._requires_row_aliasing 

2712 ) 

2713 

2714 @HasMemoized.memoized_attribute 

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

2716 self._check_configure() 

2717 

2718 if not self.with_polymorphic: 

2719 return [] 

2720 return self._mappers_from_spec(*self.with_polymorphic) 

2721 

2722 @HasMemoized.memoized_attribute 

2723 def _post_inspect(self): 

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

2725 

2726 E.g. when Query calls: 

2727 

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

2729 

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

2731 

2732 """ 

2733 self._check_configure() 

2734 

2735 @HasMemoized_ro_memoized_attribute 

2736 def _with_polymorphic_selectable(self) -> FromClause: 

2737 if not self.with_polymorphic: 

2738 return self.persist_selectable 

2739 

2740 spec, selectable = self.with_polymorphic 

2741 if selectable is not None: 

2742 return selectable 

2743 else: 

2744 return self._selectable_from_mappers( 

2745 self._mappers_from_spec(spec, selectable), False 

2746 ) 

2747 

2748 with_polymorphic_mappers = _with_polymorphic_mappers 

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

2750 default "polymorphic" query. 

2751 

2752 """ 

2753 

2754 @HasMemoized_ro_memoized_attribute 

2755 def _insert_cols_evaluating_none(self): 

2756 return { 

2757 table: frozenset( 

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

2759 ) 

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

2761 } 

2762 

2763 @HasMemoized.memoized_attribute 

2764 def _insert_cols_as_none(self): 

2765 return { 

2766 table: frozenset( 

2767 col.key 

2768 for col in columns 

2769 if not col.primary_key 

2770 and not col.server_default 

2771 and not col.default 

2772 and not col.type.should_evaluate_none 

2773 ) 

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

2775 } 

2776 

2777 @HasMemoized.memoized_attribute 

2778 def _propkey_to_col(self): 

2779 return { 

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

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

2782 } 

2783 

2784 @HasMemoized.memoized_attribute 

2785 def _pk_keys_by_table(self): 

2786 return { 

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

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

2789 } 

2790 

2791 @HasMemoized.memoized_attribute 

2792 def _pk_attr_keys_by_table(self): 

2793 return { 

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

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

2796 } 

2797 

2798 @HasMemoized.memoized_attribute 

2799 def _server_default_cols( 

2800 self, 

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

2802 return { 

2803 table: frozenset( 

2804 [ 

2805 col 

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

2807 if col.server_default is not None 

2808 or ( 

2809 col.default is not None 

2810 and col.default.is_clause_element 

2811 ) 

2812 ] 

2813 ) 

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

2815 } 

2816 

2817 @HasMemoized.memoized_attribute 

2818 def _server_onupdate_default_cols( 

2819 self, 

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

2821 return { 

2822 table: frozenset( 

2823 [ 

2824 col 

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

2826 if col.server_onupdate is not None 

2827 or ( 

2828 col.onupdate is not None 

2829 and col.onupdate.is_clause_element 

2830 ) 

2831 ] 

2832 ) 

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

2834 } 

2835 

2836 @HasMemoized.memoized_attribute 

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

2838 return { 

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

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

2841 } 

2842 

2843 @HasMemoized.memoized_attribute 

2844 def _server_onupdate_default_col_keys( 

2845 self, 

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

2847 return { 

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

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

2850 } 

2851 

2852 @HasMemoized.memoized_attribute 

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

2854 result: Set[str] = set() 

2855 

2856 col_to_property = self._columntoproperty 

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

2858 result.update( 

2859 col_to_property[col].key 

2860 for col in columns.intersection(col_to_property) 

2861 ) 

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

2863 result.update( 

2864 col_to_property[col].key 

2865 for col in columns.intersection(col_to_property) 

2866 ) 

2867 return result 

2868 

2869 @HasMemoized.memoized_instancemethod 

2870 def __clause_element__(self): 

2871 annotations: Dict[str, Any] = { 

2872 "entity_namespace": self, 

2873 "parententity": self, 

2874 "parentmapper": self, 

2875 } 

2876 if self.persist_selectable is not self.local_table: 

2877 # joined table inheritance, with polymorphic selectable, 

2878 # etc. 

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

2880 { 

2881 "entity_namespace": self, 

2882 "parententity": self, 

2883 "parentmapper": self, 

2884 } 

2885 )._set_propagate_attrs( 

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

2887 ) 

2888 

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

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

2891 ) 

2892 

2893 @util.memoized_property 

2894 def select_identity_token(self): 

2895 return ( 

2896 expression.null() 

2897 ._annotate( 

2898 { 

2899 "entity_namespace": self, 

2900 "parententity": self, 

2901 "parentmapper": self, 

2902 "identity_token": True, 

2903 } 

2904 ) 

2905 ._set_propagate_attrs( 

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

2907 ) 

2908 ) 

2909 

2910 @property 

2911 def selectable(self) -> FromClause: 

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

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

2914 

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

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

2917 full "polymorphic" selectable is returned. 

2918 

2919 """ 

2920 return self._with_polymorphic_selectable 

2921 

2922 def _with_polymorphic_args( 

2923 self, 

2924 spec: Any = None, 

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

2926 innerjoin: bool = False, 

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

2928 if selectable not in (None, False): 

2929 selectable = coercions.expect( 

2930 roles.StrictFromClauseRole, selectable, allow_select=True 

2931 ) 

2932 

2933 if self.with_polymorphic: 

2934 if not spec: 

2935 spec = self.with_polymorphic[0] 

2936 if selectable is False: 

2937 selectable = self.with_polymorphic[1] 

2938 elif selectable is False: 

2939 selectable = None 

2940 mappers = self._mappers_from_spec(spec, selectable) 

2941 if selectable is not None: 

2942 return mappers, selectable 

2943 else: 

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

2945 

2946 @HasMemoized.memoized_attribute 

2947 def _polymorphic_properties(self): 

2948 return list( 

2949 self._iterate_polymorphic_properties( 

2950 self._with_polymorphic_mappers 

2951 ) 

2952 ) 

2953 

2954 @property 

2955 def _all_column_expressions(self): 

2956 poly_properties = self._polymorphic_properties 

2957 adapter = self._polymorphic_adapter 

2958 

2959 return [ 

2960 adapter.columns[c] if adapter else c 

2961 for prop in poly_properties 

2962 if isinstance(prop, properties.ColumnProperty) 

2963 and prop._renders_in_subqueries 

2964 for c in prop.columns 

2965 ] 

2966 

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

2968 if polymorphic_mappers: 

2969 poly_properties = self._iterate_polymorphic_properties( 

2970 polymorphic_mappers 

2971 ) 

2972 else: 

2973 poly_properties = self._polymorphic_properties 

2974 

2975 return [ 

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

2977 for prop in poly_properties 

2978 if isinstance(prop, properties.ColumnProperty) 

2979 ] 

2980 

2981 @HasMemoized.memoized_attribute 

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

2983 if self._has_aliased_polymorphic_fromclause: 

2984 return orm_util.ORMAdapter( 

2985 orm_util._TraceAdaptRole.MAPPER_POLYMORPHIC_ADAPTER, 

2986 self, 

2987 selectable=self.selectable, 

2988 equivalents=self._equivalent_columns, 

2989 limit_on_entity=False, 

2990 ) 

2991 else: 

2992 return None 

2993 

2994 def _iterate_polymorphic_properties(self, mappers=None): 

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

2996 a SELECT.""" 

2997 if mappers is None: 

2998 mappers = self._with_polymorphic_mappers 

2999 

3000 if not mappers: 

3001 for c in self.iterate_properties: 

3002 yield c 

3003 else: 

3004 # in the polymorphic case, filter out discriminator columns 

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

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

3007 for c in util.unique_list( 

3008 chain( 

3009 *[ 

3010 list(mapper.iterate_properties) 

3011 for mapper in [self] + mappers 

3012 ] 

3013 ) 

3014 ): 

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

3016 self.polymorphic_on is None 

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

3018 ): 

3019 continue 

3020 yield c 

3021 

3022 @HasMemoized.memoized_attribute 

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

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

3025 associated this mapper. 

3026 

3027 This is an object that provides each property based on 

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

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

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

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

3032 column. The namespace object can also be iterated, 

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

3034 

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

3036 of this attribute which limit the types of properties 

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

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

3039 

3040 .. warning:: 

3041 

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

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

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

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

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

3047 accessing attributes dynamically, favor using the dict-access 

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

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

3050 

3051 .. seealso:: 

3052 

3053 :attr:`_orm.Mapper.all_orm_descriptors` 

3054 

3055 """ 

3056 

3057 self._check_configure() 

3058 return util.ReadOnlyProperties(self._props) 

3059 

3060 @HasMemoized.memoized_attribute 

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

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

3063 with the mapped class. 

3064 

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

3066 associated with the mapped class or its superclasses. 

3067 

3068 This namespace includes attributes that are mapped to the class 

3069 as well as attributes declared by extension modules. 

3070 It includes any Python descriptor type that inherits from 

3071 :class:`.InspectionAttr`. This includes 

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

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

3074 :class:`.AssociationProxy`. 

3075 

3076 To distinguish between mapped attributes and extension attributes, 

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

3078 to a constant that distinguishes between different extension types. 

3079 

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

3081 

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

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

3084 

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

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

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

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

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

3090 or the mapper. 

3091 

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

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

3094 class in which it first appeared. 

3095 

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

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

3098 

3099 .. versionchanged:: 1.3.19 ensured deterministic ordering for 

3100 :meth:`_orm.Mapper.all_orm_descriptors`. 

3101 

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

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

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

3105 referring to the collection of mapped properties via 

3106 :attr:`_orm.Mapper.attrs`. 

3107 

3108 .. warning:: 

3109 

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

3111 accessor namespace is an 

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

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

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

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

3116 accessing attributes dynamically, favor using the dict-access 

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

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

3119 collisions. 

3120 

3121 .. seealso:: 

3122 

3123 :attr:`_orm.Mapper.attrs` 

3124 

3125 """ 

3126 return util.ReadOnlyProperties( 

3127 dict(self.class_manager._all_sqla_attributes()) 

3128 ) 

3129 

3130 @HasMemoized.memoized_attribute 

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

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

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

3134 all synonyms that refer to primary key columns 

3135 

3136 """ 

3137 descriptor_props = util.preloaded.orm_descriptor_props 

3138 

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

3140 

3141 return { 

3142 syn.key: syn.name 

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

3144 if isinstance(syn, descriptor_props.SynonymProperty) 

3145 and syn.name in pk_keys 

3146 } 

3147 

3148 @HasMemoized.memoized_attribute 

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

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

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

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

3153 

3154 .. seealso:: 

3155 

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

3157 :class:`.MapperProperty` 

3158 objects. 

3159 

3160 """ 

3161 descriptor_props = util.preloaded.orm_descriptor_props 

3162 

3163 return self._filter_properties(descriptor_props.SynonymProperty) 

3164 

3165 @property 

3166 def entity_namespace(self): 

3167 return self.class_ 

3168 

3169 @HasMemoized.memoized_attribute 

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

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

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

3173 

3174 .. seealso:: 

3175 

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

3177 :class:`.MapperProperty` 

3178 objects. 

3179 

3180 """ 

3181 return self._filter_properties(properties.ColumnProperty) 

3182 

3183 @HasMemoized.memoized_attribute 

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

3185 def relationships( 

3186 self, 

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

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

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

3190 

3191 .. warning:: 

3192 

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

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

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

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

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

3198 accessing attributes dynamically, favor using the dict-access 

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

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

3201 collisions. 

3202 

3203 .. seealso:: 

3204 

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

3206 :class:`.MapperProperty` 

3207 objects. 

3208 

3209 """ 

3210 return self._filter_properties( 

3211 util.preloaded.orm_relationships.RelationshipProperty 

3212 ) 

3213 

3214 @HasMemoized.memoized_attribute 

3215 @util.preload_module("sqlalchemy.orm.descriptor_props") 

3216 def composites(self) -> util.ReadOnlyProperties[CompositeProperty[Any]]: 

3217 """Return a namespace of all :class:`.Composite` 

3218 properties maintained by this :class:`_orm.Mapper`. 

3219 

3220 .. seealso:: 

3221 

3222 :attr:`_orm.Mapper.attrs` - namespace of all 

3223 :class:`.MapperProperty` 

3224 objects. 

3225 

3226 """ 

3227 return self._filter_properties( 

3228 util.preloaded.orm_descriptor_props.CompositeProperty 

3229 ) 

3230 

3231 def _filter_properties( 

3232 self, type_: Type[_MP] 

3233 ) -> util.ReadOnlyProperties[_MP]: 

3234 self._check_configure() 

3235 return util.ReadOnlyProperties( 

3236 util.OrderedDict( 

3237 (k, v) for k, v in self._props.items() if isinstance(v, type_) 

3238 ) 

3239 ) 

3240 

3241 @HasMemoized.memoized_attribute 

3242 def _get_clause(self): 

3243 """create a "get clause" based on the primary key. this is used 

3244 by query.get() and many-to-one lazyloads to load this item 

3245 by primary key. 

3246 

3247 """ 

3248 params = [ 

3249 ( 

3250 primary_key, 

3251 sql.bindparam("pk_%d" % idx, type_=primary_key.type), 

3252 ) 

3253 for idx, primary_key in enumerate(self.primary_key, 1) 

3254 ] 

3255 return ( 

3256 sql.and_(*[k == v for (k, v) in params]), 

3257 util.column_dict(params), 

3258 ) 

3259 

3260 @HasMemoized.memoized_attribute 

3261 def _equivalent_columns(self) -> _EquivalentColumnMap: 

3262 """Create a map of all equivalent columns, based on 

3263 the determination of column pairs that are equated to 

3264 one another based on inherit condition. This is designed 

3265 to work with the queries that util.polymorphic_union 

3266 comes up with, which often don't include the columns from 

3267 the base table directly (including the subclass table columns 

3268 only). 

3269 

3270 The resulting structure is a dictionary of columns mapped 

3271 to lists of equivalent columns, e.g.:: 

3272 

3273 {tablea.col1: {tableb.col1, tablec.col1}, tablea.col2: {tabled.col2}} 

3274 

3275 """ # noqa: E501 

3276 result: _EquivalentColumnMap = {} 

3277 

3278 def visit_binary(binary): 

3279 if binary.operator == operators.eq: 

3280 if binary.left in result: 

3281 result[binary.left].add(binary.right) 

3282 else: 

3283 result[binary.left] = {binary.right} 

3284 if binary.right in result: 

3285 result[binary.right].add(binary.left) 

3286 else: 

3287 result[binary.right] = {binary.left} 

3288 

3289 for mapper in self.base_mapper.self_and_descendants: 

3290 if mapper.inherit_condition is not None: 

3291 visitors.traverse( 

3292 mapper.inherit_condition, {}, {"binary": visit_binary} 

3293 ) 

3294 

3295 return result 

3296 

3297 def _is_userland_descriptor(self, assigned_name: str, obj: Any) -> bool: 

3298 if isinstance( 

3299 obj, 

3300 ( 

3301 _MappedAttribute, 

3302 instrumentation.ClassManager, 

3303 expression.ColumnElement, 

3304 ), 

3305 ): 

3306 return False 

3307 else: 

3308 return assigned_name not in self._dataclass_fields 

3309 

3310 @HasMemoized.memoized_attribute 

3311 def _dataclass_fields(self): 

3312 return [f.name for f in util.dataclass_fields(self.class_)] 

3313 

3314 def _should_exclude(self, name, assigned_name, local, column): 

3315 """determine whether a particular property should be implicitly 

3316 present on the class. 

3317 

3318 This occurs when properties are propagated from an inherited class, or 

3319 are applied from the columns present in the mapped table. 

3320 

3321 """ 

3322 

3323 if column is not None and sql_base._never_select_column(column): 

3324 return True 

3325 

3326 # check for class-bound attributes and/or descriptors, 

3327 # either local or from an inherited class 

3328 # ignore dataclass field default values 

3329 if local: 

3330 if self.class_.__dict__.get( 

3331 assigned_name, None 

3332 ) is not None and self._is_userland_descriptor( 

3333 assigned_name, self.class_.__dict__[assigned_name] 

3334 ): 

3335 return True 

3336 else: 

3337 attr = self.class_manager._get_class_attr_mro(assigned_name, None) 

3338 if attr is not None and self._is_userland_descriptor( 

3339 assigned_name, attr 

3340 ): 

3341 return True 

3342 

3343 if ( 

3344 self.include_properties is not None 

3345 and name not in self.include_properties 

3346 and (column is None or column not in self.include_properties) 

3347 ): 

3348 self._log("not including property %s" % (name)) 

3349 return True 

3350 

3351 if self.exclude_properties is not None and ( 

3352 name in self.exclude_properties 

3353 or (column is not None and column in self.exclude_properties) 

3354 ): 

3355 self._log("excluding property %s" % (name)) 

3356 return True 

3357 

3358 return False 

3359 

3360 def common_parent(self, other: Mapper[Any]) -> bool: 

3361 """Return true if the given mapper shares a 

3362 common inherited parent as this mapper.""" 

3363 

3364 return self.base_mapper is other.base_mapper 

3365 

3366 def is_sibling(self, other: Mapper[Any]) -> bool: 

3367 """return true if the other mapper is an inheriting sibling to this 

3368 one. common parent but different branch 

3369 

3370 """ 

3371 return ( 

3372 self.base_mapper is other.base_mapper 

3373 and not self.isa(other) 

3374 and not other.isa(self) 

3375 ) 

3376 

3377 def _canload( 

3378 self, state: InstanceState[Any], allow_subtypes: bool 

3379 ) -> bool: 

3380 s = self.primary_mapper() 

3381 if self.polymorphic_on is not None or allow_subtypes: 

3382 return _state_mapper(state).isa(s) 

3383 else: 

3384 return _state_mapper(state) is s 

3385 

3386 def isa(self, other: Mapper[Any]) -> bool: 

3387 """Return True if the this mapper inherits from the given mapper.""" 

3388 

3389 m: Optional[Mapper[Any]] = self 

3390 while m and m is not other: 

3391 m = m.inherits 

3392 return bool(m) 

3393 

3394 def iterate_to_root(self) -> Iterator[Mapper[Any]]: 

3395 m: Optional[Mapper[Any]] = self 

3396 while m: 

3397 yield m 

3398 m = m.inherits 

3399 

3400 @HasMemoized.memoized_attribute 

3401 def self_and_descendants(self) -> Sequence[Mapper[Any]]: 

3402 """The collection including this mapper and all descendant mappers. 

3403 

3404 This includes not just the immediately inheriting mappers but 

3405 all their inheriting mappers as well. 

3406 

3407 """ 

3408 descendants = [] 

3409 stack = deque([self]) 

3410 while stack: 

3411 item = stack.popleft() 

3412 descendants.append(item) 

3413 stack.extend(item._inheriting_mappers) 

3414 return util.WeakSequence(descendants) 

3415 

3416 def polymorphic_iterator(self) -> Iterator[Mapper[Any]]: 

3417 """Iterate through the collection including this mapper and 

3418 all descendant mappers. 

3419 

3420 This includes not just the immediately inheriting mappers but 

3421 all their inheriting mappers as well. 

3422 

3423 To iterate through an entire hierarchy, use 

3424 ``mapper.base_mapper.polymorphic_iterator()``. 

3425 

3426 """ 

3427 return iter(self.self_and_descendants) 

3428 

3429 def primary_mapper(self) -> Mapper[Any]: 

3430 """Return the primary mapper corresponding to this mapper's class key 

3431 (class).""" 

3432 

3433 return self.class_manager.mapper 

3434 

3435 @property 

3436 def primary_base_mapper(self) -> Mapper[Any]: 

3437 return self.class_manager.mapper.base_mapper 

3438 

3439 def _result_has_identity_key(self, result, adapter=None): 

3440 pk_cols: Sequence[ColumnElement[Any]] 

3441 if adapter is not None: 

3442 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3443 else: 

3444 pk_cols = self.primary_key 

3445 rk = result.keys() 

3446 for col in pk_cols: 

3447 if col not in rk: 

3448 return False 

3449 else: 

3450 return True 

3451 

3452 def identity_key_from_row( 

3453 self, 

3454 row: Union[Row[Any], RowMapping], 

3455 identity_token: Optional[Any] = None, 

3456 adapter: Optional[ORMAdapter] = None, 

3457 ) -> _IdentityKeyType[_O]: 

3458 """Return an identity-map key for use in storing/retrieving an 

3459 item from the identity map. 

3460 

3461 :param row: A :class:`.Row` or :class:`.RowMapping` produced from a 

3462 result set that selected from the ORM mapped primary key columns. 

3463 

3464 .. versionchanged:: 2.0 

3465 :class:`.Row` or :class:`.RowMapping` are accepted 

3466 for the "row" argument 

3467 

3468 """ 

3469 pk_cols: Sequence[ColumnElement[Any]] 

3470 if adapter is not None: 

3471 pk_cols = [adapter.columns[c] for c in self.primary_key] 

3472 else: 

3473 pk_cols = self.primary_key 

3474 

3475 mapping: RowMapping 

3476 if hasattr(row, "_mapping"): 

3477 mapping = row._mapping 

3478 else: 

3479 mapping = row # type: ignore[assignment] 

3480 

3481 return ( 

3482 self._identity_class, 

3483 tuple(mapping[column] for column in pk_cols), 

3484 identity_token, 

3485 ) 

3486 

3487 def identity_key_from_primary_key( 

3488 self, 

3489 primary_key: Tuple[Any, ...], 

3490 identity_token: Optional[Any] = None, 

3491 ) -> _IdentityKeyType[_O]: 

3492 """Return an identity-map key for use in storing/retrieving an 

3493 item from an identity map. 

3494 

3495 :param primary_key: A list of values indicating the identifier. 

3496 

3497 """ 

3498 return ( 

3499 self._identity_class, 

3500 tuple(primary_key), 

3501 identity_token, 

3502 ) 

3503 

3504 def identity_key_from_instance(self, instance: _O) -> _IdentityKeyType[_O]: 

3505 """Return the identity key for the given instance, based on 

3506 its primary key attributes. 

3507 

3508 If the instance's state is expired, calling this method 

3509 will result in a database check to see if the object has been deleted. 

3510 If the row no longer exists, 

3511 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3512 

3513 This value is typically also found on the instance state under the 

3514 attribute name `key`. 

3515 

3516 """ 

3517 state = attributes.instance_state(instance) 

3518 return self._identity_key_from_state(state, PassiveFlag.PASSIVE_OFF) 

3519 

3520 def _identity_key_from_state( 

3521 self, 

3522 state: InstanceState[_O], 

3523 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3524 ) -> _IdentityKeyType[_O]: 

3525 dict_ = state.dict 

3526 manager = state.manager 

3527 return ( 

3528 self._identity_class, 

3529 tuple( 

3530 [ 

3531 manager[prop.key].impl.get(state, dict_, passive) 

3532 for prop in self._identity_key_props 

3533 ] 

3534 ), 

3535 state.identity_token, 

3536 ) 

3537 

3538 def primary_key_from_instance(self, instance: _O) -> Tuple[Any, ...]: 

3539 """Return the list of primary key values for the given 

3540 instance. 

3541 

3542 If the instance's state is expired, calling this method 

3543 will result in a database check to see if the object has been deleted. 

3544 If the row no longer exists, 

3545 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3546 

3547 """ 

3548 state = attributes.instance_state(instance) 

3549 identity_key = self._identity_key_from_state( 

3550 state, PassiveFlag.PASSIVE_OFF 

3551 ) 

3552 return identity_key[1] 

3553 

3554 @HasMemoized.memoized_attribute 

3555 def _persistent_sortkey_fn(self): 

3556 key_fns = [col.type.sort_key_function for col in self.primary_key] 

3557 

3558 if set(key_fns).difference([None]): 

3559 

3560 def key(state): 

3561 return tuple( 

3562 key_fn(val) if key_fn is not None else val 

3563 for key_fn, val in zip(key_fns, state.key[1]) 

3564 ) 

3565 

3566 else: 

3567 

3568 def key(state): 

3569 return state.key[1] 

3570 

3571 return key 

3572 

3573 @HasMemoized.memoized_attribute 

3574 def _identity_key_props(self): 

3575 return [self._columntoproperty[col] for col in self.primary_key] 

3576 

3577 @HasMemoized.memoized_attribute 

3578 def _all_pk_cols(self): 

3579 collection: Set[ColumnClause[Any]] = set() 

3580 for table in self.tables: 

3581 collection.update(self._pks_by_table[table]) 

3582 return collection 

3583 

3584 @HasMemoized.memoized_attribute 

3585 def _should_undefer_in_wildcard(self): 

3586 cols: Set[ColumnElement[Any]] = set(self.primary_key) 

3587 if self.polymorphic_on is not None: 

3588 cols.add(self.polymorphic_on) 

3589 return cols 

3590 

3591 @HasMemoized.memoized_attribute 

3592 def _primary_key_propkeys(self): 

3593 return {self._columntoproperty[col].key for col in self._all_pk_cols} 

3594 

3595 def _get_state_attr_by_column( 

3596 self, 

3597 state: InstanceState[_O], 

3598 dict_: _InstanceDict, 

3599 column: ColumnElement[Any], 

3600 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3601 ) -> Any: 

3602 prop = self._columntoproperty[column] 

3603 return state.manager[prop.key].impl.get(state, dict_, passive=passive) 

3604 

3605 def _set_committed_state_attr_by_column(self, state, dict_, column, value): 

3606 prop = self._columntoproperty[column] 

3607 state.manager[prop.key].impl.set_committed_value(state, dict_, value) 

3608 

3609 def _set_state_attr_by_column(self, state, dict_, column, value): 

3610 prop = self._columntoproperty[column] 

3611 state.manager[prop.key].impl.set(state, dict_, value, None) 

3612 

3613 def _get_committed_attr_by_column(self, obj, column): 

3614 state = attributes.instance_state(obj) 

3615 dict_ = attributes.instance_dict(obj) 

3616 return self._get_committed_state_attr_by_column( 

3617 state, dict_, column, passive=PassiveFlag.PASSIVE_OFF 

3618 ) 

3619 

3620 def _get_committed_state_attr_by_column( 

3621 self, state, dict_, column, passive=PassiveFlag.PASSIVE_RETURN_NO_VALUE 

3622 ): 

3623 prop = self._columntoproperty[column] 

3624 return state.manager[prop.key].impl.get_committed_value( 

3625 state, dict_, passive=passive 

3626 ) 

3627 

3628 def _optimized_get_statement(self, state, attribute_names): 

3629 """assemble a WHERE clause which retrieves a given state by primary 

3630 key, using a minimized set of tables. 

3631 

3632 Applies to a joined-table inheritance mapper where the 

3633 requested attribute names are only present on joined tables, 

3634 not the base table. The WHERE clause attempts to include 

3635 only those tables to minimize joins. 

3636 

3637 """ 

3638 props = self._props 

3639 

3640 col_attribute_names = set(attribute_names).intersection( 

3641 state.mapper.column_attrs.keys() 

3642 ) 

3643 tables: Set[FromClause] = set( 

3644 chain( 

3645 *[ 

3646 sql_util.find_tables(c, check_columns=True) 

3647 for key in col_attribute_names 

3648 for c in props[key].columns 

3649 ] 

3650 ) 

3651 ) 

3652 

3653 if self.base_mapper.local_table in tables: 

3654 return None 

3655 

3656 def visit_binary(binary): 

3657 leftcol = binary.left 

3658 rightcol = binary.right 

3659 if leftcol is None or rightcol is None: 

3660 return 

3661 

3662 if leftcol.table not in tables: 

3663 leftval = self._get_committed_state_attr_by_column( 

3664 state, 

3665 state.dict, 

3666 leftcol, 

3667 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3668 ) 

3669 if leftval in orm_util._none_set: 

3670 raise _OptGetColumnsNotAvailable() 

3671 binary.left = sql.bindparam( 

3672 None, leftval, type_=binary.right.type 

3673 ) 

3674 elif rightcol.table not in tables: 

3675 rightval = self._get_committed_state_attr_by_column( 

3676 state, 

3677 state.dict, 

3678 rightcol, 

3679 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3680 ) 

3681 if rightval in orm_util._none_set: 

3682 raise _OptGetColumnsNotAvailable() 

3683 binary.right = sql.bindparam( 

3684 None, rightval, type_=binary.right.type 

3685 ) 

3686 

3687 allconds: List[ColumnElement[bool]] = [] 

3688 

3689 start = False 

3690 

3691 # as of #7507, from the lowest base table on upwards, 

3692 # we include all intermediary tables. 

3693 

3694 for mapper in reversed(list(self.iterate_to_root())): 

3695 if mapper.local_table in tables: 

3696 start = True 

3697 elif not isinstance(mapper.local_table, expression.TableClause): 

3698 return None 

3699 if start and not mapper.single: 

3700 assert mapper.inherits 

3701 assert not mapper.concrete 

3702 assert mapper.inherit_condition is not None 

3703 allconds.append(mapper.inherit_condition) 

3704 tables.add(mapper.local_table) 

3705 

3706 # only the bottom table needs its criteria to be altered to fit 

3707 # the primary key ident - the rest of the tables upwards to the 

3708 # descendant-most class should all be present and joined to each 

3709 # other. 

3710 try: 

3711 _traversed = visitors.cloned_traverse( 

3712 allconds[0], {}, {"binary": visit_binary} 

3713 ) 

3714 except _OptGetColumnsNotAvailable: 

3715 return None 

3716 else: 

3717 allconds[0] = _traversed 

3718 

3719 cond = sql.and_(*allconds) 

3720 

3721 cols = [] 

3722 for key in col_attribute_names: 

3723 cols.extend(props[key].columns) 

3724 return ( 

3725 sql.select(*cols) 

3726 .where(cond) 

3727 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 

3728 ) 

3729 

3730 def _iterate_to_target_viawpoly(self, mapper): 

3731 if self.isa(mapper): 

3732 prev = self 

3733 for m in self.iterate_to_root(): 

3734 yield m 

3735 

3736 if m is not prev and prev not in m._with_polymorphic_mappers: 

3737 break 

3738 

3739 prev = m 

3740 if m is mapper: 

3741 break 

3742 

3743 @HasMemoized.memoized_attribute 

3744 def _would_selectinload_combinations_cache(self): 

3745 return {} 

3746 

3747 def _would_selectin_load_only_from_given_mapper(self, super_mapper): 

3748 """return True if this mapper would "selectin" polymorphic load based 

3749 on the given super mapper, and not from a setting from a subclass. 

3750 

3751 given:: 

3752 

3753 class A: ... 

3754 

3755 

3756 class B(A): 

3757 __mapper_args__ = {"polymorphic_load": "selectin"} 

3758 

3759 

3760 class C(B): ... 

3761 

3762 

3763 class D(B): 

3764 __mapper_args__ = {"polymorphic_load": "selectin"} 

3765 

3766 ``inspect(C)._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3767 returns True, because C does selectin loading because of B's setting. 

3768 

3769 OTOH, ``inspect(D) 

3770 ._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3771 returns False, because D does selectin loading because of its own 

3772 setting; when we are doing a selectin poly load from B, we want to 

3773 filter out D because it would already have its own selectin poly load 

3774 set up separately. 

3775 

3776 Added as part of #9373. 

3777 

3778 """ 

3779 cache = self._would_selectinload_combinations_cache 

3780 

3781 try: 

3782 return cache[super_mapper] 

3783 except KeyError: 

3784 pass 

3785 

3786 # assert that given object is a supermapper, meaning we already 

3787 # strong reference it directly or indirectly. this allows us 

3788 # to not worry that we are creating new strongrefs to unrelated 

3789 # mappers or other objects. 

3790 assert self.isa(super_mapper) 

3791 

3792 mapper = super_mapper 

3793 for m in self._iterate_to_target_viawpoly(mapper): 

3794 if m.polymorphic_load == "selectin": 

3795 retval = m is super_mapper 

3796 break 

3797 else: 

3798 retval = False 

3799 

3800 cache[super_mapper] = retval 

3801 return retval 

3802 

3803 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 

3804 if not enabled_via_opt: 

3805 # common case, takes place for all polymorphic loads 

3806 mapper = polymorphic_from 

3807 for m in self._iterate_to_target_viawpoly(mapper): 

3808 if m.polymorphic_load == "selectin": 

3809 return m 

3810 else: 

3811 # uncommon case, selectin load options were used 

3812 enabled_via_opt = set(enabled_via_opt) 

3813 enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 

3814 for entity in enabled_via_opt.union([polymorphic_from]): 

3815 mapper = entity.mapper 

3816 for m in self._iterate_to_target_viawpoly(mapper): 

3817 if ( 

3818 m.polymorphic_load == "selectin" 

3819 or m in enabled_via_opt_mappers 

3820 ): 

3821 return enabled_via_opt_mappers.get(m, m) 

3822 

3823 return None 

3824 

3825 @util.preload_module("sqlalchemy.orm.strategy_options") 

3826 def _subclass_load_via_in(self, entity, polymorphic_from): 

3827 """Assemble a that can load the columns local to 

3828 this subclass as a SELECT with IN. 

3829 

3830 """ 

3831 

3832 strategy_options = util.preloaded.orm_strategy_options 

3833 

3834 assert self.inherits 

3835 

3836 if self.polymorphic_on is not None: 

3837 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 

3838 keep_props = set([polymorphic_prop] + self._identity_key_props) 

3839 else: 

3840 keep_props = set(self._identity_key_props) 

3841 

3842 disable_opt = strategy_options.Load(entity) 

3843 enable_opt = strategy_options.Load(entity) 

3844 

3845 classes_to_include = {self} 

3846 m: Optional[Mapper[Any]] = self.inherits 

3847 while ( 

3848 m is not None 

3849 and m is not polymorphic_from 

3850 and m.polymorphic_load == "selectin" 

3851 ): 

3852 classes_to_include.add(m) 

3853 m = m.inherits 

3854 

3855 for prop in self.column_attrs + self.relationships: 

3856 # skip prop keys that are not instrumented on the mapped class. 

3857 # this is primarily the "_sa_polymorphic_on" property that gets 

3858 # created for an ad-hoc polymorphic_on SQL expression, issue #8704 

3859 if prop.key not in self.class_manager: 

3860 continue 

3861 

3862 if prop.parent in classes_to_include or prop in keep_props: 

3863 # "enable" options, to turn on the properties that we want to 

3864 # load by default (subject to options from the query) 

3865 if not isinstance(prop, StrategizedProperty): 

3866 continue 

3867 

3868 enable_opt = enable_opt._set_generic_strategy( 

3869 # convert string name to an attribute before passing 

3870 # to loader strategy. note this must be in terms 

3871 # of given entity, such as AliasedClass, etc. 

3872 (getattr(entity.entity_namespace, prop.key),), 

3873 dict(prop.strategy_key), 

3874 _reconcile_to_other=True, 

3875 ) 

3876 else: 

3877 # "disable" options, to turn off the properties from the 

3878 # superclass that we *don't* want to load, applied after 

3879 # the options from the query to override them 

3880 disable_opt = disable_opt._set_generic_strategy( 

3881 # convert string name to an attribute before passing 

3882 # to loader strategy. note this must be in terms 

3883 # of given entity, such as AliasedClass, etc. 

3884 (getattr(entity.entity_namespace, prop.key),), 

3885 {"do_nothing": True}, 

3886 _reconcile_to_other=False, 

3887 ) 

3888 

3889 primary_key = [ 

3890 sql_util._deep_annotate(pk, {"_orm_adapt": True}) 

3891 for pk in self.primary_key 

3892 ] 

3893 

3894 in_expr: ColumnElement[Any] 

3895 

3896 if len(primary_key) > 1: 

3897 in_expr = sql.tuple_(*primary_key) 

3898 else: 

3899 in_expr = primary_key[0] 

3900 

3901 if entity.is_aliased_class: 

3902 assert entity.mapper is self 

3903 

3904 q = sql.select(entity).set_label_style( 

3905 LABEL_STYLE_TABLENAME_PLUS_COL 

3906 ) 

3907 

3908 in_expr = entity._adapter.traverse(in_expr) 

3909 primary_key = [entity._adapter.traverse(k) for k in primary_key] 

3910 q = q.where( 

3911 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3912 ).order_by(*primary_key) 

3913 else: 

3914 q = sql.select(self).set_label_style( 

3915 LABEL_STYLE_TABLENAME_PLUS_COL 

3916 ) 

3917 q = q.where( 

3918 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3919 ).order_by(*primary_key) 

3920 

3921 return q, enable_opt, disable_opt 

3922 

3923 @HasMemoized.memoized_attribute 

3924 def _subclass_load_via_in_mapper(self): 

3925 # the default is loading this mapper against the basemost mapper 

3926 return self._subclass_load_via_in(self, self.base_mapper) 

3927 

3928 def cascade_iterator( 

3929 self, 

3930 type_: str, 

3931 state: InstanceState[_O], 

3932 halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None, 

3933 ) -> Iterator[ 

3934 Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict] 

3935 ]: 

3936 r"""Iterate each element and its mapper in an object graph, 

3937 for all relationships that meet the given cascade rule. 

3938 

3939 :param type\_: 

3940 The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 

3941 etc.). 

3942 

3943 .. note:: the ``"all"`` cascade is not accepted here. For a generic 

3944 object traversal function, see :ref:`faq_walk_objects`. 

3945 

3946 :param state: 

3947 The lead InstanceState. child items will be processed per 

3948 the relationships defined for this object's mapper. 

3949 

3950 :return: the method yields individual object instances. 

3951 

3952 .. seealso:: 

3953 

3954 :ref:`unitofwork_cascades` 

3955 

3956 :ref:`faq_walk_objects` - illustrates a generic function to 

3957 traverse all objects without relying on cascades. 

3958 

3959 """ 

3960 visited_states: Set[InstanceState[Any]] = set() 

3961 prp, mpp = object(), object() 

3962 

3963 assert state.mapper.isa(self) 

3964 

3965 # this is actually a recursive structure, fully typing it seems 

3966 # a little too difficult for what it's worth here 

3967 visitables: Deque[ 

3968 Tuple[ 

3969 Deque[Any], 

3970 object, 

3971 Optional[InstanceState[Any]], 

3972 Optional[_InstanceDict], 

3973 ] 

3974 ] 

3975 

3976 visitables = deque( 

3977 [(deque(state.mapper._props.values()), prp, state, state.dict)] 

3978 ) 

3979 

3980 while visitables: 

3981 iterator, item_type, parent_state, parent_dict = visitables[-1] 

3982 if not iterator: 

3983 visitables.pop() 

3984 continue 

3985 

3986 if item_type is prp: 

3987 prop = iterator.popleft() 

3988 if not prop.cascade or type_ not in prop.cascade: 

3989 continue 

3990 assert parent_state is not None 

3991 assert parent_dict is not None 

3992 queue = deque( 

3993 prop.cascade_iterator( 

3994 type_, 

3995 parent_state, 

3996 parent_dict, 

3997 visited_states, 

3998 halt_on, 

3999 ) 

4000 ) 

4001 if queue: 

4002 visitables.append((queue, mpp, None, None)) 

4003 elif item_type is mpp: 

4004 ( 

4005 instance, 

4006 instance_mapper, 

4007 corresponding_state, 

4008 corresponding_dict, 

4009 ) = iterator.popleft() 

4010 yield ( 

4011 instance, 

4012 instance_mapper, 

4013 corresponding_state, 

4014 corresponding_dict, 

4015 ) 

4016 visitables.append( 

4017 ( 

4018 deque(instance_mapper._props.values()), 

4019 prp, 

4020 corresponding_state, 

4021 corresponding_dict, 

4022 ) 

4023 ) 

4024 

4025 @HasMemoized.memoized_attribute 

4026 def _compiled_cache(self): 

4027 return util.LRUCache(self._compiled_cache_size) 

4028 

4029 @HasMemoized.memoized_attribute 

4030 def _multiple_persistence_tables(self): 

4031 return len(self.tables) > 1 

4032 

4033 @HasMemoized.memoized_attribute 

4034 def _sorted_tables(self): 

4035 table_to_mapper: Dict[TableClause, Mapper[Any]] = {} 

4036 

4037 for mapper in self.base_mapper.self_and_descendants: 

4038 for t in mapper.tables: 

4039 table_to_mapper.setdefault(t, mapper) 

4040 

4041 extra_dependencies = [] 

4042 for table, mapper in table_to_mapper.items(): 

4043 super_ = mapper.inherits 

4044 if super_: 

4045 extra_dependencies.extend( 

4046 [(super_table, table) for super_table in super_.tables] 

4047 ) 

4048 

4049 def skip(fk): 

4050 # attempt to skip dependencies that are not 

4051 # significant to the inheritance chain 

4052 # for two tables that are related by inheritance. 

4053 # while that dependency may be important, it's technically 

4054 # not what we mean to sort on here. 

4055 parent = table_to_mapper.get(fk.parent.table) 

4056 dep = table_to_mapper.get(fk.column.table) 

4057 if ( 

4058 parent is not None 

4059 and dep is not None 

4060 and dep is not parent 

4061 and dep.inherit_condition is not None 

4062 ): 

4063 cols = set(sql_util._find_columns(dep.inherit_condition)) 

4064 if parent.inherit_condition is not None: 

4065 cols = cols.union( 

4066 sql_util._find_columns(parent.inherit_condition) 

4067 ) 

4068 return fk.parent not in cols and fk.column not in cols 

4069 else: 

4070 return fk.parent not in cols 

4071 return False 

4072 

4073 sorted_ = sql_util.sort_tables( 

4074 table_to_mapper, 

4075 skip_fn=skip, 

4076 extra_dependencies=extra_dependencies, 

4077 ) 

4078 

4079 ret = util.OrderedDict() 

4080 for t in sorted_: 

4081 ret[t] = table_to_mapper[t] 

4082 return ret 

4083 

4084 def _memo(self, key: Any, callable_: Callable[[], _T]) -> _T: 

4085 if key in self._memoized_values: 

4086 return cast(_T, self._memoized_values[key]) 

4087 else: 

4088 self._memoized_values[key] = value = callable_() 

4089 return value 

4090 

4091 @util.memoized_property 

4092 def _table_to_equated(self): 

4093 """memoized map of tables to collections of columns to be 

4094 synchronized upwards to the base mapper.""" 

4095 

4096 result: util.defaultdict[ 

4097 Table, 

4098 List[ 

4099 Tuple[ 

4100 Mapper[Any], 

4101 List[Tuple[ColumnElement[Any], ColumnElement[Any]]], 

4102 ] 

4103 ], 

4104 ] = util.defaultdict(list) 

4105 

4106 def set_union(x, y): 

4107 return x.union(y) 

4108 

4109 for table in self._sorted_tables: 

4110 cols = set(table.c) 

4111 

4112 for m in self.iterate_to_root(): 

4113 if m._inherits_equated_pairs and cols.intersection( 

4114 reduce( 

4115 set_union, 

4116 [l.proxy_set for l, r in m._inherits_equated_pairs], 

4117 ) 

4118 ): 

4119 result[table].append((m, m._inherits_equated_pairs)) 

4120 

4121 return result 

4122 

4123 

4124class _OptGetColumnsNotAvailable(Exception): 

4125 pass 

4126 

4127 

4128def configure_mappers() -> None: 

4129 """Initialize the inter-mapper relationships of all mappers that 

4130 have been constructed thus far across all :class:`_orm.registry` 

4131 collections. 

4132 

4133 The configure step is used to reconcile and initialize the 

4134 :func:`_orm.relationship` linkages between mapped classes, as well as to 

4135 invoke configuration events such as the 

4136 :meth:`_orm.MapperEvents.before_configured` and 

4137 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM 

4138 extensions or user-defined extension hooks. 

4139 

4140 Mapper configuration is normally invoked automatically, the first time 

4141 mappings from a particular :class:`_orm.registry` are used, as well as 

4142 whenever mappings are used and additional not-yet-configured mappers have 

4143 been constructed. The automatic configuration process however is local only 

4144 to the :class:`_orm.registry` involving the target mapper and any related 

4145 :class:`_orm.registry` objects which it may depend on; this is 

4146 equivalent to invoking the :meth:`_orm.registry.configure` method 

4147 on a particular :class:`_orm.registry`. 

4148 

4149 By contrast, the :func:`_orm.configure_mappers` function will invoke the 

4150 configuration process on all :class:`_orm.registry` objects that 

4151 exist in memory, and may be useful for scenarios where many individual 

4152 :class:`_orm.registry` objects that are nonetheless interrelated are 

4153 in use. 

4154 

4155 .. versionchanged:: 1.4 

4156 

4157 As of SQLAlchemy 1.4.0b2, this function works on a 

4158 per-:class:`_orm.registry` basis, locating all :class:`_orm.registry` 

4159 objects present and invoking the :meth:`_orm.registry.configure` method 

4160 on each. The :meth:`_orm.registry.configure` method may be preferred to 

4161 limit the configuration of mappers to those local to a particular 

4162 :class:`_orm.registry` and/or declarative base class. 

4163 

4164 Points at which automatic configuration is invoked include when a mapped 

4165 class is instantiated into an instance, as well as when ORM queries 

4166 are emitted using :meth:`.Session.query` or :meth:`_orm.Session.execute` 

4167 with an ORM-enabled statement. 

4168 

4169 The mapper configure process, whether invoked by 

4170 :func:`_orm.configure_mappers` or from :meth:`_orm.registry.configure`, 

4171 provides several event hooks that can be used to augment the mapper 

4172 configuration step. These hooks include: 

4173 

4174 * :meth:`.MapperEvents.before_configured` - called once before 

4175 :func:`.configure_mappers` or :meth:`_orm.registry.configure` does any 

4176 work; this can be used to establish additional options, properties, or 

4177 related mappings before the operation proceeds. 

4178 

4179 * :meth:`.MapperEvents.mapper_configured` - called as each individual 

4180 :class:`_orm.Mapper` is configured within the process; will include all 

4181 mapper state except for backrefs set up by other mappers that are still 

4182 to be configured. 

4183 

4184 * :meth:`.MapperEvents.after_configured` - called once after 

4185 :func:`.configure_mappers` or :meth:`_orm.registry.configure` is 

4186 complete; at this stage, all :class:`_orm.Mapper` objects that fall 

4187 within the scope of the configuration operation will be fully configured. 

4188 Note that the calling application may still have other mappings that 

4189 haven't been produced yet, such as if they are in modules as yet 

4190 unimported, and may also have mappings that are still to be configured, 

4191 if they are in other :class:`_orm.registry` collections not part of the 

4192 current scope of configuration. 

4193 

4194 """ 

4195 

4196 _configure_registries(_all_registries(), cascade=True) 

4197 

4198 

4199def _configure_registries( 

4200 registries: Set[_RegistryType], cascade: bool 

4201) -> None: 

4202 for reg in registries: 

4203 if reg._new_mappers: 

4204 break 

4205 else: 

4206 return 

4207 

4208 with _CONFIGURE_MUTEX: 

4209 global _already_compiling 

4210 if _already_compiling: 

4211 return 

4212 _already_compiling = True 

4213 try: 

4214 # double-check inside mutex 

4215 for reg in registries: 

4216 if reg._new_mappers: 

4217 break 

4218 else: 

4219 return 

4220 

4221 Mapper.dispatch._for_class(Mapper).before_configured() # type: ignore # noqa: E501 

4222 # initialize properties on all mappers 

4223 # note that _mapper_registry is unordered, which 

4224 # may randomly conceal/reveal issues related to 

4225 # the order of mapper compilation 

4226 

4227 _do_configure_registries(registries, cascade) 

4228 finally: 

4229 _already_compiling = False 

4230 Mapper.dispatch._for_class(Mapper).after_configured() # type: ignore 

4231 

4232 

4233@util.preload_module("sqlalchemy.orm.decl_api") 

4234def _do_configure_registries( 

4235 registries: Set[_RegistryType], cascade: bool 

4236) -> None: 

4237 registry = util.preloaded.orm_decl_api.registry 

4238 

4239 orig = set(registries) 

4240 

4241 for reg in registry._recurse_with_dependencies(registries): 

4242 has_skip = False 

4243 

4244 for mapper in reg._mappers_to_configure(): 

4245 run_configure = None 

4246 

4247 for fn in mapper.dispatch.before_mapper_configured: 

4248 run_configure = fn(mapper, mapper.class_) 

4249 if run_configure is EXT_SKIP: 

4250 has_skip = True 

4251 break 

4252 if run_configure is EXT_SKIP: 

4253 continue 

4254 

4255 if getattr(mapper, "_configure_failed", False): 

4256 e = sa_exc.InvalidRequestError( 

4257 "One or more mappers failed to initialize - " 

4258 "can't proceed with initialization of other " 

4259 "mappers. Triggering mapper: '%s'. " 

4260 "Original exception was: %s" 

4261 % (mapper, mapper._configure_failed) 

4262 ) 

4263 e._configure_failed = mapper._configure_failed # type: ignore 

4264 raise e 

4265 

4266 if not mapper.configured: 

4267 try: 

4268 mapper._post_configure_properties() 

4269 mapper._expire_memoizations() 

4270 mapper.dispatch.mapper_configured(mapper, mapper.class_) 

4271 except Exception: 

4272 exc = sys.exc_info()[1] 

4273 if not hasattr(exc, "_configure_failed"): 

4274 mapper._configure_failed = exc 

4275 raise 

4276 if not has_skip: 

4277 reg._new_mappers = False 

4278 

4279 if not cascade and reg._dependencies.difference(orig): 

4280 raise sa_exc.InvalidRequestError( 

4281 "configure was called with cascade=False but " 

4282 "additional registries remain" 

4283 ) 

4284 

4285 

4286@util.preload_module("sqlalchemy.orm.decl_api") 

4287def _dispose_registries(registries: Set[_RegistryType], cascade: bool) -> None: 

4288 registry = util.preloaded.orm_decl_api.registry 

4289 

4290 orig = set(registries) 

4291 

4292 for reg in registry._recurse_with_dependents(registries): 

4293 if not cascade and reg._dependents.difference(orig): 

4294 raise sa_exc.InvalidRequestError( 

4295 "Registry has dependent registries that are not disposed; " 

4296 "pass cascade=True to clear these also" 

4297 ) 

4298 

4299 while reg._managers: 

4300 try: 

4301 manager, _ = reg._managers.popitem() 

4302 except KeyError: 

4303 # guard against race between while and popitem 

4304 pass 

4305 else: 

4306 reg._dispose_manager_and_mapper(manager) 

4307 

4308 reg._non_primary_mappers.clear() 

4309 reg._dependents.clear() 

4310 for dep in reg._dependencies: 

4311 dep._dependents.discard(reg) 

4312 reg._dependencies.clear() 

4313 # this wasn't done in the 1.3 clear_mappers() and in fact it 

4314 # was a bug, as it could cause configure_mappers() to invoke 

4315 # the "before_configured" event even though mappers had all been 

4316 # disposed. 

4317 reg._new_mappers = False 

4318 

4319 

4320def reconstructor(fn: _Fn) -> _Fn: 

4321 """Decorate a method as the 'reconstructor' hook. 

4322 

4323 Designates a single method as the "reconstructor", an ``__init__``-like 

4324 method that will be called by the ORM after the instance has been 

4325 loaded from the database or otherwise reconstituted. 

4326 

4327 .. tip:: 

4328 

4329 The :func:`_orm.reconstructor` decorator makes use of the 

4330 :meth:`_orm.InstanceEvents.load` event hook, which can be 

4331 used directly. 

4332 

4333 The reconstructor will be invoked with no arguments. Scalar 

4334 (non-collection) database-mapped attributes of the instance will 

4335 be available for use within the function. Eagerly-loaded 

4336 collections are generally not yet available and will usually only 

4337 contain the first element. ORM state changes made to objects at 

4338 this stage will not be recorded for the next flush() operation, so 

4339 the activity within a reconstructor should be conservative. 

4340 

4341 .. seealso:: 

4342 

4343 :meth:`.InstanceEvents.load` 

4344 

4345 """ 

4346 fn.__sa_reconstructor__ = True # type: ignore[attr-defined] 

4347 return fn 

4348 

4349 

4350def validates( 

4351 *names: str, include_removes: bool = False, include_backrefs: bool = True 

4352) -> Callable[[_Fn], _Fn]: 

4353 r"""Decorate a method as a 'validator' for one or more named properties. 

4354 

4355 Designates a method as a validator, a method which receives the 

4356 name of the attribute as well as a value to be assigned, or in the 

4357 case of a collection, the value to be added to the collection. 

4358 The function can then raise validation exceptions to halt the 

4359 process from continuing (where Python's built-in ``ValueError`` 

4360 and ``AssertionError`` exceptions are reasonable choices), or can 

4361 modify or replace the value before proceeding. The function should 

4362 otherwise return the given value. 

4363 

4364 Note that a validator for a collection **cannot** issue a load of that 

4365 collection within the validation routine - this usage raises 

4366 an assertion to avoid recursion overflows. This is a reentrant 

4367 condition which is not supported. 

4368 

4369 :param \*names: list of attribute names to be validated. 

4370 :param include_removes: if True, "remove" events will be 

4371 sent as well - the validation function must accept an additional 

4372 argument "is_remove" which will be a boolean. 

4373 

4374 :param include_backrefs: defaults to ``True``; if ``False``, the 

4375 validation function will not emit if the originator is an attribute 

4376 event related via a backref. This can be used for bi-directional 

4377 :func:`.validates` usage where only one validator should emit per 

4378 attribute operation. 

4379 

4380 .. versionchanged:: 2.0.16 This paramter inadvertently defaulted to 

4381 ``False`` for releases 2.0.0 through 2.0.15. Its correct default 

4382 of ``True`` is restored in 2.0.16. 

4383 

4384 .. seealso:: 

4385 

4386 :ref:`simple_validators` - usage examples for :func:`.validates` 

4387 

4388 """ 

4389 

4390 def wrap(fn: _Fn) -> _Fn: 

4391 fn.__sa_validators__ = names # type: ignore[attr-defined] 

4392 fn.__sa_validation_opts__ = { # type: ignore[attr-defined] 

4393 "include_removes": include_removes, 

4394 "include_backrefs": include_backrefs, 

4395 } 

4396 return fn 

4397 

4398 return wrap 

4399 

4400 

4401def _event_on_load(state, ctx): 

4402 instrumenting_mapper = state.manager.mapper 

4403 

4404 if instrumenting_mapper._reconstructor: 

4405 instrumenting_mapper._reconstructor(state.obj()) 

4406 

4407 

4408def _event_on_init(state, args, kwargs): 

4409 """Run init_instance hooks. 

4410 

4411 This also includes mapper compilation, normally not needed 

4412 here but helps with some piecemeal configuration 

4413 scenarios (such as in the ORM tutorial). 

4414 

4415 """ 

4416 

4417 instrumenting_mapper = state.manager.mapper 

4418 if instrumenting_mapper: 

4419 instrumenting_mapper._check_configure() 

4420 if instrumenting_mapper._set_polymorphic_identity: 

4421 instrumenting_mapper._set_polymorphic_identity(state) 

4422 

4423 

4424class _ColumnMapping(Dict["ColumnElement[Any]", "MapperProperty[Any]"]): 

4425 """Error reporting helper for mapper._columntoproperty.""" 

4426 

4427 __slots__ = ("mapper",) 

4428 

4429 def __init__(self, mapper): 

4430 # TODO: weakref would be a good idea here 

4431 self.mapper = mapper 

4432 

4433 def __missing__(self, column): 

4434 prop = self.mapper._props.get(column) 

4435 if prop: 

4436 raise orm_exc.UnmappedColumnError( 

4437 "Column '%s.%s' is not available, due to " 

4438 "conflicting property '%s':%r" 

4439 % (column.table.name, column.name, column.key, prop) 

4440 ) 

4441 raise orm_exc.UnmappedColumnError( 

4442 "No column %s is configured on mapper %s..." 

4443 % (column, self.mapper) 

4444 )