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

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

1405 statements  

1# orm/mapper.py 

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

3# <see AUTHORS file> 

4# 

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

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

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

8 

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

10 

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

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

13 

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

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

16 

17""" 

18from __future__ import annotations 

19 

20from collections import deque 

21from functools import reduce 

22from itertools import chain 

23import sys 

24import threading 

25from typing import Any 

26from typing import Callable 

27from typing import cast 

28from typing import Collection 

29from typing import Deque 

30from typing import Dict 

31from typing import FrozenSet 

32from typing import Generic 

33from typing import Iterable 

34from typing import Iterator 

35from typing import List 

36from typing import Mapping 

37from typing import Optional 

38from typing import Sequence 

39from typing import Set 

40from typing import Tuple 

41from typing import Type 

42from typing import TYPE_CHECKING 

43from typing import TypeVar 

44from typing import Union 

45import weakref 

46 

47from . import attributes 

48from . import exc as orm_exc 

49from . import instrumentation 

50from . import loading 

51from . import properties 

52from . import util as orm_util 

53from ._typing import _O 

54from .base import _class_to_mapper 

55from .base import _parse_mapper_argument 

56from .base import _state_mapper 

57from .base import PassiveFlag 

58from .base import state_str 

59from .interfaces import _MappedAttribute 

60from .interfaces import EXT_SKIP 

61from .interfaces import InspectionAttr 

62from .interfaces import MapperProperty 

63from .interfaces import ORMEntityColumnsClauseRole 

64from .interfaces import ORMFromClauseRole 

65from .interfaces import StrategizedProperty 

66from .path_registry import PathRegistry 

67from .. import event 

68from .. import exc as sa_exc 

69from .. import inspection 

70from .. import log 

71from .. import schema 

72from .. import sql 

73from .. import util 

74from ..event import dispatcher 

75from ..event import EventTarget 

76from ..sql import base as sql_base 

77from ..sql import coercions 

78from ..sql import expression 

79from ..sql import operators 

80from ..sql import roles 

81from ..sql import TableClause 

82from ..sql import util as sql_util 

83from ..sql import visitors 

84from ..sql.cache_key import MemoizedHasCacheKey 

85from ..sql.elements import KeyedColumnElement 

86from ..sql.schema import Column 

87from ..sql.schema import Table 

88from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

89from ..util import HasMemoized 

90from ..util import HasMemoized_ro_memoized_attribute 

91from ..util.typing import Literal 

92from ..util.typing import TupleAny 

93from ..util.typing import Unpack 

94 

95if TYPE_CHECKING: 

96 from ._typing import _IdentityKeyType 

97 from ._typing import _InstanceDict 

98 from ._typing import _ORMColumnExprArgument 

99 from ._typing import _RegistryType 

100 from .decl_api import registry 

101 from .dependency import DependencyProcessor 

102 from .descriptor_props import CompositeProperty 

103 from .descriptor_props import SynonymProperty 

104 from .events import MapperEvents 

105 from .instrumentation import ClassManager 

106 from .path_registry import CachingEntityRegistry 

107 from .properties import ColumnProperty 

108 from .relationships import RelationshipProperty 

109 from .state import InstanceState 

110 from .util import ORMAdapter 

111 from ..engine import Row 

112 from ..engine import RowMapping 

113 from ..sql._typing import _ColumnExpressionArgument 

114 from ..sql._typing import _EquivalentColumnMap 

115 from ..sql.base import ReadOnlyColumnCollection 

116 from ..sql.elements import ColumnClause 

117 from ..sql.elements import ColumnElement 

118 from ..sql.selectable import FromClause 

119 from ..util import OrderedSet 

120 

121 

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

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

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

125 

126 

127_WithPolymorphicArg = Union[ 

128 Literal["*"], 

129 Tuple[ 

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

131 Optional["FromClause"], 

132 ], 

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

134] 

135 

136 

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

138 weakref.WeakKeyDictionary() 

139) 

140 

141 

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

143 with _CONFIGURE_MUTEX: 

144 return set(_mapper_registries) 

145 

146 

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

148 for reg in _all_registries(): 

149 yield from reg._mappers_to_configure() 

150 

151 

152_already_compiling = False 

153 

154 

155# a constant returned by _get_attr_by_column to indicate 

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

157# column 

158NO_ATTRIBUTE = util.symbol("NO_ATTRIBUTE") 

159 

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

161_CONFIGURE_MUTEX = threading.RLock() 

162 

163 

164@inspection._self_inspects 

165@log.class_logger 

166class Mapper( 

167 ORMFromClauseRole, 

168 ORMEntityColumnsClauseRole[_O], 

169 MemoizedHasCacheKey, 

170 InspectionAttr, 

171 log.Identified, 

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

173 EventTarget, 

174 Generic[_O], 

175): 

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

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

178 proceed. 

179 

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

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

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

183 :ref:`orm_mapping_classes_toplevel`. 

184 

185 """ 

186 

187 dispatch: dispatcher[Mapper[_O]] 

188 

189 _dispose_called = False 

190 _configure_failed: Any = False 

191 _ready_for_configure = False 

192 

193 @util.deprecated_params( 

194 non_primary=( 

195 "1.3", 

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

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

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

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

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

201 ), 

202 ) 

203 def __init__( 

204 self, 

205 class_: Type[_O], 

206 local_table: Optional[FromClause] = None, 

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

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

209 non_primary: bool = False, 

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

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

212 inherit_foreign_keys: Optional[ 

213 Sequence[_ORMColumnExprArgument[Any]] 

214 ] = None, 

215 always_refresh: bool = False, 

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

217 version_id_generator: Optional[ 

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

219 ] = None, 

220 polymorphic_on: Optional[ 

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

222 ] = None, 

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

224 polymorphic_identity: Optional[Any] = None, 

225 concrete: bool = False, 

226 with_polymorphic: Optional[_WithPolymorphicArg] = None, 

227 polymorphic_abstract: bool = False, 

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

229 allow_partial_pks: bool = True, 

230 batch: bool = True, 

231 column_prefix: Optional[str] = None, 

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

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

234 passive_updates: bool = True, 

235 passive_deletes: bool = False, 

236 confirm_deleted_rows: bool = True, 

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

238 legacy_is_orphan: bool = False, 

239 _compiled_cache_size: int = 100, 

240 ): 

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

242 

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

244 is normally invoked through the 

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

246 :ref:`Declarative <orm_declarative_mapping>` or 

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

248 

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

250 removed; for a classical mapping configuration, use the 

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

252 

253 Parameters documented below may be passed to either the 

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

255 ``__mapper_args__`` declarative class attribute described at 

256 :ref:`orm_declarative_mapper_options`. 

257 

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

259 this argument is automatically passed as the declared class 

260 itself. 

261 

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

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

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

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

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

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

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

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

270 present. 

271 

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

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

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

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

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

277 inheritance scheme which uses 

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

279 

280 .. versionadded:: 2.0 

281 

282 .. seealso:: 

283 

284 :ref:`orm_inheritance_abstract_poly` 

285 

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

287 class will overwrite all data within object instances that already 

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

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

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

291 :meth:`_query.Query.populate_existing`. 

292 

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

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

295 possibly existing within the database. This affects whether a 

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

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

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

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

300 

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

302 of multiple entities can be batched together for efficiency. 

303 Setting to False indicates 

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

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

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

307 in between individual row persistence operations. 

308 

309 :param column_prefix: A string which will be prepended 

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

311 objects are automatically assigned as attributes to the 

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

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

314 dictionary. 

315 

316 This parameter is typically useful with imperative mappings 

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

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

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

320 

321 class User(Base): 

322 __table__ = user_table 

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

324 

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

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

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

328 

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

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

331 approach to automating a naming scheme is to intercept the 

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

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

334 pattern. 

335 

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

337 table inheritance with its parent mapper. 

338 

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

340 

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

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

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

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

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

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

347 exception in a future release. 

348 

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

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

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

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

353 are needed immediately before the flush completes. 

354 

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

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

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

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

359 

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

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

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

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

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

365 attributes are not to be accessed in any case. 

366 

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

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

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

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

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

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

373 defaults will not be fetched. 

374 

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

376 :paramref:`.Mapper.eager_defaults` 

377 

378 .. seealso:: 

379 

380 :ref:`orm_server_defaults` 

381 

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

383 INSERTed at once using the 

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

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

386 feature to be very performant on supporting backends. 

387 

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

389 be excluded from mapping. 

390 

391 .. seealso:: 

392 

393 :ref:`include_exclude_cols` 

394 

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

396 names to map. 

397 

398 .. seealso:: 

399 

400 :ref:`include_exclude_cols` 

401 

402 :param inherits: A mapped class or the corresponding 

403 :class:`_orm.Mapper` 

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

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

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

407 is passed automatically as a result of the natural class 

408 hierarchy of the declared classes. 

409 

410 .. seealso:: 

411 

412 :ref:`inheritance_toplevel` 

413 

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

415 expression which will 

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

417 between the two tables. 

418 

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

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

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

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

423 

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

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

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

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

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

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

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

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

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

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

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

435 orphan object has been flushed yet or not. 

436 

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

438 for more detail on this change. 

439 

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

441 is in addition 

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

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

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

445 only. 

446 

447 .. seealso:: 

448 

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

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

451 

452 :param passive_deletes: Indicates DELETE behavior of foreign key 

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

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

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

456 on the superclass mapper. 

457 

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

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

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

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

462 superclass table, and not this table. 

463 

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

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

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

467 validate these attributes; note that the primary key columns 

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

469 the object as a whole. 

470 

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

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

473 to specify passive_deletes without this taking effect for 

474 all subclass mappers. 

475 

476 .. seealso:: 

477 

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

479 used with :func:`_orm.relationship` 

480 

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

482 CASCADE for joined-table inheritance mappers 

483 

484 :param passive_updates: Indicates UPDATE behavior of foreign key 

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

486 inheritance mapping. Defaults to ``True``. 

487 

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

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

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

491 on joined-table rows. 

492 

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

494 referential integrity and will not be issuing its own CASCADE 

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

496 emit an UPDATE statement for the dependent columns during a 

497 primary key change. 

498 

499 .. seealso:: 

500 

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

502 used with :func:`_orm.relationship` 

503 

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

505 CASCADE for joined-table inheritance mappers 

506 

507 :param polymorphic_load: Specifies "polymorphic loading" behavior 

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

509 table inheritance only). Valid values are: 

510 

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

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

513 in a SELECT query against the base. 

514 

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

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

517 the columns specific to this subclass. The SELECT uses 

518 IN to fetch multiple subclasses at once. 

519 

520 .. versionadded:: 1.2 

521 

522 .. seealso:: 

523 

524 :ref:`with_polymorphic_mapper_config` 

525 

526 :ref:`polymorphic_selectin` 

527 

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

529 SQL expression used to determine the target class for an 

530 incoming row, when inheriting classes are present. 

531 

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

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

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

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

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

537 

538 class Employee(Base): 

539 __tablename__ = 'employee' 

540 

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

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

543 

544 __mapper_args__ = { 

545 "polymorphic_on":discriminator, 

546 "polymorphic_identity":"employee" 

547 } 

548 

549 It may also be specified 

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

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

552 approach:: 

553 

554 class Employee(Base): 

555 __tablename__ = 'employee' 

556 

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

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

559 

560 __mapper_args__ = { 

561 "polymorphic_on":case( 

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

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

564 else_="employee"), 

565 "polymorphic_identity":"employee" 

566 } 

567 

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

569 which is of particular use when using annotated column 

570 configurations:: 

571 

572 class Employee(Base): 

573 __tablename__ = 'employee' 

574 

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

576 discriminator: Mapped[str] 

577 

578 __mapper_args__ = { 

579 "polymorphic_on": "discriminator", 

580 "polymorphic_identity": "employee" 

581 } 

582 

583 When setting ``polymorphic_on`` to reference an 

584 attribute or expression that's not present in the 

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

586 of the discriminator should be persisted to the database, 

587 the value of the 

588 discriminator is not automatically set on new 

589 instances; this must be handled by the user, 

590 either through manual means or via event listeners. 

591 A typical approach to establishing such a listener 

592 looks like:: 

593 

594 from sqlalchemy import event 

595 from sqlalchemy.orm import object_mapper 

596 

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

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

599 mapper = object_mapper(instance) 

600 instance.discriminator = mapper.polymorphic_identity 

601 

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

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

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

605 in the database. 

606 

607 .. warning:: 

608 

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

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

611 columns are not yet supported. 

612 

613 .. seealso:: 

614 

615 :ref:`inheritance_toplevel` 

616 

617 :param polymorphic_identity: Specifies the value which 

618 identifies this particular class as returned by the column expression 

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

620 rows are received, the value corresponding to the 

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

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

623 reconstructed object. 

624 

625 .. seealso:: 

626 

627 :ref:`inheritance_toplevel` 

628 

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

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

631 persistence behavior of that attribute. Note that 

632 :class:`_schema.Column` 

633 objects present in 

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

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

636 When using Declarative, this argument is passed automatically, 

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

638 in the declared class body. 

639 

640 .. seealso:: 

641 

642 :ref:`orm_mapping_properties` - in the 

643 :ref:`orm_mapping_classes_toplevel` 

644 

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

646 objects, or alternatively string names of attribute names which 

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

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

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

650 can be overridden here. 

651 

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

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

654 

655 .. seealso:: 

656 

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

658 

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

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

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

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

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

664 version id, a 

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

666 thrown. 

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

668 unless ``version_id_generator`` specifies an alternative version 

669 generator. 

670 

671 .. seealso:: 

672 

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

674 and rationale. 

675 

676 :param version_id_generator: Define how new version ids should 

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

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

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

680 

681 def generate_version(version): 

682 return next_version 

683 

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

685 or programmatic versioning schemes outside of the version id 

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

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

688 of important points when using this option. 

689 

690 .. seealso:: 

691 

692 :ref:`custom_version_counter` 

693 

694 :ref:`server_side_version_counter` 

695 

696 

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

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

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

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

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

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

703 loaded immediately. The second tuple argument <selectable> 

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

705 classes. 

706 

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

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

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

710 indicating polymorphic loading styles. 

711 

712 .. seealso:: 

713 

714 :ref:`with_polymorphic_mapper_config` 

715 

716 """ 

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

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

719 self.class_.__module__, 

720 self.class_.__name__, 

721 ) 

722 

723 self._primary_key_argument = util.to_list(primary_key) 

724 self.non_primary = non_primary 

725 

726 self.always_refresh = always_refresh 

727 

728 if isinstance(version_id_col, MapperProperty): 

729 self.version_id_prop = version_id_col 

730 self.version_id_col = None 

731 else: 

732 self.version_id_col = ( 

733 coercions.expect( 

734 roles.ColumnArgumentOrKeyRole, 

735 version_id_col, 

736 argname="version_id_col", 

737 ) 

738 if version_id_col is not None 

739 else None 

740 ) 

741 

742 if version_id_generator is False: 

743 self.version_id_generator = False 

744 elif version_id_generator is None: 

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

746 else: 

747 self.version_id_generator = version_id_generator 

748 

749 self.concrete = concrete 

750 self.single = False 

751 

752 if inherits is not None: 

753 self.inherits = _parse_mapper_argument(inherits) 

754 else: 

755 self.inherits = None 

756 

757 if local_table is not None: 

758 self.local_table = coercions.expect( 

759 roles.StrictFromClauseRole, 

760 local_table, 

761 disable_inspection=True, 

762 argname="local_table", 

763 ) 

764 elif self.inherits: 

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

766 # .local_table need not be Optional 

767 self.local_table = self.inherits.local_table 

768 self.single = True 

769 else: 

770 raise sa_exc.ArgumentError( 

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

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

773 ) 

774 

775 if inherit_condition is not None: 

776 self.inherit_condition = coercions.expect( 

777 roles.OnClauseRole, inherit_condition 

778 ) 

779 else: 

780 self.inherit_condition = None 

781 

782 self.inherit_foreign_keys = inherit_foreign_keys 

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

784 self._delete_orphans = [] 

785 self.batch = batch 

786 self.eager_defaults = eager_defaults 

787 self.column_prefix = column_prefix 

788 

789 # interim - polymorphic_on is further refined in 

790 # _configure_polymorphic_setter 

791 self.polymorphic_on = ( 

792 coercions.expect( # type: ignore 

793 roles.ColumnArgumentOrKeyRole, 

794 polymorphic_on, 

795 argname="polymorphic_on", 

796 ) 

797 if polymorphic_on is not None 

798 else None 

799 ) 

800 self.polymorphic_abstract = polymorphic_abstract 

801 self._dependency_processors = [] 

802 self.validators = util.EMPTY_DICT 

803 self.passive_updates = passive_updates 

804 self.passive_deletes = passive_deletes 

805 self.legacy_is_orphan = legacy_is_orphan 

806 self._clause_adapter = None 

807 self._requires_row_aliasing = False 

808 self._inherits_equated_pairs = None 

809 self._memoized_values = {} 

810 self._compiled_cache_size = _compiled_cache_size 

811 self._reconstructor = None 

812 self.allow_partial_pks = allow_partial_pks 

813 

814 if self.inherits and not self.concrete: 

815 self.confirm_deleted_rows = False 

816 else: 

817 self.confirm_deleted_rows = confirm_deleted_rows 

818 

819 self._set_with_polymorphic(with_polymorphic) 

820 self.polymorphic_load = polymorphic_load 

821 

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

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

824 # the object instance for that row. 

825 self.polymorphic_identity = polymorphic_identity 

826 

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

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

829 # upon a select operation. 

830 if _polymorphic_map is None: 

831 self.polymorphic_map = {} 

832 else: 

833 self.polymorphic_map = _polymorphic_map 

834 

835 if include_properties is not None: 

836 self.include_properties = util.to_set(include_properties) 

837 else: 

838 self.include_properties = None 

839 if exclude_properties: 

840 self.exclude_properties = util.to_set(exclude_properties) 

841 else: 

842 self.exclude_properties = None 

843 

844 # prevent this mapper from being constructed 

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

846 # configure_mappers() until construction succeeds) 

847 with _CONFIGURE_MUTEX: 

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

849 class_, self 

850 ) 

851 self._configure_inheritance() 

852 self._configure_class_instrumentation() 

853 self._configure_properties() 

854 self._configure_polymorphic_setter() 

855 self._configure_pks() 

856 self.registry._flag_new_mapper(self) 

857 self._log("constructed") 

858 self._expire_memoizations() 

859 

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

861 

862 def _prefer_eager_defaults(self, dialect, table): 

863 if self.eager_defaults == "auto": 

864 if not table.implicit_returning: 

865 return False 

866 

867 return ( 

868 table in self._server_default_col_keys 

869 and dialect.insert_executemany_returning 

870 ) 

871 else: 

872 return self.eager_defaults 

873 

874 def _gen_cache_key(self, anon_map, bindparams): 

875 return (self,) 

876 

877 # ### BEGIN 

878 # ATTRIBUTE DECLARATIONS START HERE 

879 

880 is_mapper = True 

881 """Part of the inspection API.""" 

882 

883 represents_outer_join = False 

884 

885 registry: _RegistryType 

886 

887 @property 

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

889 """Part of the inspection API. 

890 

891 Returns self. 

892 

893 """ 

894 return self 

895 

896 @property 

897 def entity(self): 

898 r"""Part of the inspection API. 

899 

900 Returns self.class\_. 

901 

902 """ 

903 return self.class_ 

904 

905 class_: Type[_O] 

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

907 

908 _identity_class: Type[_O] 

909 

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

911 _dependency_processors: List[DependencyProcessor] 

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

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

914 _all_tables: Set[TableClause] 

915 _polymorphic_attr_key: Optional[str] 

916 

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

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

919 

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

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

922 

923 _columntoproperty: _ColumnMapping 

924 

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

926 _validate_polymorphic_identity: Optional[ 

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

928 ] 

929 

930 tables: Sequence[TableClause] 

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

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

933 is aware of. 

934 

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

936 :class:`_expression.Alias` 

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

938 :class:`_schema.Table` 

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

940 

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

942 Behavior is undefined if directly modified. 

943 

944 """ 

945 

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

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

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

949 

950 The dictionary contains string attribute names as keys 

951 mapped to the actual validation method. 

952 

953 """ 

954 

955 always_refresh: bool 

956 allow_partial_pks: bool 

957 version_id_col: Optional[ColumnElement[Any]] 

958 

959 with_polymorphic: Optional[ 

960 Tuple[ 

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

962 Optional[FromClause], 

963 ] 

964 ] 

965 

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

967 

968 local_table: FromClause 

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

970 :class:`_orm.Mapper` refers. 

971 

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

973 :class:`.FromClause`. 

974 

975 The "local" table is the 

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

977 managing from an attribute access and flush perspective. For 

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

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

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

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

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

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

984 

985 .. seealso:: 

986 

987 :attr:`_orm.Mapper.persist_selectable`. 

988 

989 :attr:`_orm.Mapper.selectable`. 

990 

991 """ 

992 

993 persist_selectable: FromClause 

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

995 is mapped. 

996 

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

998 :class:`.FromClause`. 

999 

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

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

1002 represents the inheriting class hierarchy overall in an inheritance 

1003 scenario. 

1004 

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

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

1007 alternate subquery used for selecting columns. 

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

1009 will be written on a persist operation. 

1010 

1011 .. seealso:: 

1012 

1013 :attr:`_orm.Mapper.selectable`. 

1014 

1015 :attr:`_orm.Mapper.local_table`. 

1016 

1017 """ 

1018 

1019 inherits: Optional[Mapper[Any]] 

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

1021 inherits from, if any. 

1022 

1023 """ 

1024 

1025 inherit_condition: Optional[ColumnElement[bool]] 

1026 

1027 configured: bool = False 

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

1029 

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

1031 Behavior is undefined if directly modified. 

1032 

1033 .. seealso:: 

1034 

1035 :func:`.configure_mappers`. 

1036 

1037 """ 

1038 

1039 concrete: bool 

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

1041 inheritance mapper. 

1042 

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

1044 Behavior is undefined if directly modified. 

1045 

1046 """ 

1047 

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

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

1050 objects 

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

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

1053 

1054 This list is against the selectable in 

1055 :attr:`_orm.Mapper.persist_selectable`. 

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

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

1058 :class:`_expression.Join`, the 

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

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

1061 

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

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

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

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

1066 

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

1068 Behavior is undefined if directly modified. 

1069 

1070 """ 

1071 

1072 class_manager: ClassManager[_O] 

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

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

1075 

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

1077 Behavior is undefined if directly modified. 

1078 

1079 """ 

1080 

1081 single: bool 

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

1083 inheritance mapper. 

1084 

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

1086 

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

1088 Behavior is undefined if directly modified. 

1089 

1090 """ 

1091 

1092 non_primary: bool 

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

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

1095 persistence management. 

1096 

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

1098 Behavior is undefined if directly modified. 

1099 

1100 """ 

1101 

1102 polymorphic_on: Optional[KeyedColumnElement[Any]] 

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

1104 ``polymorphic_on`` argument 

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

1106 

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

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

1109 :func:`.cast`. 

1110 

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

1112 Behavior is undefined if directly modified. 

1113 

1114 """ 

1115 

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

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

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

1119 

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

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

1122 

1123 An inheritance chain of mappers will all reference the same 

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

1125 result rows to target mappers. 

1126 

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

1128 Behavior is undefined if directly modified. 

1129 

1130 """ 

1131 

1132 polymorphic_identity: Optional[Any] 

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

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

1135 

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

1137 comparable to the type of column represented by 

1138 :attr:`_orm.Mapper.polymorphic_on`. 

1139 

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

1141 Behavior is undefined if directly modified. 

1142 

1143 """ 

1144 

1145 base_mapper: Mapper[Any] 

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

1147 

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

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

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

1151 objects in the inheritance chain. 

1152 

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

1154 Behavior is undefined if directly modified. 

1155 

1156 """ 

1157 

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

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

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

1161 

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

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

1164 except that only those columns included in 

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

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

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

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

1169 

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

1171 Behavior is undefined if directly modified. 

1172 

1173 """ 

1174 

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

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

1177 

1178 @util.non_memoized_property 

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

1180 def mapped_table(self): 

1181 return self.persist_selectable 

1182 

1183 @util.memoized_property 

1184 def _path_registry(self) -> CachingEntityRegistry: 

1185 return PathRegistry.per_mapper(self) 

1186 

1187 def _configure_inheritance(self): 

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

1189 being present.""" 

1190 

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

1192 self._inheriting_mappers = util.WeakSequence() 

1193 

1194 if self.inherits: 

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

1196 raise sa_exc.ArgumentError( 

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

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

1199 ) 

1200 

1201 self.dispatch._update(self.inherits.dispatch) 

1202 

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

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

1205 raise sa_exc.ArgumentError( 

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

1207 "only allowed from a %s mapper" 

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

1209 ) 

1210 

1211 if self.single: 

1212 self.persist_selectable = self.inherits.persist_selectable 

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

1214 if self.concrete: 

1215 self.persist_selectable = self.local_table 

1216 for mapper in self.iterate_to_root(): 

1217 if mapper.polymorphic_on is not None: 

1218 mapper._requires_row_aliasing = True 

1219 else: 

1220 if self.inherit_condition is None: 

1221 # figure out inherit condition from our table to the 

1222 # immediate table of the inherited mapper, not its 

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

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

1225 try: 

1226 self.inherit_condition = sql_util.join_condition( 

1227 self.inherits.local_table, self.local_table 

1228 ) 

1229 except sa_exc.NoForeignKeysError as nfe: 

1230 assert self.inherits.local_table is not None 

1231 assert self.local_table is not None 

1232 raise sa_exc.NoForeignKeysError( 

1233 "Can't determine the inherit condition " 

1234 "between inherited table '%s' and " 

1235 "inheriting " 

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

1237 "foreign key relationships established. " 

1238 "Please ensure the inheriting table has " 

1239 "a foreign key relationship to the " 

1240 "inherited " 

1241 "table, or provide an " 

1242 "'on clause' using " 

1243 "the 'inherit_condition' mapper argument." 

1244 % ( 

1245 self.inherits.local_table.description, 

1246 self.local_table.description, 

1247 ) 

1248 ) from nfe 

1249 except sa_exc.AmbiguousForeignKeysError as afe: 

1250 assert self.inherits.local_table is not None 

1251 assert self.local_table is not None 

1252 raise sa_exc.AmbiguousForeignKeysError( 

1253 "Can't determine the inherit condition " 

1254 "between inherited table '%s' and " 

1255 "inheriting " 

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

1257 "foreign key relationship established. " 

1258 "Please specify the 'on clause' using " 

1259 "the 'inherit_condition' mapper argument." 

1260 % ( 

1261 self.inherits.local_table.description, 

1262 self.local_table.description, 

1263 ) 

1264 ) from afe 

1265 assert self.inherits.persist_selectable is not None 

1266 self.persist_selectable = sql.join( 

1267 self.inherits.persist_selectable, 

1268 self.local_table, 

1269 self.inherit_condition, 

1270 ) 

1271 

1272 fks = util.to_set(self.inherit_foreign_keys) 

1273 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 

1274 self.persist_selectable.onclause, 

1275 consider_as_foreign_keys=fks, 

1276 ) 

1277 else: 

1278 self.persist_selectable = self.local_table 

1279 

1280 if self.polymorphic_identity is None: 

1281 self._identity_class = self.class_ 

1282 

1283 if ( 

1284 not self.polymorphic_abstract 

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

1286 ): 

1287 util.warn( 

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

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

1290 f"'polymorphic_on' column of " 

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

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

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

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

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

1296 "class unmapped when using Declarative, set the " 

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

1298 ) 

1299 elif self.concrete: 

1300 self._identity_class = self.class_ 

1301 else: 

1302 self._identity_class = self.inherits._identity_class 

1303 

1304 if self.version_id_col is None: 

1305 self.version_id_col = self.inherits.version_id_col 

1306 self.version_id_generator = self.inherits.version_id_generator 

1307 elif ( 

1308 self.inherits.version_id_col is not None 

1309 and self.version_id_col is not self.inherits.version_id_col 

1310 ): 

1311 util.warn( 

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

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

1314 "the inherited versioning column. " 

1315 "version_id_col should only be specified on " 

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

1317 % ( 

1318 self.version_id_col.description, 

1319 self.inherits.version_id_col.description, 

1320 ) 

1321 ) 

1322 

1323 self.polymorphic_map = self.inherits.polymorphic_map 

1324 self.batch = self.inherits.batch 

1325 self.inherits._inheriting_mappers.append(self) 

1326 self.base_mapper = self.inherits.base_mapper 

1327 self.passive_updates = self.inherits.passive_updates 

1328 self.passive_deletes = ( 

1329 self.inherits.passive_deletes or self.passive_deletes 

1330 ) 

1331 self._all_tables = self.inherits._all_tables 

1332 

1333 if self.polymorphic_identity is not None: 

1334 if self.polymorphic_identity in self.polymorphic_map: 

1335 util.warn( 

1336 "Reassigning polymorphic association for identity %r " 

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

1338 "value for polymorphic_identity." 

1339 % ( 

1340 self.polymorphic_identity, 

1341 self.polymorphic_map[self.polymorphic_identity], 

1342 self, 

1343 self.polymorphic_identity, 

1344 ) 

1345 ) 

1346 self.polymorphic_map[self.polymorphic_identity] = self 

1347 

1348 if self.polymorphic_load and self.concrete: 

1349 raise sa_exc.ArgumentError( 

1350 "polymorphic_load is not currently supported " 

1351 "with concrete table inheritance" 

1352 ) 

1353 if self.polymorphic_load == "inline": 

1354 self.inherits._add_with_polymorphic_subclass(self) 

1355 elif self.polymorphic_load == "selectin": 

1356 pass 

1357 elif self.polymorphic_load is not None: 

1358 raise sa_exc.ArgumentError( 

1359 "unknown argument for polymorphic_load: %r" 

1360 % self.polymorphic_load 

1361 ) 

1362 

1363 else: 

1364 self._all_tables = set() 

1365 self.base_mapper = self 

1366 assert self.local_table is not None 

1367 self.persist_selectable = self.local_table 

1368 if self.polymorphic_identity is not None: 

1369 self.polymorphic_map[self.polymorphic_identity] = self 

1370 self._identity_class = self.class_ 

1371 

1372 if self.persist_selectable is None: 

1373 raise sa_exc.ArgumentError( 

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

1375 % self 

1376 ) 

1377 

1378 def _set_with_polymorphic( 

1379 self, with_polymorphic: Optional[_WithPolymorphicArg] 

1380 ) -> None: 

1381 if with_polymorphic == "*": 

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

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

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

1385 self.with_polymorphic = cast( 

1386 """Tuple[ 

1387 Union[ 

1388 Literal["*"], 

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

1390 ], 

1391 Optional["FromClause"], 

1392 ]""", 

1393 with_polymorphic, 

1394 ) 

1395 else: 

1396 self.with_polymorphic = (with_polymorphic, None) 

1397 elif with_polymorphic is not None: 

1398 raise sa_exc.ArgumentError( 

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

1400 ) 

1401 else: 

1402 self.with_polymorphic = None 

1403 

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

1405 self.with_polymorphic = ( 

1406 self.with_polymorphic[0], 

1407 coercions.expect( 

1408 roles.StrictFromClauseRole, 

1409 self.with_polymorphic[1], 

1410 allow_select=True, 

1411 ), 

1412 ) 

1413 

1414 if self.configured: 

1415 self._expire_memoizations() 

1416 

1417 def _add_with_polymorphic_subclass(self, mapper): 

1418 subcl = mapper.class_ 

1419 if self.with_polymorphic is None: 

1420 self._set_with_polymorphic((subcl,)) 

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

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

1423 self._set_with_polymorphic( 

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

1425 ) 

1426 

1427 def _set_concrete_base(self, mapper): 

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

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

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

1431 

1432 assert self.concrete 

1433 assert not self.inherits 

1434 assert isinstance(mapper, Mapper) 

1435 self.inherits = mapper 

1436 self.inherits.polymorphic_map.update(self.polymorphic_map) 

1437 self.polymorphic_map = self.inherits.polymorphic_map 

1438 for mapper in self.iterate_to_root(): 

1439 if mapper.polymorphic_on is not None: 

1440 mapper._requires_row_aliasing = True 

1441 self.batch = self.inherits.batch 

1442 for mp in self.self_and_descendants: 

1443 mp.base_mapper = self.inherits.base_mapper 

1444 self.inherits._inheriting_mappers.append(self) 

1445 self.passive_updates = self.inherits.passive_updates 

1446 self._all_tables = self.inherits._all_tables 

1447 

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

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

1450 key, key, local=False, column=None 

1451 ): 

1452 self._adapt_inherited_property(key, prop, False) 

1453 

1454 def _set_polymorphic_on(self, polymorphic_on): 

1455 self.polymorphic_on = polymorphic_on 

1456 self._configure_polymorphic_setter(True) 

1457 

1458 def _configure_class_instrumentation(self): 

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

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

1461 given class and entity name. 

1462 

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

1464 name combination will return this mapper. Also decorate the 

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

1466 auto-session attachment logic. 

1467 

1468 """ 

1469 

1470 # we expect that declarative has applied the class manager 

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

1472 # this raises as of 2.0. 

1473 manager = attributes.opt_manager_of_class(self.class_) 

1474 

1475 if self.non_primary: 

1476 if not manager or not manager.is_mapped: 

1477 raise sa_exc.InvalidRequestError( 

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

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

1480 "Mapper." % self.class_ 

1481 ) 

1482 self.class_manager = manager 

1483 

1484 assert manager.registry is not None 

1485 self.registry = manager.registry 

1486 self._identity_class = manager.mapper._identity_class 

1487 manager.registry._add_non_primary_mapper(self) 

1488 return 

1489 

1490 if manager is None or not manager.registry: 

1491 raise sa_exc.InvalidRequestError( 

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

1493 "invoked directly outside of a declarative registry." 

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

1495 "function for a classical mapping." 

1496 ) 

1497 

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

1499 

1500 # this invokes the class_instrument event and sets up 

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

1502 # occur after the instrument_class event above. 

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

1504 # :( 

1505 

1506 manager = instrumentation.register_class( 

1507 self.class_, 

1508 mapper=self, 

1509 expired_attribute_loader=util.partial( 

1510 loading.load_scalar_attributes, self 

1511 ), 

1512 # finalize flag means instrument the __init__ method 

1513 # and call the class_instrument event 

1514 finalize=True, 

1515 ) 

1516 

1517 self.class_manager = manager 

1518 

1519 assert manager.registry is not None 

1520 self.registry = manager.registry 

1521 

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

1523 # e_name None or not. 

1524 if manager.mapper is None: 

1525 return 

1526 

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

1528 

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

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

1531 method = method._sa_original_init 

1532 if hasattr(method, "__func__"): 

1533 method = method.__func__ 

1534 if callable(method): 

1535 if hasattr(method, "__sa_reconstructor__"): 

1536 self._reconstructor = method 

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

1538 elif hasattr(method, "__sa_validators__"): 

1539 validation_opts = method.__sa_validation_opts__ 

1540 for name in method.__sa_validators__: 

1541 if name in self.validators: 

1542 raise sa_exc.InvalidRequestError( 

1543 "A validation function for mapped " 

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

1545 % (name, self) 

1546 ) 

1547 self.validators = self.validators.union( 

1548 {name: (method, validation_opts)} 

1549 ) 

1550 

1551 def _set_dispose_flags(self) -> None: 

1552 self.configured = True 

1553 self._ready_for_configure = True 

1554 self._dispose_called = True 

1555 

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

1557 

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

1559 try: 

1560 prop = self._props[key] 

1561 except KeyError as err: 

1562 raise sa_exc.ArgumentError( 

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

1564 "no attribute is mapped to this name." 

1565 ) from err 

1566 try: 

1567 expr = prop.expression 

1568 except AttributeError as ae: 

1569 raise sa_exc.ArgumentError( 

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

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

1572 ) from ae 

1573 if not isinstance(expr, Column): 

1574 raise sa_exc.ArgumentError( 

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

1576 "property does not refer to a single " 

1577 "mapped Column" 

1578 ) 

1579 return expr 

1580 

1581 def _configure_pks(self) -> None: 

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

1583 

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

1585 

1586 self._pks_by_table = {} 

1587 self._cols_by_table = {} 

1588 

1589 all_cols = util.column_set( 

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

1591 ) 

1592 

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

1594 

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

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

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

1598 # ordering is important since it determines the ordering of 

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

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

1601 fc.primary_key 

1602 ).intersection( 

1603 pk_cols 

1604 ) 

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

1606 all_cols 

1607 ) 

1608 

1609 if self._primary_key_argument: 

1610 coerced_pk_arg = [ 

1611 ( 

1612 self._str_arg_to_mapped_col("primary_key", c) 

1613 if isinstance(c, str) 

1614 else c 

1615 ) 

1616 for c in ( 

1617 coercions.expect( 

1618 roles.DDLConstraintColumnRole, 

1619 coerce_pk, 

1620 argname="primary_key", 

1621 ) 

1622 for coerce_pk in self._primary_key_argument 

1623 ) 

1624 ] 

1625 else: 

1626 coerced_pk_arg = None 

1627 

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

1629 # primary key mappings 

1630 if coerced_pk_arg: 

1631 for k in coerced_pk_arg: 

1632 if k.table not in self._pks_by_table: 

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

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

1635 

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

1637 elif ( 

1638 self.persist_selectable not in self._pks_by_table 

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

1640 ): 

1641 raise sa_exc.ArgumentError( 

1642 "Mapper %s could not assemble any primary " 

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

1644 % (self, self.persist_selectable.description) 

1645 ) 

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

1647 self.local_table, schema.Table 

1648 ): 

1649 util.warn( 

1650 "Could not assemble any primary " 

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

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

1653 % self.local_table.description 

1654 ) 

1655 

1656 if ( 

1657 self.inherits 

1658 and not self.concrete 

1659 and not self._primary_key_argument 

1660 ): 

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

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

1663 self.primary_key = self.inherits.primary_key 

1664 else: 

1665 # determine primary key from argument or persist_selectable pks 

1666 primary_key: Collection[ColumnElement[Any]] 

1667 

1668 if coerced_pk_arg: 

1669 primary_key = [ 

1670 cc if cc is not None else c 

1671 for cc, c in ( 

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

1673 for c in coerced_pk_arg 

1674 ) 

1675 ] 

1676 else: 

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

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

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

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

1681 primary_key = sql_util.reduce_columns( 

1682 self._pks_by_table[self.persist_selectable], 

1683 ignore_nonexistent_tables=True, 

1684 ) 

1685 

1686 if len(primary_key) == 0: 

1687 raise sa_exc.ArgumentError( 

1688 "Mapper %s could not assemble any primary " 

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

1690 % (self, self.persist_selectable.description) 

1691 ) 

1692 

1693 self.primary_key = tuple(primary_key) 

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

1695 

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

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

1698 self._readonly_props = { 

1699 self._columntoproperty[col] 

1700 for col in self._columntoproperty 

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

1702 and ( 

1703 not hasattr(col, "table") 

1704 or col.table not in self._cols_by_table 

1705 ) 

1706 } 

1707 

1708 def _configure_properties(self) -> None: 

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

1710 

1711 # object attribute names mapped to MapperProperty objects 

1712 self._props = util.OrderedDict() 

1713 

1714 # table columns mapped to MapperProperty 

1715 self._columntoproperty = _ColumnMapping(self) 

1716 

1717 explicit_col_props_by_column: Dict[ 

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

1719 ] = {} 

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

1721 

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

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

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

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

1726 if self._init_properties: 

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

1728 if not isinstance(prop_arg, MapperProperty): 

1729 possible_col_prop = self._make_prop_from_column( 

1730 key, prop_arg 

1731 ) 

1732 else: 

1733 possible_col_prop = prop_arg 

1734 

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

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

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

1738 # the Table. 

1739 

1740 _map_as_property_now = True 

1741 if isinstance(possible_col_prop, properties.ColumnProperty): 

1742 for given_col in possible_col_prop.columns: 

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

1744 _map_as_property_now = False 

1745 explicit_col_props_by_key[key] = possible_col_prop 

1746 explicit_col_props_by_column[given_col] = ( 

1747 key, 

1748 possible_col_prop, 

1749 ) 

1750 

1751 if _map_as_property_now: 

1752 self._configure_property( 

1753 key, 

1754 possible_col_prop, 

1755 init=False, 

1756 ) 

1757 

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

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

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

1761 if self.inherits: 

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

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

1764 continue 

1765 

1766 incoming_prop = explicit_col_props_by_key.get(key) 

1767 if incoming_prop: 

1768 new_prop = self._reconcile_prop_with_incoming_columns( 

1769 key, 

1770 inherited_prop, 

1771 warn_only=False, 

1772 incoming_prop=incoming_prop, 

1773 ) 

1774 explicit_col_props_by_key[key] = new_prop 

1775 

1776 for inc_col in incoming_prop.columns: 

1777 explicit_col_props_by_column[inc_col] = ( 

1778 key, 

1779 new_prop, 

1780 ) 

1781 elif key not in self._props: 

1782 self._adapt_inherited_property(key, inherited_prop, False) 

1783 

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

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

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

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

1788 # reconciliation against inherited columns occurs here also. 

1789 

1790 for column in self.persist_selectable.columns: 

1791 if column in explicit_col_props_by_column: 

1792 # column was explicitly passed to properties; configure 

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

1794 # Table / selectable 

1795 key, prop = explicit_col_props_by_column[column] 

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

1797 continue 

1798 

1799 elif column in self._columntoproperty: 

1800 continue 

1801 

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

1803 if self._should_exclude( 

1804 column.key, 

1805 column_key, 

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

1807 column=column, 

1808 ): 

1809 continue 

1810 

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

1812 # of the inheriting mapper 

1813 for mapper in self.iterate_to_root(): 

1814 if column in mapper._columntoproperty: 

1815 column_key = mapper._columntoproperty[column].key 

1816 

1817 self._configure_property( 

1818 column_key, 

1819 column, 

1820 init=False, 

1821 setparent=True, 

1822 ) 

1823 

1824 def _configure_polymorphic_setter(self, init=False): 

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

1826 'polymorphic_on' column, if applicable, and not 

1827 already generated by _configure_properties (which is typical). 

1828 

1829 Also create a setter function which will assign this 

1830 attribute to the value of the 'polymorphic_identity' 

1831 upon instance construction, also if applicable. This 

1832 routine will run when an instance is created. 

1833 

1834 """ 

1835 setter = False 

1836 polymorphic_key: Optional[str] = None 

1837 

1838 if self.polymorphic_on is not None: 

1839 setter = True 

1840 

1841 if isinstance(self.polymorphic_on, str): 

1842 # polymorphic_on specified as a string - link 

1843 # it to mapped ColumnProperty 

1844 try: 

1845 self.polymorphic_on = self._props[self.polymorphic_on] 

1846 except KeyError as err: 

1847 raise sa_exc.ArgumentError( 

1848 "Can't determine polymorphic_on " 

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

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

1851 ) from err 

1852 

1853 if self.polymorphic_on in self._columntoproperty: 

1854 # polymorphic_on is a column that is already mapped 

1855 # to a ColumnProperty 

1856 prop = self._columntoproperty[self.polymorphic_on] 

1857 elif isinstance(self.polymorphic_on, MapperProperty): 

1858 # polymorphic_on is directly a MapperProperty, 

1859 # ensure it's a ColumnProperty 

1860 if not isinstance( 

1861 self.polymorphic_on, properties.ColumnProperty 

1862 ): 

1863 raise sa_exc.ArgumentError( 

1864 "Only direct column-mapped " 

1865 "property or SQL expression " 

1866 "can be passed for polymorphic_on" 

1867 ) 

1868 prop = self.polymorphic_on 

1869 else: 

1870 # polymorphic_on is a Column or SQL expression and 

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

1872 # only present in the with_polymorphic selectable or 

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

1874 # hope is compatible with this mapper's persist_selectable 

1875 col = self.persist_selectable.corresponding_column( 

1876 self.polymorphic_on 

1877 ) 

1878 if col is None: 

1879 # polymorphic_on doesn't derive from any 

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

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

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

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

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

1885 # represented somehow in either persist_selectable or 

1886 # with_polymorphic. Otherwise as of 0.7.4 we 

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

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

1889 setter = False 

1890 instrument = False 

1891 col = self.polymorphic_on 

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

1893 self.with_polymorphic is None 

1894 or self.with_polymorphic[1] is None 

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

1896 is None 

1897 ): 

1898 raise sa_exc.InvalidRequestError( 

1899 "Could not map polymorphic_on column " 

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

1901 "loads will not function properly" 

1902 % col.description 

1903 ) 

1904 else: 

1905 # column/expression that polymorphic_on derives from 

1906 # is present in our mapped table 

1907 # and is probably mapped, but polymorphic_on itself 

1908 # is not. This happens when 

1909 # the polymorphic_on is only directly present in the 

1910 # with_polymorphic selectable, as when use 

1911 # polymorphic_union. 

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

1913 instrument = True 

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

1915 if key: 

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

1917 raise sa_exc.InvalidRequestError( 

1918 "Cannot exclude or override the " 

1919 "discriminator column %r" % key 

1920 ) 

1921 else: 

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

1923 key = col.key 

1924 

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

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

1927 

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

1929 # column in the property 

1930 self.polymorphic_on = prop.columns[0] 

1931 polymorphic_key = prop.key 

1932 else: 

1933 # no polymorphic_on was set. 

1934 # check inheriting mappers for one. 

1935 for mapper in self.iterate_to_root(): 

1936 # determine if polymorphic_on of the parent 

1937 # should be propagated here. If the col 

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

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

1940 # inheritance), we can use it 

1941 if mapper.polymorphic_on is not None: 

1942 if self.persist_selectable is mapper.persist_selectable: 

1943 self.polymorphic_on = mapper.polymorphic_on 

1944 else: 

1945 self.polymorphic_on = ( 

1946 self.persist_selectable 

1947 ).corresponding_column(mapper.polymorphic_on) 

1948 # we can use the parent mapper's _set_polymorphic_identity 

1949 # directly; it ensures the polymorphic_identity of the 

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

1951 if self.polymorphic_on is not None: 

1952 self._set_polymorphic_identity = ( 

1953 mapper._set_polymorphic_identity 

1954 ) 

1955 self._polymorphic_attr_key = ( 

1956 mapper._polymorphic_attr_key 

1957 ) 

1958 self._validate_polymorphic_identity = ( 

1959 mapper._validate_polymorphic_identity 

1960 ) 

1961 else: 

1962 self._set_polymorphic_identity = None 

1963 self._polymorphic_attr_key = None 

1964 return 

1965 

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

1967 raise sa_exc.InvalidRequestError( 

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

1969 "on a mapper hierarchy which includes the " 

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

1971 ) 

1972 

1973 if setter: 

1974 

1975 def _set_polymorphic_identity(state): 

1976 dict_ = state.dict 

1977 # TODO: what happens if polymorphic_on column attribute name 

1978 # does not match .key? 

1979 

1980 polymorphic_identity = ( 

1981 state.manager.mapper.polymorphic_identity 

1982 ) 

1983 if ( 

1984 polymorphic_identity is None 

1985 and state.manager.mapper.polymorphic_abstract 

1986 ): 

1987 raise sa_exc.InvalidRequestError( 

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

1989 "mapper is marked polymorphic_abstract=True" 

1990 ) 

1991 

1992 state.get_impl(polymorphic_key).set( 

1993 state, 

1994 dict_, 

1995 polymorphic_identity, 

1996 None, 

1997 ) 

1998 

1999 self._polymorphic_attr_key = polymorphic_key 

2000 

2001 def _validate_polymorphic_identity(mapper, state, dict_): 

2002 if ( 

2003 polymorphic_key in dict_ 

2004 and dict_[polymorphic_key] 

2005 not in mapper._acceptable_polymorphic_identities 

2006 ): 

2007 util.warn_limited( 

2008 "Flushing object %s with " 

2009 "incompatible polymorphic identity %r; the " 

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

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

2012 ) 

2013 

2014 self._set_polymorphic_identity = _set_polymorphic_identity 

2015 self._validate_polymorphic_identity = ( 

2016 _validate_polymorphic_identity 

2017 ) 

2018 else: 

2019 self._polymorphic_attr_key = None 

2020 self._set_polymorphic_identity = None 

2021 

2022 _validate_polymorphic_identity = None 

2023 

2024 @HasMemoized.memoized_attribute 

2025 def _version_id_prop(self): 

2026 if self.version_id_col is not None: 

2027 return self._columntoproperty[self.version_id_col] 

2028 else: 

2029 return None 

2030 

2031 @HasMemoized.memoized_attribute 

2032 def _acceptable_polymorphic_identities(self): 

2033 identities = set() 

2034 

2035 stack = deque([self]) 

2036 while stack: 

2037 item = stack.popleft() 

2038 if item.persist_selectable is self.persist_selectable: 

2039 identities.add(item.polymorphic_identity) 

2040 stack.extend(item._inheriting_mappers) 

2041 

2042 return identities 

2043 

2044 @HasMemoized.memoized_attribute 

2045 def _prop_set(self): 

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

2047 

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

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

2050 descriptor_props = util.preloaded.orm_descriptor_props 

2051 

2052 if not self.concrete: 

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

2054 elif key not in self._props: 

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

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

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

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

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

2060 # descriptors that might have side effects when invoked. 

2061 implementing_attribute = self.class_manager._get_class_attr_mro( 

2062 key, prop 

2063 ) 

2064 if implementing_attribute is prop or ( 

2065 isinstance( 

2066 implementing_attribute, attributes.InstrumentedAttribute 

2067 ) 

2068 and implementing_attribute._parententity is prop.parent 

2069 ): 

2070 self._configure_property( 

2071 key, 

2072 descriptor_props.ConcreteInheritedProperty(), 

2073 init=init, 

2074 setparent=True, 

2075 ) 

2076 

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

2078 def _configure_property( 

2079 self, 

2080 key: str, 

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

2082 *, 

2083 init: bool = True, 

2084 setparent: bool = True, 

2085 warn_for_existing: bool = False, 

2086 ) -> MapperProperty[Any]: 

2087 descriptor_props = util.preloaded.orm_descriptor_props 

2088 self._log( 

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

2090 ) 

2091 

2092 if not isinstance(prop_arg, MapperProperty): 

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

2094 key, prop_arg 

2095 ) 

2096 else: 

2097 prop = prop_arg 

2098 

2099 if isinstance(prop, properties.ColumnProperty): 

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

2101 

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

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

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

2105 if col is None and self.inherits: 

2106 path = [self] 

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

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

2109 if col is not None: 

2110 for m2 in path: 

2111 m2.persist_selectable._refresh_for_new_column(col) 

2112 col = self.persist_selectable.corresponding_column( 

2113 prop.columns[0] 

2114 ) 

2115 break 

2116 path.append(m) 

2117 

2118 # subquery expression, column not present in the mapped 

2119 # selectable. 

2120 if col is None: 

2121 col = prop.columns[0] 

2122 

2123 # column is coming in after _readonly_props was 

2124 # initialized; check for 'readonly' 

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

2126 not hasattr(col, "table") 

2127 or col.table not in self._cols_by_table 

2128 ): 

2129 self._readonly_props.add(prop) 

2130 

2131 else: 

2132 # if column is coming in after _cols_by_table was 

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

2134 if ( 

2135 hasattr(self, "_cols_by_table") 

2136 and col.table in self._cols_by_table 

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

2138 ): 

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

2140 

2141 # if this properties.ColumnProperty represents the "polymorphic 

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

2143 # columns in SELECT statements. 

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

2145 prop._is_polymorphic_discriminator = ( 

2146 col is self.polymorphic_on 

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

2148 ) 

2149 

2150 if isinstance(col, expression.Label): 

2151 # new in 1.4, get column property against expressions 

2152 # to be addressable in subqueries 

2153 col.key = col._tq_key_label = key 

2154 

2155 self.columns.add(col, key) 

2156 

2157 for col in prop.columns: 

2158 for proxy_col in col.proxy_set: 

2159 self._columntoproperty[proxy_col] = prop 

2160 

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

2162 util.warn( 

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

2164 "assigned to attribute " 

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

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

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

2168 ) 

2169 

2170 prop.key = key 

2171 

2172 if setparent: 

2173 prop.set_parent(self, init) 

2174 

2175 if key in self._props and getattr( 

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

2177 ): 

2178 syn = self._props[key]._mapped_by_synonym 

2179 raise sa_exc.ArgumentError( 

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

2181 "a ColumnProperty already exists keyed to the name " 

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

2183 ) 

2184 

2185 # replacement cases 

2186 

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

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

2189 if ( 

2190 key in self._props 

2191 and not isinstance( 

2192 self._props[key], descriptor_props.ConcreteInheritedProperty 

2193 ) 

2194 and not isinstance(prop, descriptor_props.SynonymProperty) 

2195 ): 

2196 if warn_for_existing: 

2197 util.warn_deprecated( 

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

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

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

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

2202 "release", 

2203 "2.0", 

2204 ) 

2205 oldprop = self._props[key] 

2206 self._path_registry.pop(oldprop, None) 

2207 

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

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

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

2211 # get replaced. 

2212 elif ( 

2213 warn_for_existing 

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

2215 and not isinstance(prop, descriptor_props.SynonymProperty) 

2216 and not isinstance( 

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

2218 descriptor_props.ConcreteInheritedProperty, 

2219 ) 

2220 ): 

2221 util.warn_deprecated( 

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

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

2224 "attribute of the same name. " 

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

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

2227 "release", 

2228 "2.0", 

2229 ) 

2230 

2231 self._props[key] = prop 

2232 

2233 if not self.non_primary: 

2234 prop.instrument_class(self) 

2235 

2236 for mapper in self._inheriting_mappers: 

2237 mapper._adapt_inherited_property(key, prop, init) 

2238 

2239 if init: 

2240 prop.init() 

2241 prop.post_instrument_class(self) 

2242 

2243 if self.configured: 

2244 self._expire_memoizations() 

2245 

2246 return prop 

2247 

2248 def _make_prop_from_column( 

2249 self, 

2250 key: str, 

2251 column: Union[ 

2252 Sequence[KeyedColumnElement[Any]], KeyedColumnElement[Any] 

2253 ], 

2254 ) -> ColumnProperty[Any]: 

2255 columns = util.to_list(column) 

2256 mapped_column = [] 

2257 for c in columns: 

2258 mc = self.persist_selectable.corresponding_column(c) 

2259 if mc is None: 

2260 mc = self.local_table.corresponding_column(c) 

2261 if mc is not None: 

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

2263 # mapped table, this corresponds to adding a 

2264 # column after the fact to the local table. 

2265 # [ticket:1523] 

2266 self.persist_selectable._refresh_for_new_column(mc) 

2267 mc = self.persist_selectable.corresponding_column(c) 

2268 if mc is None: 

2269 raise sa_exc.ArgumentError( 

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

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

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

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

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

2275 ) 

2276 mapped_column.append(mc) 

2277 return properties.ColumnProperty(*mapped_column) 

2278 

2279 def _reconcile_prop_with_incoming_columns( 

2280 self, 

2281 key: str, 

2282 existing_prop: MapperProperty[Any], 

2283 warn_only: bool, 

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

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

2286 ) -> ColumnProperty[Any]: 

2287 if incoming_prop and ( 

2288 self.concrete 

2289 or not isinstance(existing_prop, properties.ColumnProperty) 

2290 ): 

2291 return incoming_prop 

2292 

2293 existing_column = existing_prop.columns[0] 

2294 

2295 if incoming_prop and existing_column in incoming_prop.columns: 

2296 return incoming_prop 

2297 

2298 if incoming_prop is None: 

2299 assert single_column is not None 

2300 incoming_column = single_column 

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

2302 else: 

2303 assert single_column is None 

2304 incoming_column = incoming_prop.columns[0] 

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

2306 

2307 if ( 

2308 ( 

2309 not self._inherits_equated_pairs 

2310 or (equated_pair_key not in self._inherits_equated_pairs) 

2311 ) 

2312 and not existing_column.shares_lineage(incoming_column) 

2313 and existing_column is not self.version_id_col 

2314 and incoming_column is not self.version_id_col 

2315 ): 

2316 msg = ( 

2317 "Implicitly combining column %s with column " 

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

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

2320 "explicitly." 

2321 % ( 

2322 existing_prop.columns[-1], 

2323 incoming_column, 

2324 key, 

2325 ) 

2326 ) 

2327 if warn_only: 

2328 util.warn(msg) 

2329 else: 

2330 raise sa_exc.InvalidRequestError(msg) 

2331 

2332 # existing properties.ColumnProperty from an inheriting 

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

2334 # breakpoint() 

2335 new_prop = existing_prop.copy() 

2336 

2337 new_prop.columns.insert(0, incoming_column) 

2338 self._log( 

2339 "inserting column to existing list " 

2340 "in properties.ColumnProperty %s", 

2341 key, 

2342 ) 

2343 return new_prop # type: ignore 

2344 

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

2346 def _property_from_column( 

2347 self, 

2348 key: str, 

2349 column: KeyedColumnElement[Any], 

2350 ) -> ColumnProperty[Any]: 

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

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

2353 

2354 descriptor_props = util.preloaded.orm_descriptor_props 

2355 

2356 prop = self._props.get(key) 

2357 

2358 if isinstance(prop, properties.ColumnProperty): 

2359 return self._reconcile_prop_with_incoming_columns( 

2360 key, 

2361 prop, 

2362 single_column=column, 

2363 warn_only=prop.parent is not self, 

2364 ) 

2365 elif prop is None or isinstance( 

2366 prop, descriptor_props.ConcreteInheritedProperty 

2367 ): 

2368 return self._make_prop_from_column(key, column) 

2369 else: 

2370 raise sa_exc.ArgumentError( 

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

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

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

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

2375 "to remove all awareness of the column entirely " 

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

2377 "use the 'include_properties' or 'exclude_properties' " 

2378 "mapper arguments to control specifically which table " 

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

2380 ) 

2381 

2382 @util.langhelpers.tag_method_for_warnings( 

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

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

2385 "operation.", 

2386 sa_exc.SAWarning, 

2387 ) 

2388 def _check_configure(self) -> None: 

2389 if self.registry._new_mappers: 

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

2391 

2392 def _post_configure_properties(self) -> None: 

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

2394 attached to this mapper. 

2395 

2396 This is a deferred configuration step which is intended 

2397 to execute once all mappers have been constructed. 

2398 

2399 """ 

2400 

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

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

2403 for key, prop in l: 

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

2405 

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

2407 prop.init() 

2408 

2409 if prop._configure_finished: 

2410 prop.post_instrument_class(self) 

2411 

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

2413 self.configured = True 

2414 

2415 def add_properties(self, dict_of_properties): 

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

2417 using `add_property`. 

2418 

2419 """ 

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

2421 self.add_property(key, value) 

2422 

2423 def add_property( 

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

2425 ) -> None: 

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

2427 

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

2429 property to the initial properties dictionary sent to the 

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

2431 the given MapperProperty is configured immediately. 

2432 

2433 """ 

2434 prop = self._configure_property( 

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

2436 ) 

2437 assert isinstance(prop, MapperProperty) 

2438 self._init_properties[key] = prop 

2439 

2440 def _expire_memoizations(self) -> None: 

2441 for mapper in self.iterate_to_root(): 

2442 mapper._reset_memoizations() 

2443 

2444 @property 

2445 def _log_desc(self) -> str: 

2446 return ( 

2447 "(" 

2448 + self.class_.__name__ 

2449 + "|" 

2450 + ( 

2451 self.local_table is not None 

2452 and self.local_table.description 

2453 or str(self.local_table) 

2454 ) 

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

2456 + ")" 

2457 ) 

2458 

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

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

2461 

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

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

2464 

2465 def __repr__(self) -> str: 

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

2467 

2468 def __str__(self) -> str: 

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

2470 self.class_.__name__, 

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

2472 ( 

2473 self.local_table.description 

2474 if self.local_table is not None 

2475 else self.persist_selectable.description 

2476 ), 

2477 ) 

2478 

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

2480 orphan_possible = False 

2481 for mapper in self.iterate_to_root(): 

2482 for key, cls in mapper._delete_orphans: 

2483 orphan_possible = True 

2484 

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

2486 state, key, optimistic=state.has_identity 

2487 ) 

2488 

2489 if self.legacy_is_orphan and has_parent: 

2490 return False 

2491 elif not self.legacy_is_orphan and not has_parent: 

2492 return True 

2493 

2494 if self.legacy_is_orphan: 

2495 return orphan_possible 

2496 else: 

2497 return False 

2498 

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

2500 return key in self._props 

2501 

2502 def get_property( 

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

2504 ) -> MapperProperty[Any]: 

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

2506 

2507 if _configure_mappers: 

2508 self._check_configure() 

2509 

2510 try: 

2511 return self._props[key] 

2512 except KeyError as err: 

2513 raise sa_exc.InvalidRequestError( 

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

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

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

2517 ) from err 

2518 

2519 def get_property_by_column( 

2520 self, column: ColumnElement[_T] 

2521 ) -> MapperProperty[_T]: 

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

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

2524 

2525 return self._columntoproperty[column] 

2526 

2527 @property 

2528 def iterate_properties(self): 

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

2530 

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

2532 

2533 def _mappers_from_spec( 

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

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

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

2537 represents. 

2538 

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

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

2541 

2542 """ 

2543 if spec == "*": 

2544 mappers = list(self.self_and_descendants) 

2545 elif spec: 

2546 mapper_set = set() 

2547 for m in util.to_list(spec): 

2548 m = _class_to_mapper(m) 

2549 if not m.isa(self): 

2550 raise sa_exc.InvalidRequestError( 

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

2552 ) 

2553 

2554 if selectable is None: 

2555 mapper_set.update(m.iterate_to_root()) 

2556 else: 

2557 mapper_set.add(m) 

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

2559 else: 

2560 mappers = [] 

2561 

2562 if selectable is not None: 

2563 tables = set( 

2564 sql_util.find_tables(selectable, include_aliases=True) 

2565 ) 

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

2567 return mappers 

2568 

2569 def _selectable_from_mappers( 

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

2571 ) -> FromClause: 

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

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

2574 mapped tables. 

2575 

2576 """ 

2577 from_obj = self.persist_selectable 

2578 for m in mappers: 

2579 if m is self: 

2580 continue 

2581 if m.concrete: 

2582 raise sa_exc.InvalidRequestError( 

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

2584 "when concrete-inheriting mappers are used." 

2585 ) 

2586 elif not m.single: 

2587 if innerjoin: 

2588 from_obj = from_obj.join( 

2589 m.local_table, m.inherit_condition 

2590 ) 

2591 else: 

2592 from_obj = from_obj.outerjoin( 

2593 m.local_table, m.inherit_condition 

2594 ) 

2595 

2596 return from_obj 

2597 

2598 @HasMemoized.memoized_attribute 

2599 def _version_id_has_server_side_value(self) -> bool: 

2600 vid_col = self.version_id_col 

2601 

2602 if vid_col is None: 

2603 return False 

2604 

2605 elif not isinstance(vid_col, Column): 

2606 return True 

2607 else: 

2608 return vid_col.server_default is not None or ( 

2609 vid_col.default is not None 

2610 and ( 

2611 not vid_col.default.is_scalar 

2612 and not vid_col.default.is_callable 

2613 ) 

2614 ) 

2615 

2616 @HasMemoized.memoized_attribute 

2617 def _single_table_criterion(self): 

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

2619 return self.polymorphic_on._annotate( 

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

2621 ).in_( 

2622 [ 

2623 m.polymorphic_identity 

2624 for m in self.self_and_descendants 

2625 if not m.polymorphic_abstract 

2626 ] 

2627 ) 

2628 else: 

2629 return None 

2630 

2631 @HasMemoized.memoized_attribute 

2632 def _has_aliased_polymorphic_fromclause(self): 

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

2634 like a subquery. 

2635 

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

2637 if this is present. 

2638 

2639 """ 

2640 return self.with_polymorphic and isinstance( 

2641 self.with_polymorphic[1], 

2642 expression.AliasedReturnsRows, 

2643 ) 

2644 

2645 @HasMemoized.memoized_attribute 

2646 def _should_select_with_poly_adapter(self): 

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

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

2649 rows for mapped classes and subclasses against this Mapper. 

2650 

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

2652 for this condition. 

2653 

2654 """ 

2655 

2656 # this has been simplified as of #8456. 

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

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

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

2660 # 

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

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

2663 # flattened JOIN for with_polymorphic.) 

2664 # 

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

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

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

2668 # some kind or polymorphic_union. 

2669 # 

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

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

2672 # on it (such as test_join_from_polymorphic_explicit_aliased_three). 

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

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

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

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

2677 # legacy case we should probably disable. 

2678 # 

2679 # 

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

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

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

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

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

2685 # polymorphic base class. 

2686 # 

2687 return ( 

2688 self._has_aliased_polymorphic_fromclause 

2689 or self._requires_row_aliasing 

2690 or (self.base_mapper._has_aliased_polymorphic_fromclause) 

2691 or self.base_mapper._requires_row_aliasing 

2692 ) 

2693 

2694 @HasMemoized.memoized_attribute 

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

2696 self._check_configure() 

2697 

2698 if not self.with_polymorphic: 

2699 return [] 

2700 return self._mappers_from_spec(*self.with_polymorphic) 

2701 

2702 @HasMemoized.memoized_attribute 

2703 def _post_inspect(self): 

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

2705 

2706 E.g. when Query calls: 

2707 

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

2709 

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

2711 

2712 """ 

2713 self._check_configure() 

2714 

2715 @HasMemoized_ro_memoized_attribute 

2716 def _with_polymorphic_selectable(self) -> FromClause: 

2717 if not self.with_polymorphic: 

2718 return self.persist_selectable 

2719 

2720 spec, selectable = self.with_polymorphic 

2721 if selectable is not None: 

2722 return selectable 

2723 else: 

2724 return self._selectable_from_mappers( 

2725 self._mappers_from_spec(spec, selectable), False 

2726 ) 

2727 

2728 with_polymorphic_mappers = _with_polymorphic_mappers 

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

2730 default "polymorphic" query. 

2731 

2732 """ 

2733 

2734 @HasMemoized_ro_memoized_attribute 

2735 def _insert_cols_evaluating_none(self): 

2736 return { 

2737 table: frozenset( 

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

2739 ) 

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

2741 } 

2742 

2743 @HasMemoized.memoized_attribute 

2744 def _insert_cols_as_none(self): 

2745 return { 

2746 table: frozenset( 

2747 col.key 

2748 for col in columns 

2749 if not col.primary_key 

2750 and not col.server_default 

2751 and not col.default 

2752 and not col.type.should_evaluate_none 

2753 ) 

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

2755 } 

2756 

2757 @HasMemoized.memoized_attribute 

2758 def _propkey_to_col(self): 

2759 return { 

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

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

2762 } 

2763 

2764 @HasMemoized.memoized_attribute 

2765 def _pk_keys_by_table(self): 

2766 return { 

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

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

2769 } 

2770 

2771 @HasMemoized.memoized_attribute 

2772 def _pk_attr_keys_by_table(self): 

2773 return { 

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

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

2776 } 

2777 

2778 @HasMemoized.memoized_attribute 

2779 def _server_default_cols( 

2780 self, 

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

2782 return { 

2783 table: frozenset( 

2784 [ 

2785 col 

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

2787 if col.server_default is not None 

2788 or ( 

2789 col.default is not None 

2790 and col.default.is_clause_element 

2791 ) 

2792 ] 

2793 ) 

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

2795 } 

2796 

2797 @HasMemoized.memoized_attribute 

2798 def _server_onupdate_default_cols( 

2799 self, 

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

2801 return { 

2802 table: frozenset( 

2803 [ 

2804 col 

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

2806 if col.server_onupdate is not None 

2807 or ( 

2808 col.onupdate is not None 

2809 and col.onupdate.is_clause_element 

2810 ) 

2811 ] 

2812 ) 

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

2814 } 

2815 

2816 @HasMemoized.memoized_attribute 

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

2818 return { 

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

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

2821 } 

2822 

2823 @HasMemoized.memoized_attribute 

2824 def _server_onupdate_default_col_keys( 

2825 self, 

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

2827 return { 

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

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

2830 } 

2831 

2832 @HasMemoized.memoized_attribute 

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

2834 result: Set[str] = set() 

2835 

2836 col_to_property = self._columntoproperty 

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

2838 result.update( 

2839 col_to_property[col].key 

2840 for col in columns.intersection(col_to_property) 

2841 ) 

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

2843 result.update( 

2844 col_to_property[col].key 

2845 for col in columns.intersection(col_to_property) 

2846 ) 

2847 return result 

2848 

2849 @HasMemoized.memoized_instancemethod 

2850 def __clause_element__(self): 

2851 annotations: Dict[str, Any] = { 

2852 "entity_namespace": self, 

2853 "parententity": self, 

2854 "parentmapper": self, 

2855 } 

2856 if self.persist_selectable is not self.local_table: 

2857 # joined table inheritance, with polymorphic selectable, 

2858 # etc. 

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

2860 { 

2861 "entity_namespace": self, 

2862 "parententity": self, 

2863 "parentmapper": self, 

2864 } 

2865 )._set_propagate_attrs( 

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

2867 ) 

2868 

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

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

2871 ) 

2872 

2873 @util.memoized_property 

2874 def select_identity_token(self): 

2875 return ( 

2876 expression.null() 

2877 ._annotate( 

2878 { 

2879 "entity_namespace": self, 

2880 "parententity": self, 

2881 "parentmapper": self, 

2882 "identity_token": True, 

2883 } 

2884 ) 

2885 ._set_propagate_attrs( 

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

2887 ) 

2888 ) 

2889 

2890 @property 

2891 def selectable(self) -> FromClause: 

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

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

2894 

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

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

2897 full "polymorphic" selectable is returned. 

2898 

2899 """ 

2900 return self._with_polymorphic_selectable 

2901 

2902 def _with_polymorphic_args( 

2903 self, 

2904 spec: Any = None, 

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

2906 innerjoin: bool = False, 

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

2908 if selectable not in (None, False): 

2909 selectable = coercions.expect( 

2910 roles.StrictFromClauseRole, selectable, allow_select=True 

2911 ) 

2912 

2913 if self.with_polymorphic: 

2914 if not spec: 

2915 spec = self.with_polymorphic[0] 

2916 if selectable is False: 

2917 selectable = self.with_polymorphic[1] 

2918 elif selectable is False: 

2919 selectable = None 

2920 mappers = self._mappers_from_spec(spec, selectable) 

2921 if selectable is not None: 

2922 return mappers, selectable 

2923 else: 

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

2925 

2926 @HasMemoized.memoized_attribute 

2927 def _polymorphic_properties(self): 

2928 return list( 

2929 self._iterate_polymorphic_properties( 

2930 self._with_polymorphic_mappers 

2931 ) 

2932 ) 

2933 

2934 @property 

2935 def _all_column_expressions(self): 

2936 poly_properties = self._polymorphic_properties 

2937 adapter = self._polymorphic_adapter 

2938 

2939 return [ 

2940 adapter.columns[c] if adapter else c 

2941 for prop in poly_properties 

2942 if isinstance(prop, properties.ColumnProperty) 

2943 and prop._renders_in_subqueries 

2944 for c in prop.columns 

2945 ] 

2946 

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

2948 if polymorphic_mappers: 

2949 poly_properties = self._iterate_polymorphic_properties( 

2950 polymorphic_mappers 

2951 ) 

2952 else: 

2953 poly_properties = self._polymorphic_properties 

2954 

2955 return [ 

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

2957 for prop in poly_properties 

2958 if isinstance(prop, properties.ColumnProperty) 

2959 ] 

2960 

2961 @HasMemoized.memoized_attribute 

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

2963 if self._has_aliased_polymorphic_fromclause: 

2964 return orm_util.ORMAdapter( 

2965 orm_util._TraceAdaptRole.MAPPER_POLYMORPHIC_ADAPTER, 

2966 self, 

2967 selectable=self.selectable, 

2968 equivalents=self._equivalent_columns, 

2969 limit_on_entity=False, 

2970 ) 

2971 else: 

2972 return None 

2973 

2974 def _iterate_polymorphic_properties(self, mappers=None): 

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

2976 a SELECT.""" 

2977 if mappers is None: 

2978 mappers = self._with_polymorphic_mappers 

2979 

2980 if not mappers: 

2981 for c in self.iterate_properties: 

2982 yield c 

2983 else: 

2984 # in the polymorphic case, filter out discriminator columns 

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

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

2987 for c in util.unique_list( 

2988 chain( 

2989 *[ 

2990 list(mapper.iterate_properties) 

2991 for mapper in [self] + mappers 

2992 ] 

2993 ) 

2994 ): 

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

2996 self.polymorphic_on is None 

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

2998 ): 

2999 continue 

3000 yield c 

3001 

3002 @HasMemoized.memoized_attribute 

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

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

3005 associated this mapper. 

3006 

3007 This is an object that provides each property based on 

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

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

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

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

3012 column. The namespace object can also be iterated, 

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

3014 

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

3016 of this attribute which limit the types of properties 

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

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

3019 

3020 .. warning:: 

3021 

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

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

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

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

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

3027 accessing attributes dynamically, favor using the dict-access 

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

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

3030 

3031 .. seealso:: 

3032 

3033 :attr:`_orm.Mapper.all_orm_descriptors` 

3034 

3035 """ 

3036 

3037 self._check_configure() 

3038 return util.ReadOnlyProperties(self._props) 

3039 

3040 @HasMemoized.memoized_attribute 

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

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

3043 with the mapped class. 

3044 

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

3046 associated with the mapped class or its superclasses. 

3047 

3048 This namespace includes attributes that are mapped to the class 

3049 as well as attributes declared by extension modules. 

3050 It includes any Python descriptor type that inherits from 

3051 :class:`.InspectionAttr`. This includes 

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

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

3054 :class:`.AssociationProxy`. 

3055 

3056 To distinguish between mapped attributes and extension attributes, 

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

3058 to a constant that distinguishes between different extension types. 

3059 

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

3061 

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

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

3064 

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

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

3067 3 below. The order will be the 

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

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

3070 or the mapper. 

3071 

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

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

3074 class in which it first appeared. 

3075 

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

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

3078 

3079 .. versionchanged:: 1.3.19 ensured deterministic ordering for 

3080 :meth:`_orm.Mapper.all_orm_descriptors`. 

3081 

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

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

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

3085 referring to the collection of mapped properties via 

3086 :attr:`_orm.Mapper.attrs`. 

3087 

3088 .. warning:: 

3089 

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

3091 accessor namespace is an 

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

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

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

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

3096 accessing attributes dynamically, favor using the dict-access 

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

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

3099 collisions. 

3100 

3101 .. seealso:: 

3102 

3103 :attr:`_orm.Mapper.attrs` 

3104 

3105 """ 

3106 return util.ReadOnlyProperties( 

3107 dict(self.class_manager._all_sqla_attributes()) 

3108 ) 

3109 

3110 @HasMemoized.memoized_attribute 

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

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

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

3114 all synonyms that refer to primary key columns 

3115 

3116 """ 

3117 descriptor_props = util.preloaded.orm_descriptor_props 

3118 

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

3120 

3121 return { 

3122 syn.key: syn.name 

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

3124 if isinstance(syn, descriptor_props.SynonymProperty) 

3125 and syn.name in pk_keys 

3126 } 

3127 

3128 @HasMemoized.memoized_attribute 

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

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

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

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

3133 

3134 .. seealso:: 

3135 

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

3137 :class:`.MapperProperty` 

3138 objects. 

3139 

3140 """ 

3141 descriptor_props = util.preloaded.orm_descriptor_props 

3142 

3143 return self._filter_properties(descriptor_props.SynonymProperty) 

3144 

3145 @property 

3146 def entity_namespace(self): 

3147 return self.class_ 

3148 

3149 @HasMemoized.memoized_attribute 

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

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

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

3153 

3154 .. seealso:: 

3155 

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

3157 :class:`.MapperProperty` 

3158 objects. 

3159 

3160 """ 

3161 return self._filter_properties(properties.ColumnProperty) 

3162 

3163 @HasMemoized.memoized_attribute 

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

3165 def relationships( 

3166 self, 

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

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

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

3170 

3171 .. warning:: 

3172 

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

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

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

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

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

3178 accessing attributes dynamically, favor using the dict-access 

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

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

3181 collisions. 

3182 

3183 .. seealso:: 

3184 

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

3186 :class:`.MapperProperty` 

3187 objects. 

3188 

3189 """ 

3190 return self._filter_properties( 

3191 util.preloaded.orm_relationships.RelationshipProperty 

3192 ) 

3193 

3194 @HasMemoized.memoized_attribute 

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

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

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

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

3199 

3200 .. seealso:: 

3201 

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

3203 :class:`.MapperProperty` 

3204 objects. 

3205 

3206 """ 

3207 return self._filter_properties( 

3208 util.preloaded.orm_descriptor_props.CompositeProperty 

3209 ) 

3210 

3211 def _filter_properties( 

3212 self, type_: Type[_MP] 

3213 ) -> util.ReadOnlyProperties[_MP]: 

3214 self._check_configure() 

3215 return util.ReadOnlyProperties( 

3216 util.OrderedDict( 

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

3218 ) 

3219 ) 

3220 

3221 @HasMemoized.memoized_attribute 

3222 def _get_clause(self): 

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

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

3225 by primary key. 

3226 

3227 """ 

3228 params = [ 

3229 ( 

3230 primary_key, 

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

3232 ) 

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

3234 ] 

3235 return ( 

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

3237 util.column_dict(params), 

3238 ) 

3239 

3240 @HasMemoized.memoized_attribute 

3241 def _equivalent_columns(self) -> _EquivalentColumnMap: 

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

3243 the determination of column pairs that are equated to 

3244 one another based on inherit condition. This is designed 

3245 to work with the queries that util.polymorphic_union 

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

3247 the base table directly (including the subclass table columns 

3248 only). 

3249 

3250 The resulting structure is a dictionary of columns mapped 

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

3252 

3253 { 

3254 tablea.col1: 

3255 {tableb.col1, tablec.col1}, 

3256 tablea.col2: 

3257 {tabled.col2} 

3258 } 

3259 

3260 """ 

3261 result: _EquivalentColumnMap = {} 

3262 

3263 def visit_binary(binary): 

3264 if binary.operator == operators.eq: 

3265 if binary.left in result: 

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

3267 else: 

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

3269 if binary.right in result: 

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

3271 else: 

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

3273 

3274 for mapper in self.base_mapper.self_and_descendants: 

3275 if mapper.inherit_condition is not None: 

3276 visitors.traverse( 

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

3278 ) 

3279 

3280 return result 

3281 

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

3283 if isinstance( 

3284 obj, 

3285 ( 

3286 _MappedAttribute, 

3287 instrumentation.ClassManager, 

3288 expression.ColumnElement, 

3289 ), 

3290 ): 

3291 return False 

3292 else: 

3293 return assigned_name not in self._dataclass_fields 

3294 

3295 @HasMemoized.memoized_attribute 

3296 def _dataclass_fields(self): 

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

3298 

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

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

3301 present on the class. 

3302 

3303 This occurs when properties are propagated from an inherited class, or 

3304 are applied from the columns present in the mapped table. 

3305 

3306 """ 

3307 

3308 if column is not None and sql_base._never_select_column(column): 

3309 return True 

3310 

3311 # check for class-bound attributes and/or descriptors, 

3312 # either local or from an inherited class 

3313 # ignore dataclass field default values 

3314 if local: 

3315 if self.class_.__dict__.get( 

3316 assigned_name, None 

3317 ) is not None and self._is_userland_descriptor( 

3318 assigned_name, self.class_.__dict__[assigned_name] 

3319 ): 

3320 return True 

3321 else: 

3322 attr = self.class_manager._get_class_attr_mro(assigned_name, None) 

3323 if attr is not None and self._is_userland_descriptor( 

3324 assigned_name, attr 

3325 ): 

3326 return True 

3327 

3328 if ( 

3329 self.include_properties is not None 

3330 and name not in self.include_properties 

3331 and (column is None or column not in self.include_properties) 

3332 ): 

3333 self._log("not including property %s" % (name)) 

3334 return True 

3335 

3336 if self.exclude_properties is not None and ( 

3337 name in self.exclude_properties 

3338 or (column is not None and column in self.exclude_properties) 

3339 ): 

3340 self._log("excluding property %s" % (name)) 

3341 return True 

3342 

3343 return False 

3344 

3345 def common_parent(self, other: Mapper[Any]) -> bool: 

3346 """Return true if the given mapper shares a 

3347 common inherited parent as this mapper.""" 

3348 

3349 return self.base_mapper is other.base_mapper 

3350 

3351 def is_sibling(self, other: Mapper[Any]) -> bool: 

3352 """return true if the other mapper is an inheriting sibling to this 

3353 one. common parent but different branch 

3354 

3355 """ 

3356 return ( 

3357 self.base_mapper is other.base_mapper 

3358 and not self.isa(other) 

3359 and not other.isa(self) 

3360 ) 

3361 

3362 def _canload( 

3363 self, state: InstanceState[Any], allow_subtypes: bool 

3364 ) -> bool: 

3365 s = self.primary_mapper() 

3366 if self.polymorphic_on is not None or allow_subtypes: 

3367 return _state_mapper(state).isa(s) 

3368 else: 

3369 return _state_mapper(state) is s 

3370 

3371 def isa(self, other: Mapper[Any]) -> bool: 

3372 """Return True if the this mapper inherits from the given mapper.""" 

3373 

3374 m: Optional[Mapper[Any]] = self 

3375 while m and m is not other: 

3376 m = m.inherits 

3377 return bool(m) 

3378 

3379 def iterate_to_root(self) -> Iterator[Mapper[Any]]: 

3380 m: Optional[Mapper[Any]] = self 

3381 while m: 

3382 yield m 

3383 m = m.inherits 

3384 

3385 @HasMemoized.memoized_attribute 

3386 def self_and_descendants(self) -> Sequence[Mapper[Any]]: 

3387 """The collection including this mapper and all descendant mappers. 

3388 

3389 This includes not just the immediately inheriting mappers but 

3390 all their inheriting mappers as well. 

3391 

3392 """ 

3393 descendants = [] 

3394 stack = deque([self]) 

3395 while stack: 

3396 item = stack.popleft() 

3397 descendants.append(item) 

3398 stack.extend(item._inheriting_mappers) 

3399 return util.WeakSequence(descendants) 

3400 

3401 def polymorphic_iterator(self) -> Iterator[Mapper[Any]]: 

3402 """Iterate through the collection including this mapper and 

3403 all descendant mappers. 

3404 

3405 This includes not just the immediately inheriting mappers but 

3406 all their inheriting mappers as well. 

3407 

3408 To iterate through an entire hierarchy, use 

3409 ``mapper.base_mapper.polymorphic_iterator()``. 

3410 

3411 """ 

3412 return iter(self.self_and_descendants) 

3413 

3414 def primary_mapper(self) -> Mapper[Any]: 

3415 """Return the primary mapper corresponding to this mapper's class key 

3416 (class).""" 

3417 

3418 return self.class_manager.mapper 

3419 

3420 @property 

3421 def primary_base_mapper(self) -> Mapper[Any]: 

3422 return self.class_manager.mapper.base_mapper 

3423 

3424 def _result_has_identity_key(self, result, adapter=None): 

3425 pk_cols: Sequence[ColumnClause[Any]] = self.primary_key 

3426 if adapter: 

3427 pk_cols = [adapter.columns[c] for c in pk_cols] 

3428 rk = result.keys() 

3429 for col in pk_cols: 

3430 if col not in rk: 

3431 return False 

3432 else: 

3433 return True 

3434 

3435 def identity_key_from_row( 

3436 self, 

3437 row: Optional[Union[Row[Unpack[TupleAny]], RowMapping]], 

3438 identity_token: Optional[Any] = None, 

3439 adapter: Optional[ORMAdapter] = None, 

3440 ) -> _IdentityKeyType[_O]: 

3441 """Return an identity-map key for use in storing/retrieving an 

3442 item from the identity map. 

3443 

3444 :param row: A :class:`.Row` or :class:`.RowMapping` produced from a 

3445 result set that selected from the ORM mapped primary key columns. 

3446 

3447 .. versionchanged:: 2.0 

3448 :class:`.Row` or :class:`.RowMapping` are accepted 

3449 for the "row" argument 

3450 

3451 """ 

3452 pk_cols: Sequence[ColumnClause[Any]] = self.primary_key 

3453 if adapter: 

3454 pk_cols = [adapter.columns[c] for c in pk_cols] 

3455 

3456 if hasattr(row, "_mapping"): 

3457 mapping = row._mapping # type: ignore 

3458 else: 

3459 mapping = cast("Mapping[Any, Any]", row) 

3460 

3461 return ( 

3462 self._identity_class, 

3463 tuple(mapping[column] for column in pk_cols), # type: ignore 

3464 identity_token, 

3465 ) 

3466 

3467 def identity_key_from_primary_key( 

3468 self, 

3469 primary_key: Tuple[Any, ...], 

3470 identity_token: Optional[Any] = None, 

3471 ) -> _IdentityKeyType[_O]: 

3472 """Return an identity-map key for use in storing/retrieving an 

3473 item from an identity map. 

3474 

3475 :param primary_key: A list of values indicating the identifier. 

3476 

3477 """ 

3478 return ( 

3479 self._identity_class, 

3480 tuple(primary_key), 

3481 identity_token, 

3482 ) 

3483 

3484 def identity_key_from_instance(self, instance: _O) -> _IdentityKeyType[_O]: 

3485 """Return the identity key for the given instance, based on 

3486 its primary key attributes. 

3487 

3488 If the instance's state is expired, calling this method 

3489 will result in a database check to see if the object has been deleted. 

3490 If the row no longer exists, 

3491 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3492 

3493 This value is typically also found on the instance state under the 

3494 attribute name `key`. 

3495 

3496 """ 

3497 state = attributes.instance_state(instance) 

3498 return self._identity_key_from_state(state, PassiveFlag.PASSIVE_OFF) 

3499 

3500 def _identity_key_from_state( 

3501 self, 

3502 state: InstanceState[_O], 

3503 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3504 ) -> _IdentityKeyType[_O]: 

3505 dict_ = state.dict 

3506 manager = state.manager 

3507 return ( 

3508 self._identity_class, 

3509 tuple( 

3510 [ 

3511 manager[prop.key].impl.get(state, dict_, passive) 

3512 for prop in self._identity_key_props 

3513 ] 

3514 ), 

3515 state.identity_token, 

3516 ) 

3517 

3518 def primary_key_from_instance(self, instance: _O) -> Tuple[Any, ...]: 

3519 """Return the list of primary key values for the given 

3520 instance. 

3521 

3522 If the instance's state is expired, calling this method 

3523 will result in a database check to see if the object has been deleted. 

3524 If the row no longer exists, 

3525 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

3526 

3527 """ 

3528 state = attributes.instance_state(instance) 

3529 identity_key = self._identity_key_from_state( 

3530 state, PassiveFlag.PASSIVE_OFF 

3531 ) 

3532 return identity_key[1] 

3533 

3534 @HasMemoized.memoized_attribute 

3535 def _persistent_sortkey_fn(self): 

3536 key_fns = [col.type.sort_key_function for col in self.primary_key] 

3537 

3538 if set(key_fns).difference([None]): 

3539 

3540 def key(state): 

3541 return tuple( 

3542 key_fn(val) if key_fn is not None else val 

3543 for key_fn, val in zip(key_fns, state.key[1]) 

3544 ) 

3545 

3546 else: 

3547 

3548 def key(state): 

3549 return state.key[1] 

3550 

3551 return key 

3552 

3553 @HasMemoized.memoized_attribute 

3554 def _identity_key_props(self): 

3555 return [self._columntoproperty[col] for col in self.primary_key] 

3556 

3557 @HasMemoized.memoized_attribute 

3558 def _all_pk_cols(self): 

3559 collection: Set[ColumnClause[Any]] = set() 

3560 for table in self.tables: 

3561 collection.update(self._pks_by_table[table]) 

3562 return collection 

3563 

3564 @HasMemoized.memoized_attribute 

3565 def _should_undefer_in_wildcard(self): 

3566 cols: Set[ColumnElement[Any]] = set(self.primary_key) 

3567 if self.polymorphic_on is not None: 

3568 cols.add(self.polymorphic_on) 

3569 return cols 

3570 

3571 @HasMemoized.memoized_attribute 

3572 def _primary_key_propkeys(self): 

3573 return {self._columntoproperty[col].key for col in self._all_pk_cols} 

3574 

3575 def _get_state_attr_by_column( 

3576 self, 

3577 state: InstanceState[_O], 

3578 dict_: _InstanceDict, 

3579 column: ColumnElement[Any], 

3580 passive: PassiveFlag = PassiveFlag.PASSIVE_RETURN_NO_VALUE, 

3581 ) -> Any: 

3582 prop = self._columntoproperty[column] 

3583 return state.manager[prop.key].impl.get(state, dict_, passive=passive) 

3584 

3585 def _set_committed_state_attr_by_column(self, state, dict_, column, value): 

3586 prop = self._columntoproperty[column] 

3587 state.manager[prop.key].impl.set_committed_value(state, dict_, value) 

3588 

3589 def _set_state_attr_by_column(self, state, dict_, column, value): 

3590 prop = self._columntoproperty[column] 

3591 state.manager[prop.key].impl.set(state, dict_, value, None) 

3592 

3593 def _get_committed_attr_by_column(self, obj, column): 

3594 state = attributes.instance_state(obj) 

3595 dict_ = attributes.instance_dict(obj) 

3596 return self._get_committed_state_attr_by_column( 

3597 state, dict_, column, passive=PassiveFlag.PASSIVE_OFF 

3598 ) 

3599 

3600 def _get_committed_state_attr_by_column( 

3601 self, state, dict_, column, passive=PassiveFlag.PASSIVE_RETURN_NO_VALUE 

3602 ): 

3603 prop = self._columntoproperty[column] 

3604 return state.manager[prop.key].impl.get_committed_value( 

3605 state, dict_, passive=passive 

3606 ) 

3607 

3608 def _optimized_get_statement(self, state, attribute_names): 

3609 """assemble a WHERE clause which retrieves a given state by primary 

3610 key, using a minimized set of tables. 

3611 

3612 Applies to a joined-table inheritance mapper where the 

3613 requested attribute names are only present on joined tables, 

3614 not the base table. The WHERE clause attempts to include 

3615 only those tables to minimize joins. 

3616 

3617 """ 

3618 props = self._props 

3619 

3620 col_attribute_names = set(attribute_names).intersection( 

3621 state.mapper.column_attrs.keys() 

3622 ) 

3623 tables: Set[FromClause] = set( 

3624 chain( 

3625 *[ 

3626 sql_util.find_tables(c, check_columns=True) 

3627 for key in col_attribute_names 

3628 for c in props[key].columns 

3629 ] 

3630 ) 

3631 ) 

3632 

3633 if self.base_mapper.local_table in tables: 

3634 return None 

3635 

3636 def visit_binary(binary): 

3637 leftcol = binary.left 

3638 rightcol = binary.right 

3639 if leftcol is None or rightcol is None: 

3640 return 

3641 

3642 if leftcol.table not in tables: 

3643 leftval = self._get_committed_state_attr_by_column( 

3644 state, 

3645 state.dict, 

3646 leftcol, 

3647 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3648 ) 

3649 if leftval in orm_util._none_set: 

3650 raise _OptGetColumnsNotAvailable() 

3651 binary.left = sql.bindparam( 

3652 None, leftval, type_=binary.right.type 

3653 ) 

3654 elif rightcol.table not in tables: 

3655 rightval = self._get_committed_state_attr_by_column( 

3656 state, 

3657 state.dict, 

3658 rightcol, 

3659 passive=PassiveFlag.PASSIVE_NO_INITIALIZE, 

3660 ) 

3661 if rightval in orm_util._none_set: 

3662 raise _OptGetColumnsNotAvailable() 

3663 binary.right = sql.bindparam( 

3664 None, rightval, type_=binary.right.type 

3665 ) 

3666 

3667 allconds: List[ColumnElement[bool]] = [] 

3668 

3669 start = False 

3670 

3671 # as of #7507, from the lowest base table on upwards, 

3672 # we include all intermediary tables. 

3673 

3674 for mapper in reversed(list(self.iterate_to_root())): 

3675 if mapper.local_table in tables: 

3676 start = True 

3677 elif not isinstance(mapper.local_table, expression.TableClause): 

3678 return None 

3679 if start and not mapper.single: 

3680 assert mapper.inherits 

3681 assert not mapper.concrete 

3682 assert mapper.inherit_condition is not None 

3683 allconds.append(mapper.inherit_condition) 

3684 tables.add(mapper.local_table) 

3685 

3686 # only the bottom table needs its criteria to be altered to fit 

3687 # the primary key ident - the rest of the tables upwards to the 

3688 # descendant-most class should all be present and joined to each 

3689 # other. 

3690 try: 

3691 _traversed = visitors.cloned_traverse( 

3692 allconds[0], {}, {"binary": visit_binary} 

3693 ) 

3694 except _OptGetColumnsNotAvailable: 

3695 return None 

3696 else: 

3697 allconds[0] = _traversed 

3698 

3699 cond = sql.and_(*allconds) 

3700 

3701 cols = [] 

3702 for key in col_attribute_names: 

3703 cols.extend(props[key].columns) 

3704 return ( 

3705 sql.select(*cols) 

3706 .where(cond) 

3707 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 

3708 ) 

3709 

3710 def _iterate_to_target_viawpoly(self, mapper): 

3711 if self.isa(mapper): 

3712 prev = self 

3713 for m in self.iterate_to_root(): 

3714 yield m 

3715 

3716 if m is not prev and prev not in m._with_polymorphic_mappers: 

3717 break 

3718 

3719 prev = m 

3720 if m is mapper: 

3721 break 

3722 

3723 @HasMemoized.memoized_attribute 

3724 def _would_selectinload_combinations_cache(self): 

3725 return {} 

3726 

3727 def _would_selectin_load_only_from_given_mapper(self, super_mapper): 

3728 """return True if this mapper would "selectin" polymorphic load based 

3729 on the given super mapper, and not from a setting from a subclass. 

3730 

3731 given:: 

3732 

3733 class A: 

3734 ... 

3735 

3736 class B(A): 

3737 __mapper_args__ = {"polymorphic_load": "selectin"} 

3738 

3739 class C(B): 

3740 ... 

3741 

3742 class D(B): 

3743 __mapper_args__ = {"polymorphic_load": "selectin"} 

3744 

3745 ``inspect(C)._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3746 returns True, because C does selectin loading because of B's setting. 

3747 

3748 OTOH, ``inspect(D) 

3749 ._would_selectin_load_only_from_given_mapper(inspect(B))`` 

3750 returns False, because D does selectin loading because of its own 

3751 setting; when we are doing a selectin poly load from B, we want to 

3752 filter out D because it would already have its own selectin poly load 

3753 set up separately. 

3754 

3755 Added as part of #9373. 

3756 

3757 """ 

3758 cache = self._would_selectinload_combinations_cache 

3759 

3760 try: 

3761 return cache[super_mapper] 

3762 except KeyError: 

3763 pass 

3764 

3765 # assert that given object is a supermapper, meaning we already 

3766 # strong reference it directly or indirectly. this allows us 

3767 # to not worry that we are creating new strongrefs to unrelated 

3768 # mappers or other objects. 

3769 assert self.isa(super_mapper) 

3770 

3771 mapper = super_mapper 

3772 for m in self._iterate_to_target_viawpoly(mapper): 

3773 if m.polymorphic_load == "selectin": 

3774 retval = m is super_mapper 

3775 break 

3776 else: 

3777 retval = False 

3778 

3779 cache[super_mapper] = retval 

3780 return retval 

3781 

3782 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 

3783 if not enabled_via_opt: 

3784 # common case, takes place for all polymorphic loads 

3785 mapper = polymorphic_from 

3786 for m in self._iterate_to_target_viawpoly(mapper): 

3787 if m.polymorphic_load == "selectin": 

3788 return m 

3789 else: 

3790 # uncommon case, selectin load options were used 

3791 enabled_via_opt = set(enabled_via_opt) 

3792 enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 

3793 for entity in enabled_via_opt.union([polymorphic_from]): 

3794 mapper = entity.mapper 

3795 for m in self._iterate_to_target_viawpoly(mapper): 

3796 if ( 

3797 m.polymorphic_load == "selectin" 

3798 or m in enabled_via_opt_mappers 

3799 ): 

3800 return enabled_via_opt_mappers.get(m, m) 

3801 

3802 return None 

3803 

3804 @util.preload_module("sqlalchemy.orm.strategy_options") 

3805 def _subclass_load_via_in(self, entity, polymorphic_from): 

3806 """Assemble a that can load the columns local to 

3807 this subclass as a SELECT with IN. 

3808 

3809 """ 

3810 

3811 strategy_options = util.preloaded.orm_strategy_options 

3812 

3813 assert self.inherits 

3814 

3815 if self.polymorphic_on is not None: 

3816 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 

3817 keep_props = set([polymorphic_prop] + self._identity_key_props) 

3818 else: 

3819 keep_props = set(self._identity_key_props) 

3820 

3821 disable_opt = strategy_options.Load(entity) 

3822 enable_opt = strategy_options.Load(entity) 

3823 

3824 classes_to_include = {self} 

3825 m: Optional[Mapper[Any]] = self.inherits 

3826 while ( 

3827 m is not None 

3828 and m is not polymorphic_from 

3829 and m.polymorphic_load == "selectin" 

3830 ): 

3831 classes_to_include.add(m) 

3832 m = m.inherits 

3833 

3834 for prop in self.column_attrs + self.relationships: 

3835 # skip prop keys that are not instrumented on the mapped class. 

3836 # this is primarily the "_sa_polymorphic_on" property that gets 

3837 # created for an ad-hoc polymorphic_on SQL expression, issue #8704 

3838 if prop.key not in self.class_manager: 

3839 continue 

3840 

3841 if prop.parent in classes_to_include or prop in keep_props: 

3842 # "enable" options, to turn on the properties that we want to 

3843 # load by default (subject to options from the query) 

3844 if not isinstance(prop, StrategizedProperty): 

3845 continue 

3846 

3847 enable_opt = enable_opt._set_generic_strategy( 

3848 # convert string name to an attribute before passing 

3849 # to loader strategy. note this must be in terms 

3850 # of given entity, such as AliasedClass, etc. 

3851 (getattr(entity.entity_namespace, prop.key),), 

3852 dict(prop.strategy_key), 

3853 _reconcile_to_other=True, 

3854 ) 

3855 else: 

3856 # "disable" options, to turn off the properties from the 

3857 # superclass that we *don't* want to load, applied after 

3858 # the options from the query to override them 

3859 disable_opt = disable_opt._set_generic_strategy( 

3860 # convert string name to an attribute before passing 

3861 # to loader strategy. note this must be in terms 

3862 # of given entity, such as AliasedClass, etc. 

3863 (getattr(entity.entity_namespace, prop.key),), 

3864 {"do_nothing": True}, 

3865 _reconcile_to_other=False, 

3866 ) 

3867 

3868 primary_key = [ 

3869 sql_util._deep_annotate(pk, {"_orm_adapt": True}) 

3870 for pk in self.primary_key 

3871 ] 

3872 

3873 in_expr: ColumnElement[Any] 

3874 

3875 if len(primary_key) > 1: 

3876 in_expr = sql.tuple_(*primary_key) 

3877 else: 

3878 in_expr = primary_key[0] 

3879 

3880 if entity.is_aliased_class: 

3881 assert entity.mapper is self 

3882 

3883 q = sql.select(entity).set_label_style( 

3884 LABEL_STYLE_TABLENAME_PLUS_COL 

3885 ) 

3886 

3887 in_expr = entity._adapter.traverse(in_expr) 

3888 primary_key = [entity._adapter.traverse(k) for k in primary_key] 

3889 q = q.where( 

3890 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3891 ).order_by(*primary_key) 

3892 else: 

3893 q = sql.select(self).set_label_style( 

3894 LABEL_STYLE_TABLENAME_PLUS_COL 

3895 ) 

3896 q = q.where( 

3897 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3898 ).order_by(*primary_key) 

3899 

3900 return q, enable_opt, disable_opt 

3901 

3902 @HasMemoized.memoized_attribute 

3903 def _subclass_load_via_in_mapper(self): 

3904 # the default is loading this mapper against the basemost mapper 

3905 return self._subclass_load_via_in(self, self.base_mapper) 

3906 

3907 def cascade_iterator( 

3908 self, 

3909 type_: str, 

3910 state: InstanceState[_O], 

3911 halt_on: Optional[Callable[[InstanceState[Any]], bool]] = None, 

3912 ) -> Iterator[ 

3913 Tuple[object, Mapper[Any], InstanceState[Any], _InstanceDict] 

3914 ]: 

3915 r"""Iterate each element and its mapper in an object graph, 

3916 for all relationships that meet the given cascade rule. 

3917 

3918 :param type\_: 

3919 The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 

3920 etc.). 

3921 

3922 .. note:: the ``"all"`` cascade is not accepted here. For a generic 

3923 object traversal function, see :ref:`faq_walk_objects`. 

3924 

3925 :param state: 

3926 The lead InstanceState. child items will be processed per 

3927 the relationships defined for this object's mapper. 

3928 

3929 :return: the method yields individual object instances. 

3930 

3931 .. seealso:: 

3932 

3933 :ref:`unitofwork_cascades` 

3934 

3935 :ref:`faq_walk_objects` - illustrates a generic function to 

3936 traverse all objects without relying on cascades. 

3937 

3938 """ 

3939 visited_states: Set[InstanceState[Any]] = set() 

3940 prp, mpp = object(), object() 

3941 

3942 assert state.mapper.isa(self) 

3943 

3944 # this is actually a recursive structure, fully typing it seems 

3945 # a little too difficult for what it's worth here 

3946 visitables: Deque[ 

3947 Tuple[ 

3948 Deque[Any], 

3949 object, 

3950 Optional[InstanceState[Any]], 

3951 Optional[_InstanceDict], 

3952 ] 

3953 ] 

3954 

3955 visitables = deque( 

3956 [(deque(state.mapper._props.values()), prp, state, state.dict)] 

3957 ) 

3958 

3959 while visitables: 

3960 iterator, item_type, parent_state, parent_dict = visitables[-1] 

3961 if not iterator: 

3962 visitables.pop() 

3963 continue 

3964 

3965 if item_type is prp: 

3966 prop = iterator.popleft() 

3967 if not prop.cascade or type_ not in prop.cascade: 

3968 continue 

3969 assert parent_state is not None 

3970 assert parent_dict is not None 

3971 queue = deque( 

3972 prop.cascade_iterator( 

3973 type_, 

3974 parent_state, 

3975 parent_dict, 

3976 visited_states, 

3977 halt_on, 

3978 ) 

3979 ) 

3980 if queue: 

3981 visitables.append((queue, mpp, None, None)) 

3982 elif item_type is mpp: 

3983 ( 

3984 instance, 

3985 instance_mapper, 

3986 corresponding_state, 

3987 corresponding_dict, 

3988 ) = iterator.popleft() 

3989 yield ( 

3990 instance, 

3991 instance_mapper, 

3992 corresponding_state, 

3993 corresponding_dict, 

3994 ) 

3995 visitables.append( 

3996 ( 

3997 deque(instance_mapper._props.values()), 

3998 prp, 

3999 corresponding_state, 

4000 corresponding_dict, 

4001 ) 

4002 ) 

4003 

4004 @HasMemoized.memoized_attribute 

4005 def _compiled_cache(self): 

4006 return util.LRUCache(self._compiled_cache_size) 

4007 

4008 @HasMemoized.memoized_attribute 

4009 def _multiple_persistence_tables(self): 

4010 return len(self.tables) > 1 

4011 

4012 @HasMemoized.memoized_attribute 

4013 def _sorted_tables(self): 

4014 table_to_mapper: Dict[TableClause, Mapper[Any]] = {} 

4015 

4016 for mapper in self.base_mapper.self_and_descendants: 

4017 for t in mapper.tables: 

4018 table_to_mapper.setdefault(t, mapper) 

4019 

4020 extra_dependencies = [] 

4021 for table, mapper in table_to_mapper.items(): 

4022 super_ = mapper.inherits 

4023 if super_: 

4024 extra_dependencies.extend( 

4025 [(super_table, table) for super_table in super_.tables] 

4026 ) 

4027 

4028 def skip(fk): 

4029 # attempt to skip dependencies that are not 

4030 # significant to the inheritance chain 

4031 # for two tables that are related by inheritance. 

4032 # while that dependency may be important, it's technically 

4033 # not what we mean to sort on here. 

4034 parent = table_to_mapper.get(fk.parent.table) 

4035 dep = table_to_mapper.get(fk.column.table) 

4036 if ( 

4037 parent is not None 

4038 and dep is not None 

4039 and dep is not parent 

4040 and dep.inherit_condition is not None 

4041 ): 

4042 cols = set(sql_util._find_columns(dep.inherit_condition)) 

4043 if parent.inherit_condition is not None: 

4044 cols = cols.union( 

4045 sql_util._find_columns(parent.inherit_condition) 

4046 ) 

4047 return fk.parent not in cols and fk.column not in cols 

4048 else: 

4049 return fk.parent not in cols 

4050 return False 

4051 

4052 sorted_ = sql_util.sort_tables( 

4053 table_to_mapper, 

4054 skip_fn=skip, 

4055 extra_dependencies=extra_dependencies, 

4056 ) 

4057 

4058 ret = util.OrderedDict() 

4059 for t in sorted_: 

4060 ret[t] = table_to_mapper[t] 

4061 return ret 

4062 

4063 def _memo(self, key: Any, callable_: Callable[[], _T]) -> _T: 

4064 if key in self._memoized_values: 

4065 return cast(_T, self._memoized_values[key]) 

4066 else: 

4067 self._memoized_values[key] = value = callable_() 

4068 return value 

4069 

4070 @util.memoized_property 

4071 def _table_to_equated(self): 

4072 """memoized map of tables to collections of columns to be 

4073 synchronized upwards to the base mapper.""" 

4074 

4075 result: util.defaultdict[ 

4076 Table, 

4077 List[ 

4078 Tuple[ 

4079 Mapper[Any], 

4080 List[Tuple[ColumnElement[Any], ColumnElement[Any]]], 

4081 ] 

4082 ], 

4083 ] = util.defaultdict(list) 

4084 

4085 def set_union(x, y): 

4086 return x.union(y) 

4087 

4088 for table in self._sorted_tables: 

4089 cols = set(table.c) 

4090 

4091 for m in self.iterate_to_root(): 

4092 if m._inherits_equated_pairs and cols.intersection( 

4093 reduce( 

4094 set_union, 

4095 [l.proxy_set for l, r in m._inherits_equated_pairs], 

4096 ) 

4097 ): 

4098 result[table].append((m, m._inherits_equated_pairs)) 

4099 

4100 return result 

4101 

4102 

4103class _OptGetColumnsNotAvailable(Exception): 

4104 pass 

4105 

4106 

4107def configure_mappers() -> None: 

4108 """Initialize the inter-mapper relationships of all mappers that 

4109 have been constructed thus far across all :class:`_orm.registry` 

4110 collections. 

4111 

4112 The configure step is used to reconcile and initialize the 

4113 :func:`_orm.relationship` linkages between mapped classes, as well as to 

4114 invoke configuration events such as the 

4115 :meth:`_orm.MapperEvents.before_configured` and 

4116 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM 

4117 extensions or user-defined extension hooks. 

4118 

4119 Mapper configuration is normally invoked automatically, the first time 

4120 mappings from a particular :class:`_orm.registry` are used, as well as 

4121 whenever mappings are used and additional not-yet-configured mappers have 

4122 been constructed. The automatic configuration process however is local only 

4123 to the :class:`_orm.registry` involving the target mapper and any related 

4124 :class:`_orm.registry` objects which it may depend on; this is 

4125 equivalent to invoking the :meth:`_orm.registry.configure` method 

4126 on a particular :class:`_orm.registry`. 

4127 

4128 By contrast, the :func:`_orm.configure_mappers` function will invoke the 

4129 configuration process on all :class:`_orm.registry` objects that 

4130 exist in memory, and may be useful for scenarios where many individual 

4131 :class:`_orm.registry` objects that are nonetheless interrelated are 

4132 in use. 

4133 

4134 .. versionchanged:: 1.4 

4135 

4136 As of SQLAlchemy 1.4.0b2, this function works on a 

4137 per-:class:`_orm.registry` basis, locating all :class:`_orm.registry` 

4138 objects present and invoking the :meth:`_orm.registry.configure` method 

4139 on each. The :meth:`_orm.registry.configure` method may be preferred to 

4140 limit the configuration of mappers to those local to a particular 

4141 :class:`_orm.registry` and/or declarative base class. 

4142 

4143 Points at which automatic configuration is invoked include when a mapped 

4144 class is instantiated into an instance, as well as when ORM queries 

4145 are emitted using :meth:`.Session.query` or :meth:`_orm.Session.execute` 

4146 with an ORM-enabled statement. 

4147 

4148 The mapper configure process, whether invoked by 

4149 :func:`_orm.configure_mappers` or from :meth:`_orm.registry.configure`, 

4150 provides several event hooks that can be used to augment the mapper 

4151 configuration step. These hooks include: 

4152 

4153 * :meth:`.MapperEvents.before_configured` - called once before 

4154 :func:`.configure_mappers` or :meth:`_orm.registry.configure` does any 

4155 work; this can be used to establish additional options, properties, or 

4156 related mappings before the operation proceeds. 

4157 

4158 * :meth:`.MapperEvents.mapper_configured` - called as each individual 

4159 :class:`_orm.Mapper` is configured within the process; will include all 

4160 mapper state except for backrefs set up by other mappers that are still 

4161 to be configured. 

4162 

4163 * :meth:`.MapperEvents.after_configured` - called once after 

4164 :func:`.configure_mappers` or :meth:`_orm.registry.configure` is 

4165 complete; at this stage, all :class:`_orm.Mapper` objects that fall 

4166 within the scope of the configuration operation will be fully configured. 

4167 Note that the calling application may still have other mappings that 

4168 haven't been produced yet, such as if they are in modules as yet 

4169 unimported, and may also have mappings that are still to be configured, 

4170 if they are in other :class:`_orm.registry` collections not part of the 

4171 current scope of configuration. 

4172 

4173 """ 

4174 

4175 _configure_registries(_all_registries(), cascade=True) 

4176 

4177 

4178def _configure_registries( 

4179 registries: Set[_RegistryType], cascade: bool 

4180) -> None: 

4181 for reg in registries: 

4182 if reg._new_mappers: 

4183 break 

4184 else: 

4185 return 

4186 

4187 with _CONFIGURE_MUTEX: 

4188 global _already_compiling 

4189 if _already_compiling: 

4190 return 

4191 _already_compiling = True 

4192 try: 

4193 # double-check inside mutex 

4194 for reg in registries: 

4195 if reg._new_mappers: 

4196 break 

4197 else: 

4198 return 

4199 

4200 Mapper.dispatch._for_class(Mapper).before_configured() # type: ignore # noqa: E501 

4201 # initialize properties on all mappers 

4202 # note that _mapper_registry is unordered, which 

4203 # may randomly conceal/reveal issues related to 

4204 # the order of mapper compilation 

4205 

4206 _do_configure_registries(registries, cascade) 

4207 finally: 

4208 _already_compiling = False 

4209 Mapper.dispatch._for_class(Mapper).after_configured() # type: ignore 

4210 

4211 

4212@util.preload_module("sqlalchemy.orm.decl_api") 

4213def _do_configure_registries( 

4214 registries: Set[_RegistryType], cascade: bool 

4215) -> None: 

4216 registry = util.preloaded.orm_decl_api.registry 

4217 

4218 orig = set(registries) 

4219 

4220 for reg in registry._recurse_with_dependencies(registries): 

4221 has_skip = False 

4222 

4223 for mapper in reg._mappers_to_configure(): 

4224 run_configure = None 

4225 

4226 for fn in mapper.dispatch.before_mapper_configured: 

4227 run_configure = fn(mapper, mapper.class_) 

4228 if run_configure is EXT_SKIP: 

4229 has_skip = True 

4230 break 

4231 if run_configure is EXT_SKIP: 

4232 continue 

4233 

4234 if getattr(mapper, "_configure_failed", False): 

4235 e = sa_exc.InvalidRequestError( 

4236 "One or more mappers failed to initialize - " 

4237 "can't proceed with initialization of other " 

4238 "mappers. Triggering mapper: '%s'. " 

4239 "Original exception was: %s" 

4240 % (mapper, mapper._configure_failed) 

4241 ) 

4242 e._configure_failed = mapper._configure_failed # type: ignore 

4243 raise e 

4244 

4245 if not mapper.configured: 

4246 try: 

4247 mapper._post_configure_properties() 

4248 mapper._expire_memoizations() 

4249 mapper.dispatch.mapper_configured(mapper, mapper.class_) 

4250 except Exception: 

4251 exc = sys.exc_info()[1] 

4252 if not hasattr(exc, "_configure_failed"): 

4253 mapper._configure_failed = exc 

4254 raise 

4255 if not has_skip: 

4256 reg._new_mappers = False 

4257 

4258 if not cascade and reg._dependencies.difference(orig): 

4259 raise sa_exc.InvalidRequestError( 

4260 "configure was called with cascade=False but " 

4261 "additional registries remain" 

4262 ) 

4263 

4264 

4265@util.preload_module("sqlalchemy.orm.decl_api") 

4266def _dispose_registries(registries: Set[_RegistryType], cascade: bool) -> None: 

4267 registry = util.preloaded.orm_decl_api.registry 

4268 

4269 orig = set(registries) 

4270 

4271 for reg in registry._recurse_with_dependents(registries): 

4272 if not cascade and reg._dependents.difference(orig): 

4273 raise sa_exc.InvalidRequestError( 

4274 "Registry has dependent registries that are not disposed; " 

4275 "pass cascade=True to clear these also" 

4276 ) 

4277 

4278 while reg._managers: 

4279 try: 

4280 manager, _ = reg._managers.popitem() 

4281 except KeyError: 

4282 # guard against race between while and popitem 

4283 pass 

4284 else: 

4285 reg._dispose_manager_and_mapper(manager) 

4286 

4287 reg._non_primary_mappers.clear() 

4288 reg._dependents.clear() 

4289 for dep in reg._dependencies: 

4290 dep._dependents.discard(reg) 

4291 reg._dependencies.clear() 

4292 # this wasn't done in the 1.3 clear_mappers() and in fact it 

4293 # was a bug, as it could cause configure_mappers() to invoke 

4294 # the "before_configured" event even though mappers had all been 

4295 # disposed. 

4296 reg._new_mappers = False 

4297 

4298 

4299def reconstructor(fn): 

4300 """Decorate a method as the 'reconstructor' hook. 

4301 

4302 Designates a single method as the "reconstructor", an ``__init__``-like 

4303 method that will be called by the ORM after the instance has been 

4304 loaded from the database or otherwise reconstituted. 

4305 

4306 .. tip:: 

4307 

4308 The :func:`_orm.reconstructor` decorator makes use of the 

4309 :meth:`_orm.InstanceEvents.load` event hook, which can be 

4310 used directly. 

4311 

4312 The reconstructor will be invoked with no arguments. Scalar 

4313 (non-collection) database-mapped attributes of the instance will 

4314 be available for use within the function. Eagerly-loaded 

4315 collections are generally not yet available and will usually only 

4316 contain the first element. ORM state changes made to objects at 

4317 this stage will not be recorded for the next flush() operation, so 

4318 the activity within a reconstructor should be conservative. 

4319 

4320 .. seealso:: 

4321 

4322 :meth:`.InstanceEvents.load` 

4323 

4324 """ 

4325 fn.__sa_reconstructor__ = True 

4326 return fn 

4327 

4328 

4329def validates( 

4330 *names: str, include_removes: bool = False, include_backrefs: bool = True 

4331) -> Callable[[_Fn], _Fn]: 

4332 r"""Decorate a method as a 'validator' for one or more named properties. 

4333 

4334 Designates a method as a validator, a method which receives the 

4335 name of the attribute as well as a value to be assigned, or in the 

4336 case of a collection, the value to be added to the collection. 

4337 The function can then raise validation exceptions to halt the 

4338 process from continuing (where Python's built-in ``ValueError`` 

4339 and ``AssertionError`` exceptions are reasonable choices), or can 

4340 modify or replace the value before proceeding. The function should 

4341 otherwise return the given value. 

4342 

4343 Note that a validator for a collection **cannot** issue a load of that 

4344 collection within the validation routine - this usage raises 

4345 an assertion to avoid recursion overflows. This is a reentrant 

4346 condition which is not supported. 

4347 

4348 :param \*names: list of attribute names to be validated. 

4349 :param include_removes: if True, "remove" events will be 

4350 sent as well - the validation function must accept an additional 

4351 argument "is_remove" which will be a boolean. 

4352 

4353 :param include_backrefs: defaults to ``True``; if ``False``, the 

4354 validation function will not emit if the originator is an attribute 

4355 event related via a backref. This can be used for bi-directional 

4356 :func:`.validates` usage where only one validator should emit per 

4357 attribute operation. 

4358 

4359 .. versionchanged:: 2.0.16 This paramter inadvertently defaulted to 

4360 ``False`` for releases 2.0.0 through 2.0.15. Its correct default 

4361 of ``True`` is restored in 2.0.16. 

4362 

4363 .. seealso:: 

4364 

4365 :ref:`simple_validators` - usage examples for :func:`.validates` 

4366 

4367 """ 

4368 

4369 def wrap(fn: _Fn) -> _Fn: 

4370 fn.__sa_validators__ = names # type: ignore[attr-defined] 

4371 fn.__sa_validation_opts__ = { # type: ignore[attr-defined] 

4372 "include_removes": include_removes, 

4373 "include_backrefs": include_backrefs, 

4374 } 

4375 return fn 

4376 

4377 return wrap 

4378 

4379 

4380def _event_on_load(state, ctx): 

4381 instrumenting_mapper = state.manager.mapper 

4382 

4383 if instrumenting_mapper._reconstructor: 

4384 instrumenting_mapper._reconstructor(state.obj()) 

4385 

4386 

4387def _event_on_init(state, args, kwargs): 

4388 """Run init_instance hooks. 

4389 

4390 This also includes mapper compilation, normally not needed 

4391 here but helps with some piecemeal configuration 

4392 scenarios (such as in the ORM tutorial). 

4393 

4394 """ 

4395 

4396 instrumenting_mapper = state.manager.mapper 

4397 if instrumenting_mapper: 

4398 instrumenting_mapper._check_configure() 

4399 if instrumenting_mapper._set_polymorphic_identity: 

4400 instrumenting_mapper._set_polymorphic_identity(state) 

4401 

4402 

4403class _ColumnMapping(Dict["ColumnElement[Any]", "MapperProperty[Any]"]): 

4404 """Error reporting helper for mapper._columntoproperty.""" 

4405 

4406 __slots__ = ("mapper",) 

4407 

4408 def __init__(self, mapper): 

4409 # TODO: weakref would be a good idea here 

4410 self.mapper = mapper 

4411 

4412 def __missing__(self, column): 

4413 prop = self.mapper._props.get(column) 

4414 if prop: 

4415 raise orm_exc.UnmappedColumnError( 

4416 "Column '%s.%s' is not available, due to " 

4417 "conflicting property '%s':%r" 

4418 % (column.table.name, column.name, column.key, prop) 

4419 ) 

4420 raise orm_exc.UnmappedColumnError( 

4421 "No column %s is configured on mapper %s..." 

4422 % (column, self.mapper) 

4423 )