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

1186 statements  

« prev     ^ index     » next       coverage.py v7.0.1, created at 2022-12-25 06:11 +0000

1# orm/mapper.py 

2# Copyright (C) 2005-2022 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 

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

9 

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

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

12 

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

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

15 

16""" 

17from __future__ import absolute_import 

18 

19from collections import deque 

20from itertools import chain 

21import sys 

22import weakref 

23 

24from . import attributes 

25from . import exc as orm_exc 

26from . import instrumentation 

27from . import loading 

28from . import properties 

29from . import util as orm_util 

30from .base import _class_to_mapper 

31from .base import _state_mapper 

32from .base import class_mapper 

33from .base import state_str 

34from .interfaces import _MappedAttribute 

35from .interfaces import EXT_SKIP 

36from .interfaces import InspectionAttr 

37from .interfaces import MapperProperty 

38from .interfaces import ORMEntityColumnsClauseRole 

39from .interfaces import ORMFromClauseRole 

40from .interfaces import StrategizedProperty 

41from .path_registry import PathRegistry 

42from .. import event 

43from .. import exc as sa_exc 

44from .. import inspection 

45from .. import log 

46from .. import schema 

47from .. import sql 

48from .. import util 

49from ..sql import base as sql_base 

50from ..sql import coercions 

51from ..sql import expression 

52from ..sql import operators 

53from ..sql import roles 

54from ..sql import util as sql_util 

55from ..sql import visitors 

56from ..sql.selectable import LABEL_STYLE_TABLENAME_PLUS_COL 

57from ..util import HasMemoized 

58 

59_mapper_registries = weakref.WeakKeyDictionary() 

60 

61_legacy_registry = None 

62 

63 

64def _all_registries(): 

65 with _CONFIGURE_MUTEX: 

66 return set(_mapper_registries) 

67 

68 

69def _unconfigured_mappers(): 

70 for reg in _all_registries(): 

71 for mapper in reg._mappers_to_configure(): 

72 yield mapper 

73 

74 

75_already_compiling = False 

76 

77 

78# a constant returned by _get_attr_by_column to indicate 

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

80# column 

81NO_ATTRIBUTE = util.symbol("NO_ATTRIBUTE") 

82 

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

84_CONFIGURE_MUTEX = util.threading.RLock() 

85 

86 

87@inspection._self_inspects 

88@log.class_logger 

89class Mapper( 

90 ORMFromClauseRole, 

91 ORMEntityColumnsClauseRole, 

92 sql_base.MemoizedHasCacheKey, 

93 InspectionAttr, 

94): 

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

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

97 proceed. 

98 

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

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

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

102 :ref:`orm_mapping_classes_toplevel`. 

103 

104 """ 

105 

106 _dispose_called = False 

107 _ready_for_configure = False 

108 

109 @util.deprecated_params( 

110 non_primary=( 

111 "1.3", 

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

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

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

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

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

117 ), 

118 ) 

119 def __init__( 

120 self, 

121 class_, 

122 local_table=None, 

123 properties=None, 

124 primary_key=None, 

125 non_primary=False, 

126 inherits=None, 

127 inherit_condition=None, 

128 inherit_foreign_keys=None, 

129 always_refresh=False, 

130 version_id_col=None, 

131 version_id_generator=None, 

132 polymorphic_on=None, 

133 _polymorphic_map=None, 

134 polymorphic_identity=None, 

135 concrete=False, 

136 with_polymorphic=None, 

137 polymorphic_load=None, 

138 allow_partial_pks=True, 

139 batch=True, 

140 column_prefix=None, 

141 include_properties=None, 

142 exclude_properties=None, 

143 passive_updates=True, 

144 passive_deletes=False, 

145 confirm_deleted_rows=True, 

146 eager_defaults=False, 

147 legacy_is_orphan=False, 

148 _compiled_cache_size=100, 

149 ): 

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

151 

152 The :func:`_orm.mapper` function is normally invoked through the 

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

154 :ref:`Declarative <orm_declarative_mapping>` or 

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

156 

157 .. versionchanged:: 1.4 The :func:`_orm.mapper` function should not 

158 be called directly for classical mapping; for a classical mapping 

159 configuration, use the :meth:`_orm.registry.map_imperatively` 

160 method. The :func:`_orm.mapper` function may become private in a 

161 future release. 

162 

163 Parameters documented below may be passed to either the 

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

165 ``__mapper_args__`` declarative class attribute described at 

166 :ref:`orm_declarative_mapper_options`. 

167 

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

169 this argument is automatically passed as the declared class 

170 itself. 

171 

172 :param local_table: The :class:`_schema.Table` or other selectable 

173 to which the class is mapped. May be ``None`` if 

174 this mapper inherits from another mapper using single-table 

175 inheritance. When using Declarative, this argument is 

176 automatically passed by the extension, based on what 

177 is configured via the ``__table__`` argument or via the 

178 :class:`_schema.Table` 

179 produced as a result of the ``__tablename__`` 

180 and :class:`_schema.Column` arguments present. 

181 

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

183 class will overwrite all data within object instances that already 

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

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

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

187 :meth:`_query.Query.populate_existing`. 

188 

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

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

191 possibly existing within the database. This affects whether a 

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

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

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

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

196 

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

198 of multiple entities can be batched together for efficiency. 

199 Setting to False indicates 

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

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

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

203 in between individual row persistence operations. 

204 

205 :param column_prefix: A string which will be prepended 

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

207 objects are automatically assigned as attributes to the 

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

209 are mapped explicitly in the :paramref:`.mapper.properties` 

210 dictionary. 

211 

212 This parameter is typically useful with imperative mappings 

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

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

215 ``user_id``, ``user_name``, and ``password``:: 

216 

217 class User(Base): 

218 __table__ = user_table 

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

220 

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

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

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

224 

225 The :paramref:`.mapper.column_prefix` parameter is uncommon in 

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

227 approach to automating a naming scheme is to intercept the 

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

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

230 pattern. 

231 

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

233 table inheritance with its parent mapper. 

234 

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

236 

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

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

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

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

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

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

243 exception in a future release. 

244 

245 .. versionadded:: 0.9.4 - added 

246 :paramref:`.mapper.confirm_deleted_rows` as well as conditional 

247 matched row checking on delete. 

248 

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

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

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

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

253 are needed immediately before the flush completes. By default, 

254 this scheme will emit an individual ``SELECT`` statement per row 

255 inserted or updated, which note can add significant performance 

256 overhead. However, if the 

257 target database supports :term:`RETURNING`, the default values will 

258 be returned inline with the INSERT or UPDATE statement, which can 

259 greatly enhance performance for an application that needs frequent 

260 access to just-generated server defaults. 

261 

262 .. seealso:: 

263 

264 :ref:`orm_server_defaults` 

265 

266 .. versionchanged:: 0.9.0 The ``eager_defaults`` option can now 

267 make use of :term:`RETURNING` for backends which support it. 

268 

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

270 be excluded from mapping. 

271 

272 See :ref:`include_exclude_cols` for an example. 

273 

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

275 names to map. 

276 

277 See :ref:`include_exclude_cols` for an example. 

278 

279 :param inherits: A mapped class or the corresponding 

280 :class:`_orm.Mapper` 

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

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

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

284 is passed automatically as a result of the natural class 

285 hierarchy of the declared classes. 

286 

287 .. seealso:: 

288 

289 :ref:`inheritance_toplevel` 

290 

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

292 expression which will 

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

294 between the two tables. 

295 

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

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

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

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

300 

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

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

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

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

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

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

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

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

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

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

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

312 orphan object has been flushed yet or not. 

313 

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

315 for more detail on this change. 

316 

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

318 is in addition 

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

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

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

322 only. 

323 

324 .. seealso:: 

325 

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

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

328 

329 :param passive_deletes: Indicates DELETE behavior of foreign key 

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

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

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

333 on the superclass mapper. 

334 

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

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

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

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

339 superclass table, and not this table. 

340 

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

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

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

344 validate these attributes; note that the primary key columns 

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

346 the object as a whole. 

347 

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

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

350 to specify passive_deletes without this taking effect for 

351 all subclass mappers. 

352 

353 .. versionadded:: 1.1 

354 

355 .. seealso:: 

356 

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

358 used with :func:`_orm.relationship` 

359 

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

361 CASCADE for joined-table inheritance mappers 

362 

363 :param passive_updates: Indicates UPDATE behavior of foreign key 

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

365 inheritance mapping. Defaults to ``True``. 

366 

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

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

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

370 on joined-table rows. 

371 

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

373 referential integrity and will not be issuing its own CASCADE 

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

375 emit an UPDATE statement for the dependent columns during a 

376 primary key change. 

377 

378 .. seealso:: 

379 

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

381 used with :func:`_orm.relationship` 

382 

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

384 CASCADE for joined-table inheritance mappers 

385 

386 :param polymorphic_load: Specifies "polymorphic loading" behavior 

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

388 table inheritance only). Valid values are: 

389 

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

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

392 in a SELECT query against the base. 

393 

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

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

396 the columns specific to this subclass. The SELECT uses 

397 IN to fetch multiple subclasses at once. 

398 

399 .. versionadded:: 1.2 

400 

401 .. seealso:: 

402 

403 :ref:`with_polymorphic_mapper_config` 

404 

405 :ref:`polymorphic_selectin` 

406 

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

408 SQL expression used to determine the target class for an 

409 incoming row, when inheriting classes are present. 

410 

411 This value is commonly a :class:`_schema.Column` object that's 

412 present in the mapped :class:`_schema.Table`:: 

413 

414 class Employee(Base): 

415 __tablename__ = 'employee' 

416 

417 id = Column(Integer, primary_key=True) 

418 discriminator = Column(String(50)) 

419 

420 __mapper_args__ = { 

421 "polymorphic_on":discriminator, 

422 "polymorphic_identity":"employee" 

423 } 

424 

425 It may also be specified 

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

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

428 approach:: 

429 

430 class Employee(Base): 

431 __tablename__ = 'employee' 

432 

433 id = Column(Integer, primary_key=True) 

434 discriminator = Column(String(50)) 

435 

436 __mapper_args__ = { 

437 "polymorphic_on":case([ 

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

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

440 ], else_="employee"), 

441 "polymorphic_identity":"employee" 

442 } 

443 

444 It may also refer to any attribute 

445 configured with :func:`.column_property`, or to the 

446 string name of one:: 

447 

448 class Employee(Base): 

449 __tablename__ = 'employee' 

450 

451 id = Column(Integer, primary_key=True) 

452 discriminator = Column(String(50)) 

453 employee_type = column_property( 

454 case([ 

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

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

457 ], else_="employee") 

458 ) 

459 

460 __mapper_args__ = { 

461 "polymorphic_on":employee_type, 

462 "polymorphic_identity":"employee" 

463 } 

464 

465 When setting ``polymorphic_on`` to reference an 

466 attribute or expression that's not present in the 

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

468 of the discriminator should be persisted to the database, 

469 the value of the 

470 discriminator is not automatically set on new 

471 instances; this must be handled by the user, 

472 either through manual means or via event listeners. 

473 A typical approach to establishing such a listener 

474 looks like:: 

475 

476 from sqlalchemy import event 

477 from sqlalchemy.orm import object_mapper 

478 

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

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

481 mapper = object_mapper(instance) 

482 instance.discriminator = mapper.polymorphic_identity 

483 

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

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

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

487 in the database. 

488 

489 .. warning:: 

490 

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

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

493 columns are not yet supported. 

494 

495 .. seealso:: 

496 

497 :ref:`inheritance_toplevel` 

498 

499 :param polymorphic_identity: Specifies the value which 

500 identifies this particular class as returned by the 

501 column expression referred to by the ``polymorphic_on`` 

502 setting. As rows are received, the value corresponding 

503 to the ``polymorphic_on`` column expression is compared 

504 to this value, indicating which subclass should 

505 be used for the newly reconstructed object. 

506 

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

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

509 persistence behavior of that attribute. Note that 

510 :class:`_schema.Column` 

511 objects present in 

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

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

514 When using Declarative, this argument is passed automatically, 

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

516 in the declared class body. 

517 

518 .. seealso:: 

519 

520 :ref:`orm_mapping_properties` - in the 

521 :ref:`orm_mapping_classes_toplevel` 

522 

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

524 objects which define 

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

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

527 can be overridden here. 

528 

529 .. seealso:: 

530 

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

532 

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

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

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

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

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

538 version id, a 

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

540 thrown. 

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

542 unless ``version_id_generator`` specifies an alternative version 

543 generator. 

544 

545 .. seealso:: 

546 

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

548 and rationale. 

549 

550 :param version_id_generator: Define how new version ids should 

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

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

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

554 

555 def generate_version(version): 

556 return next_version 

557 

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

559 or programmatic versioning schemes outside of the version id 

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

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

562 of important points when using this option. 

563 

564 .. versionadded:: 0.9.0 ``version_id_generator`` supports 

565 server-side version number generation. 

566 

567 .. seealso:: 

568 

569 :ref:`custom_version_counter` 

570 

571 :ref:`server_side_version_counter` 

572 

573 

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

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

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

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

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

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

580 loaded immediately. The second tuple argument <selectable> 

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

582 classes. 

583 

584 .. seealso:: 

585 

586 :ref:`with_polymorphic` - discussion of polymorphic querying 

587 techniques. 

588 

589 """ 

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

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

592 self.class_.__module__, 

593 self.class_.__name__, 

594 ) 

595 

596 self.class_manager = None 

597 

598 self._primary_key_argument = util.to_list(primary_key) 

599 self.non_primary = non_primary 

600 

601 self.always_refresh = always_refresh 

602 

603 if isinstance(version_id_col, MapperProperty): 

604 self.version_id_prop = version_id_col 

605 self.version_id_col = None 

606 else: 

607 self.version_id_col = version_id_col 

608 if version_id_generator is False: 

609 self.version_id_generator = False 

610 elif version_id_generator is None: 

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

612 else: 

613 self.version_id_generator = version_id_generator 

614 

615 self.concrete = concrete 

616 self.single = False 

617 self.inherits = inherits 

618 if local_table is not None: 

619 self.local_table = coercions.expect( 

620 roles.StrictFromClauseRole, local_table 

621 ) 

622 else: 

623 self.local_table = None 

624 

625 self.inherit_condition = inherit_condition 

626 self.inherit_foreign_keys = inherit_foreign_keys 

627 self._init_properties = properties or {} 

628 self._delete_orphans = [] 

629 self.batch = batch 

630 self.eager_defaults = eager_defaults 

631 self.column_prefix = column_prefix 

632 self.polymorphic_on = ( 

633 coercions.expect( 

634 roles.ColumnArgumentOrKeyRole, 

635 polymorphic_on, 

636 argname="polymorphic_on", 

637 ) 

638 if polymorphic_on is not None 

639 else None 

640 ) 

641 self._dependency_processors = [] 

642 self.validators = util.EMPTY_DICT 

643 self.passive_updates = passive_updates 

644 self.passive_deletes = passive_deletes 

645 self.legacy_is_orphan = legacy_is_orphan 

646 self._clause_adapter = None 

647 self._requires_row_aliasing = False 

648 self._inherits_equated_pairs = None 

649 self._memoized_values = {} 

650 self._compiled_cache_size = _compiled_cache_size 

651 self._reconstructor = None 

652 self.allow_partial_pks = allow_partial_pks 

653 

654 if self.inherits and not self.concrete: 

655 self.confirm_deleted_rows = False 

656 else: 

657 self.confirm_deleted_rows = confirm_deleted_rows 

658 

659 self._set_with_polymorphic(with_polymorphic) 

660 self.polymorphic_load = polymorphic_load 

661 

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

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

664 # the object instance for that row. 

665 self.polymorphic_identity = polymorphic_identity 

666 

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

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

669 # upon a select operation. 

670 if _polymorphic_map is None: 

671 self.polymorphic_map = {} 

672 else: 

673 self.polymorphic_map = _polymorphic_map 

674 

675 if include_properties is not None: 

676 self.include_properties = util.to_set(include_properties) 

677 else: 

678 self.include_properties = None 

679 if exclude_properties: 

680 self.exclude_properties = util.to_set(exclude_properties) 

681 else: 

682 self.exclude_properties = None 

683 

684 # prevent this mapper from being constructed 

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

686 # configure_mappers() until construction succeeds) 

687 with _CONFIGURE_MUTEX: 

688 self.dispatch._events._new_mapper_instance(class_, self) 

689 self._configure_inheritance() 

690 self._configure_class_instrumentation() 

691 self._configure_properties() 

692 self._configure_polymorphic_setter() 

693 self._configure_pks() 

694 self.registry._flag_new_mapper(self) 

695 self._log("constructed") 

696 self._expire_memoizations() 

697 

698 # major attributes initialized at the classlevel so that 

699 # they can be Sphinx-documented. 

700 

701 is_mapper = True 

702 """Part of the inspection API.""" 

703 

704 represents_outer_join = False 

705 

706 @property 

707 def mapper(self): 

708 """Part of the inspection API. 

709 

710 Returns self. 

711 

712 """ 

713 return self 

714 

715 def _gen_cache_key(self, anon_map, bindparams): 

716 return (self,) 

717 

718 @property 

719 def entity(self): 

720 r"""Part of the inspection API. 

721 

722 Returns self.class\_. 

723 

724 """ 

725 return self.class_ 

726 

727 local_table = None 

728 """The :class:`_expression.Selectable` which this :class:`_orm.Mapper` 

729 manages. 

730 

731 Typically is an instance of :class:`_schema.Table` or 

732 :class:`_expression.Alias`. 

733 May also be ``None``. 

734 

735 The "local" table is the 

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

737 managing from an attribute access and flush perspective. For 

738 non-inheriting mappers, the local table is the same as the 

739 "mapped" table. For joined-table inheritance mappers, local_table 

740 will be the particular sub-table of the overall "join" which 

741 this :class:`_orm.Mapper` represents. If this mapper is a 

742 single-table inheriting mapper, local_table will be ``None``. 

743 

744 .. seealso:: 

745 

746 :attr:`_orm.Mapper.persist_selectable`. 

747 

748 """ 

749 

750 persist_selectable = None 

751 """The :class:`_expression.Selectable` to which this :class:`_orm.Mapper` 

752 is mapped. 

753 

754 Typically an instance of :class:`_schema.Table`, 

755 :class:`_expression.Join`, or :class:`_expression.Alias`. 

756 

757 The :attr:`_orm.Mapper.persist_selectable` is separate from 

758 :attr:`_orm.Mapper.selectable` in that the former represents columns 

759 that are mapped on this class or its superclasses, whereas the 

760 latter may be a "polymorphic" selectable that contains additional columns 

761 which are in fact mapped on subclasses only. 

762 

763 "persist selectable" is the "thing the mapper writes to" and 

764 "selectable" is the "thing the mapper selects from". 

765 

766 :attr:`_orm.Mapper.persist_selectable` is also separate from 

767 :attr:`_orm.Mapper.local_table`, which represents the set of columns that 

768 are locally mapped on this class directly. 

769 

770 

771 .. seealso:: 

772 

773 :attr:`_orm.Mapper.selectable`. 

774 

775 :attr:`_orm.Mapper.local_table`. 

776 

777 """ 

778 

779 inherits = None 

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

781 inherits from, if any. 

782 

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

784 Behavior is undefined if directly modified. 

785 

786 """ 

787 

788 configured = False 

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

790 

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

792 Behavior is undefined if directly modified. 

793 

794 .. seealso:: 

795 

796 :func:`.configure_mappers`. 

797 

798 """ 

799 

800 concrete = None 

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

802 inheritance mapper. 

803 

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

805 Behavior is undefined if directly modified. 

806 

807 """ 

808 

809 tables = None 

810 """An iterable containing the collection of :class:`_schema.Table` objects 

811 which this :class:`_orm.Mapper` is aware of. 

812 

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

814 :class:`_expression.Alias` 

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

816 :class:`_schema.Table` 

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

818 

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

820 Behavior is undefined if directly modified. 

821 

822 """ 

823 

824 primary_key = None 

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

826 objects 

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

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

829 

830 This list is against the selectable in 

831 :attr:`_orm.Mapper.persist_selectable`. 

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

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

834 :class:`_expression.Join`, the 

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

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

837 

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

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

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

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

842 

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

844 Behavior is undefined if directly modified. 

845 

846 """ 

847 

848 class_ = None 

849 """The Python class which this :class:`_orm.Mapper` maps. 

850 

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

852 Behavior is undefined if directly modified. 

853 

854 """ 

855 

856 class_manager = None 

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

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

859 

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

861 Behavior is undefined if directly modified. 

862 

863 """ 

864 

865 single = None 

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

867 inheritance mapper. 

868 

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

870 

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

872 Behavior is undefined if directly modified. 

873 

874 """ 

875 

876 non_primary = None 

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

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

879 persistence management. 

880 

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

882 Behavior is undefined if directly modified. 

883 

884 """ 

885 

886 polymorphic_on = None 

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

888 ``polymorphic_on`` argument 

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

890 

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

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

893 :func:`.cast`. 

894 

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

896 Behavior is undefined if directly modified. 

897 

898 """ 

899 

900 polymorphic_map = None 

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

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

903 

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

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

906 

907 An inheritance chain of mappers will all reference the same 

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

909 result rows to target mappers. 

910 

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

912 Behavior is undefined if directly modified. 

913 

914 """ 

915 

916 polymorphic_identity = None 

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

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

919 

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

921 comparable to the type of column represented by 

922 :attr:`_orm.Mapper.polymorphic_on`. 

923 

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

925 Behavior is undefined if directly modified. 

926 

927 """ 

928 

929 base_mapper = None 

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

931 

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

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

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

935 objects in the inheritance chain. 

936 

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

938 Behavior is undefined if directly modified. 

939 

940 """ 

941 

942 columns = None 

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

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

945 

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

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

948 except that only those columns included in 

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

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

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

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

953 

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

955 Behavior is undefined if directly modified. 

956 

957 """ 

958 

959 validators = None 

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

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

962 

963 The dictionary contains string attribute names as keys 

964 mapped to the actual validation method. 

965 

966 """ 

967 

968 c = None 

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

970 

971 @property 

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

973 def mapped_table(self): 

974 return self.persist_selectable 

975 

976 @util.memoized_property 

977 def _path_registry(self): 

978 return PathRegistry.per_mapper(self) 

979 

980 def _configure_inheritance(self): 

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

982 being present.""" 

983 

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

985 self._inheriting_mappers = util.WeakSequence() 

986 

987 if self.inherits: 

988 if isinstance(self.inherits, type): 

989 self.inherits = class_mapper(self.inherits, configure=False) 

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

991 raise sa_exc.ArgumentError( 

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

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

994 ) 

995 

996 self.dispatch._update(self.inherits.dispatch) 

997 

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

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

1000 raise sa_exc.ArgumentError( 

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

1002 "only allowed from a %s mapper" 

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

1004 ) 

1005 # inherit_condition is optional. 

1006 if self.local_table is None: 

1007 self.local_table = self.inherits.local_table 

1008 self.persist_selectable = self.inherits.persist_selectable 

1009 self.single = True 

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

1011 if self.concrete: 

1012 self.persist_selectable = self.local_table 

1013 for mapper in self.iterate_to_root(): 

1014 if mapper.polymorphic_on is not None: 

1015 mapper._requires_row_aliasing = True 

1016 else: 

1017 if self.inherit_condition is None: 

1018 # figure out inherit condition from our table to the 

1019 # immediate table of the inherited mapper, not its 

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

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

1022 try: 

1023 self.inherit_condition = sql_util.join_condition( 

1024 self.inherits.local_table, self.local_table 

1025 ) 

1026 except sa_exc.NoForeignKeysError as nfe: 

1027 assert self.inherits.local_table is not None 

1028 assert self.local_table is not None 

1029 util.raise_( 

1030 sa_exc.NoForeignKeysError( 

1031 "Can't determine the inherit condition " 

1032 "between inherited table '%s' and " 

1033 "inheriting " 

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

1035 "foreign key relationships established. " 

1036 "Please ensure the inheriting table has " 

1037 "a foreign key relationship to the " 

1038 "inherited " 

1039 "table, or provide an " 

1040 "'on clause' using " 

1041 "the 'inherit_condition' mapper argument." 

1042 % ( 

1043 self.inherits.local_table.description, 

1044 self.local_table.description, 

1045 ) 

1046 ), 

1047 replace_context=nfe, 

1048 ) 

1049 except sa_exc.AmbiguousForeignKeysError as afe: 

1050 assert self.inherits.local_table is not None 

1051 assert self.local_table is not None 

1052 util.raise_( 

1053 sa_exc.AmbiguousForeignKeysError( 

1054 "Can't determine the inherit condition " 

1055 "between inherited table '%s' and " 

1056 "inheriting " 

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

1058 "foreign key relationship established. " 

1059 "Please specify the 'on clause' using " 

1060 "the 'inherit_condition' mapper argument." 

1061 % ( 

1062 self.inherits.local_table.description, 

1063 self.local_table.description, 

1064 ) 

1065 ), 

1066 replace_context=afe, 

1067 ) 

1068 self.persist_selectable = sql.join( 

1069 self.inherits.persist_selectable, 

1070 self.local_table, 

1071 self.inherit_condition, 

1072 ) 

1073 

1074 fks = util.to_set(self.inherit_foreign_keys) 

1075 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 

1076 self.persist_selectable.onclause, 

1077 consider_as_foreign_keys=fks, 

1078 ) 

1079 else: 

1080 self.persist_selectable = self.local_table 

1081 

1082 if self.polymorphic_identity is None: 

1083 self._identity_class = self.class_ 

1084 

1085 if self.inherits.base_mapper.polymorphic_on is not None: 

1086 util.warn( 

1087 "Mapper %s does not indicate a polymorphic_identity, " 

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

1089 "polymorphic_on column of '%s'. Objects of this type " 

1090 "cannot be loaded polymorphically which can lead to " 

1091 "degraded or incorrect loading behavior in some " 

1092 "scenarios. Please establish a polmorphic_identity " 

1093 "for this class, or leave it un-mapped. " 

1094 "To omit mapping an intermediary class when using " 

1095 "declarative, set the '__abstract__ = True' " 

1096 "attribute on that class." 

1097 % (self, self.inherits.base_mapper.polymorphic_on) 

1098 ) 

1099 elif self.concrete: 

1100 self._identity_class = self.class_ 

1101 else: 

1102 self._identity_class = self.inherits._identity_class 

1103 

1104 if self.version_id_col is None: 

1105 self.version_id_col = self.inherits.version_id_col 

1106 self.version_id_generator = self.inherits.version_id_generator 

1107 elif ( 

1108 self.inherits.version_id_col is not None 

1109 and self.version_id_col is not self.inherits.version_id_col 

1110 ): 

1111 util.warn( 

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

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

1114 "the inherited versioning column. " 

1115 "version_id_col should only be specified on " 

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

1117 % ( 

1118 self.version_id_col.description, 

1119 self.inherits.version_id_col.description, 

1120 ) 

1121 ) 

1122 

1123 self.polymorphic_map = self.inherits.polymorphic_map 

1124 self.batch = self.inherits.batch 

1125 self.inherits._inheriting_mappers.append(self) 

1126 self.base_mapper = self.inherits.base_mapper 

1127 self.passive_updates = self.inherits.passive_updates 

1128 self.passive_deletes = ( 

1129 self.inherits.passive_deletes or self.passive_deletes 

1130 ) 

1131 self._all_tables = self.inherits._all_tables 

1132 

1133 if self.polymorphic_identity is not None: 

1134 if self.polymorphic_identity in self.polymorphic_map: 

1135 util.warn( 

1136 "Reassigning polymorphic association for identity %r " 

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

1138 "value for polymorphic_identity." 

1139 % ( 

1140 self.polymorphic_identity, 

1141 self.polymorphic_map[self.polymorphic_identity], 

1142 self, 

1143 self.polymorphic_identity, 

1144 ) 

1145 ) 

1146 self.polymorphic_map[self.polymorphic_identity] = self 

1147 

1148 if self.polymorphic_load and self.concrete: 

1149 raise sa_exc.ArgumentError( 

1150 "polymorphic_load is not currently supported " 

1151 "with concrete table inheritance" 

1152 ) 

1153 if self.polymorphic_load == "inline": 

1154 self.inherits._add_with_polymorphic_subclass(self) 

1155 elif self.polymorphic_load == "selectin": 

1156 pass 

1157 elif self.polymorphic_load is not None: 

1158 raise sa_exc.ArgumentError( 

1159 "unknown argument for polymorphic_load: %r" 

1160 % self.polymorphic_load 

1161 ) 

1162 

1163 else: 

1164 self._all_tables = set() 

1165 self.base_mapper = self 

1166 self.persist_selectable = self.local_table 

1167 if self.polymorphic_identity is not None: 

1168 self.polymorphic_map[self.polymorphic_identity] = self 

1169 self._identity_class = self.class_ 

1170 

1171 if self.persist_selectable is None: 

1172 raise sa_exc.ArgumentError( 

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

1174 % self 

1175 ) 

1176 

1177 def _set_with_polymorphic(self, with_polymorphic): 

1178 if with_polymorphic == "*": 

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

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

1181 if isinstance( 

1182 with_polymorphic[0], util.string_types + (tuple, list) 

1183 ): 

1184 self.with_polymorphic = with_polymorphic 

1185 else: 

1186 self.with_polymorphic = (with_polymorphic, None) 

1187 elif with_polymorphic is not None: 

1188 raise sa_exc.ArgumentError("Invalid setting for with_polymorphic") 

1189 else: 

1190 self.with_polymorphic = None 

1191 

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

1193 self.with_polymorphic = ( 

1194 self.with_polymorphic[0], 

1195 coercions.expect( 

1196 roles.StrictFromClauseRole, 

1197 self.with_polymorphic[1], 

1198 allow_select=True, 

1199 ), 

1200 ) 

1201 

1202 if self.configured: 

1203 self._expire_memoizations() 

1204 

1205 def _add_with_polymorphic_subclass(self, mapper): 

1206 subcl = mapper.class_ 

1207 if self.with_polymorphic is None: 

1208 self._set_with_polymorphic((subcl,)) 

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

1210 self._set_with_polymorphic( 

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

1212 ) 

1213 

1214 def _set_concrete_base(self, mapper): 

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

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

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

1218 

1219 assert self.concrete 

1220 assert not self.inherits 

1221 assert isinstance(mapper, Mapper) 

1222 self.inherits = mapper 

1223 self.inherits.polymorphic_map.update(self.polymorphic_map) 

1224 self.polymorphic_map = self.inherits.polymorphic_map 

1225 for mapper in self.iterate_to_root(): 

1226 if mapper.polymorphic_on is not None: 

1227 mapper._requires_row_aliasing = True 

1228 self.batch = self.inherits.batch 

1229 for mp in self.self_and_descendants: 

1230 mp.base_mapper = self.inherits.base_mapper 

1231 self.inherits._inheriting_mappers.append(self) 

1232 self.passive_updates = self.inherits.passive_updates 

1233 self._all_tables = self.inherits._all_tables 

1234 

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

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

1237 key, key, local=False, column=None 

1238 ): 

1239 self._adapt_inherited_property(key, prop, False) 

1240 

1241 def _set_polymorphic_on(self, polymorphic_on): 

1242 self.polymorphic_on = polymorphic_on 

1243 self._configure_polymorphic_setter(True) 

1244 

1245 def _configure_class_instrumentation(self): 

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

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

1248 given class and entity name. 

1249 

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

1251 name combination will return this mapper. Also decorate the 

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

1253 auto-session attachment logic. 

1254 

1255 """ 

1256 

1257 # we expect that declarative has applied the class manager 

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

1259 # we will emit a deprecation warning below when we also see that 

1260 # it has no registry. 

1261 manager = attributes.manager_of_class(self.class_) 

1262 

1263 if self.non_primary: 

1264 if not manager or not manager.is_mapped: 

1265 raise sa_exc.InvalidRequestError( 

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

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

1268 "Mapper." % self.class_ 

1269 ) 

1270 self.class_manager = manager 

1271 self.registry = manager.registry 

1272 self._identity_class = manager.mapper._identity_class 

1273 manager.registry._add_non_primary_mapper(self) 

1274 return 

1275 

1276 if manager is not None: 

1277 assert manager.class_ is self.class_ 

1278 if manager.is_mapped: 

1279 # changed in #7579: 

1280 # this message is defined in two places as of this change, 

1281 # also in decl_api -> _add_manager(). in 2.0, this codepath 

1282 # is removed as any calls to mapper() / Mapper without 

1283 # the registry setting up first will be rejected. 

1284 raise sa_exc.ArgumentError( 

1285 "Class '%s' already has a primary mapper defined. " 

1286 % self.class_ 

1287 ) 

1288 # else: 

1289 # a ClassManager may already exist as 

1290 # ClassManager.instrument_attribute() creates 

1291 # new managers for each subclass if they don't yet exist. 

1292 

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

1294 

1295 # this invokes the class_instrument event and sets up 

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

1297 # occur after the instrument_class event above. 

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

1299 # :( 

1300 

1301 manager = instrumentation.register_class( 

1302 self.class_, 

1303 mapper=self, 

1304 expired_attribute_loader=util.partial( 

1305 loading.load_scalar_attributes, self 

1306 ), 

1307 # finalize flag means instrument the __init__ method 

1308 # and call the class_instrument event 

1309 finalize=True, 

1310 ) 

1311 

1312 if not manager.registry: 

1313 util.warn_deprecated_20( 

1314 "Calling the mapper() function directly outside of a " 

1315 "declarative registry is deprecated." 

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

1317 "function for a classical mapping." 

1318 ) 

1319 assert _legacy_registry is not None 

1320 _legacy_registry._add_manager(manager) 

1321 

1322 self.class_manager = manager 

1323 self.registry = manager.registry 

1324 

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

1326 # e_name None or not. 

1327 if manager.mapper is None: 

1328 return 

1329 

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

1331 

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

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

1334 method = method._sa_original_init 

1335 if hasattr(method, "__func__"): 

1336 method = method.__func__ 

1337 if callable(method): 

1338 if hasattr(method, "__sa_reconstructor__"): 

1339 self._reconstructor = method 

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

1341 elif hasattr(method, "__sa_validators__"): 

1342 validation_opts = method.__sa_validation_opts__ 

1343 for name in method.__sa_validators__: 

1344 if name in self.validators: 

1345 raise sa_exc.InvalidRequestError( 

1346 "A validation function for mapped " 

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

1348 % (name, self) 

1349 ) 

1350 self.validators = self.validators.union( 

1351 {name: (method, validation_opts)} 

1352 ) 

1353 

1354 def _set_dispose_flags(self): 

1355 self.configured = True 

1356 self._ready_for_configure = True 

1357 self._dispose_called = True 

1358 

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

1360 

1361 def _configure_pks(self): 

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

1363 

1364 self._pks_by_table = {} 

1365 self._cols_by_table = {} 

1366 

1367 all_cols = util.column_set( 

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

1369 ) 

1370 

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

1372 

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

1374 tables = set(self.tables + [self.persist_selectable]) 

1375 self._all_tables.update(tables) 

1376 for t in tables: 

1377 if t.primary_key and pk_cols.issuperset(t.primary_key): 

1378 # ordering is important since it determines the ordering of 

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

1380 self._pks_by_table[t] = util.ordered_column_set( 

1381 t.primary_key 

1382 ).intersection(pk_cols) 

1383 self._cols_by_table[t] = util.ordered_column_set(t.c).intersection( 

1384 all_cols 

1385 ) 

1386 

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

1388 # primary key mappings 

1389 if self._primary_key_argument: 

1390 for k in self._primary_key_argument: 

1391 if k.table not in self._pks_by_table: 

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

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

1394 

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

1396 elif ( 

1397 self.persist_selectable not in self._pks_by_table 

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

1399 ): 

1400 raise sa_exc.ArgumentError( 

1401 "Mapper %s could not assemble any primary " 

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

1403 % (self, self.persist_selectable.description) 

1404 ) 

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

1406 self.local_table, schema.Table 

1407 ): 

1408 util.warn( 

1409 "Could not assemble any primary " 

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

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

1412 % self.local_table.description 

1413 ) 

1414 

1415 if ( 

1416 self.inherits 

1417 and not self.concrete 

1418 and not self._primary_key_argument 

1419 ): 

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

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

1422 self.primary_key = self.inherits.primary_key 

1423 else: 

1424 # determine primary key from argument or persist_selectable pks 

1425 if self._primary_key_argument: 

1426 primary_key = [ 

1427 self.persist_selectable.corresponding_column(c) 

1428 for c in self._primary_key_argument 

1429 ] 

1430 else: 

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

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

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

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

1435 primary_key = sql_util.reduce_columns( 

1436 self._pks_by_table[self.persist_selectable], 

1437 ignore_nonexistent_tables=True, 

1438 ) 

1439 

1440 if len(primary_key) == 0: 

1441 raise sa_exc.ArgumentError( 

1442 "Mapper %s could not assemble any primary " 

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

1444 % (self, self.persist_selectable.description) 

1445 ) 

1446 

1447 self.primary_key = tuple(primary_key) 

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

1449 

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

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

1452 self._readonly_props = set( 

1453 self._columntoproperty[col] 

1454 for col in self._columntoproperty 

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

1456 and ( 

1457 not hasattr(col, "table") 

1458 or col.table not in self._cols_by_table 

1459 ) 

1460 ) 

1461 

1462 def _configure_properties(self): 

1463 

1464 # TODO: consider using DedupeColumnCollection 

1465 self.columns = self.c = sql_base.ColumnCollection() 

1466 

1467 # object attribute names mapped to MapperProperty objects 

1468 self._props = util.OrderedDict() 

1469 

1470 # table columns mapped to MapperProperty 

1471 self._columntoproperty = _ColumnMapping(self) 

1472 

1473 # load custom properties 

1474 if self._init_properties: 

1475 for key, prop in self._init_properties.items(): 

1476 self._configure_property(key, prop, False) 

1477 

1478 # pull properties from the inherited mapper if any. 

1479 if self.inherits: 

1480 for key, prop in self.inherits._props.items(): 

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

1482 key, key, local=False, column=None 

1483 ): 

1484 self._adapt_inherited_property(key, prop, False) 

1485 

1486 # create properties for each column in the mapped table, 

1487 # for those columns which don't already map to a property 

1488 for column in self.persist_selectable.columns: 

1489 if column in self._columntoproperty: 

1490 continue 

1491 

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

1493 

1494 if self._should_exclude( 

1495 column.key, 

1496 column_key, 

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

1498 column=column, 

1499 ): 

1500 continue 

1501 

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

1503 # of the inheriting mapper 

1504 for mapper in self.iterate_to_root(): 

1505 if column in mapper._columntoproperty: 

1506 column_key = mapper._columntoproperty[column].key 

1507 

1508 self._configure_property( 

1509 column_key, column, init=False, setparent=True 

1510 ) 

1511 

1512 def _configure_polymorphic_setter(self, init=False): 

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

1514 'polymorphic_on' column, if applicable, and not 

1515 already generated by _configure_properties (which is typical). 

1516 

1517 Also create a setter function which will assign this 

1518 attribute to the value of the 'polymorphic_identity' 

1519 upon instance construction, also if applicable. This 

1520 routine will run when an instance is created. 

1521 

1522 """ 

1523 setter = False 

1524 

1525 if self.polymorphic_on is not None: 

1526 setter = True 

1527 

1528 if isinstance(self.polymorphic_on, util.string_types): 

1529 # polymorphic_on specified as a string - link 

1530 # it to mapped ColumnProperty 

1531 try: 

1532 self.polymorphic_on = self._props[self.polymorphic_on] 

1533 except KeyError as err: 

1534 util.raise_( 

1535 sa_exc.ArgumentError( 

1536 "Can't determine polymorphic_on " 

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

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

1539 ), 

1540 replace_context=err, 

1541 ) 

1542 

1543 if self.polymorphic_on in self._columntoproperty: 

1544 # polymorphic_on is a column that is already mapped 

1545 # to a ColumnProperty 

1546 prop = self._columntoproperty[self.polymorphic_on] 

1547 elif isinstance(self.polymorphic_on, MapperProperty): 

1548 # polymorphic_on is directly a MapperProperty, 

1549 # ensure it's a ColumnProperty 

1550 if not isinstance( 

1551 self.polymorphic_on, properties.ColumnProperty 

1552 ): 

1553 raise sa_exc.ArgumentError( 

1554 "Only direct column-mapped " 

1555 "property or SQL expression " 

1556 "can be passed for polymorphic_on" 

1557 ) 

1558 prop = self.polymorphic_on 

1559 else: 

1560 # polymorphic_on is a Column or SQL expression and 

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

1562 # only present in the with_polymorphic selectable or 

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

1564 # hope is compatible with this mapper's persist_selectable 

1565 col = self.persist_selectable.corresponding_column( 

1566 self.polymorphic_on 

1567 ) 

1568 if col is None: 

1569 # polymorphic_on doesn't derive from any 

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

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

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

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

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

1575 # represented somehow in either persist_selectable or 

1576 # with_polymorphic. Otherwise as of 0.7.4 we 

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

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

1579 setter = False 

1580 instrument = False 

1581 col = self.polymorphic_on 

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

1583 self.with_polymorphic is None 

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

1585 is None 

1586 ): 

1587 raise sa_exc.InvalidRequestError( 

1588 "Could not map polymorphic_on column " 

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

1590 "loads will not function properly" 

1591 % col.description 

1592 ) 

1593 else: 

1594 # column/expression that polymorphic_on derives from 

1595 # is present in our mapped table 

1596 # and is probably mapped, but polymorphic_on itself 

1597 # is not. This happens when 

1598 # the polymorphic_on is only directly present in the 

1599 # with_polymorphic selectable, as when use 

1600 # polymorphic_union. 

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

1602 instrument = True 

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

1604 if key: 

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

1606 raise sa_exc.InvalidRequestError( 

1607 "Cannot exclude or override the " 

1608 "discriminator column %r" % col.key 

1609 ) 

1610 else: 

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

1612 key = col.key 

1613 

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

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

1616 

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

1618 # column in the property 

1619 self.polymorphic_on = prop.columns[0] 

1620 polymorphic_key = prop.key 

1621 

1622 else: 

1623 # no polymorphic_on was set. 

1624 # check inheriting mappers for one. 

1625 for mapper in self.iterate_to_root(): 

1626 # determine if polymorphic_on of the parent 

1627 # should be propagated here. If the col 

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

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

1630 # inheritance), we can use it 

1631 if mapper.polymorphic_on is not None: 

1632 if self.persist_selectable is mapper.persist_selectable: 

1633 self.polymorphic_on = mapper.polymorphic_on 

1634 else: 

1635 self.polymorphic_on = ( 

1636 self.persist_selectable 

1637 ).corresponding_column(mapper.polymorphic_on) 

1638 # we can use the parent mapper's _set_polymorphic_identity 

1639 # directly; it ensures the polymorphic_identity of the 

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

1641 if self.polymorphic_on is not None: 

1642 self._set_polymorphic_identity = ( 

1643 mapper._set_polymorphic_identity 

1644 ) 

1645 self._validate_polymorphic_identity = ( 

1646 mapper._validate_polymorphic_identity 

1647 ) 

1648 else: 

1649 self._set_polymorphic_identity = None 

1650 return 

1651 

1652 if setter: 

1653 

1654 def _set_polymorphic_identity(state): 

1655 dict_ = state.dict 

1656 state.get_impl(polymorphic_key).set( 

1657 state, 

1658 dict_, 

1659 state.manager.mapper.polymorphic_identity, 

1660 None, 

1661 ) 

1662 

1663 def _validate_polymorphic_identity(mapper, state, dict_): 

1664 if ( 

1665 polymorphic_key in dict_ 

1666 and dict_[polymorphic_key] 

1667 not in mapper._acceptable_polymorphic_identities 

1668 ): 

1669 util.warn_limited( 

1670 "Flushing object %s with " 

1671 "incompatible polymorphic identity %r; the " 

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

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

1674 ) 

1675 

1676 self._set_polymorphic_identity = _set_polymorphic_identity 

1677 self._validate_polymorphic_identity = ( 

1678 _validate_polymorphic_identity 

1679 ) 

1680 else: 

1681 self._set_polymorphic_identity = None 

1682 

1683 _validate_polymorphic_identity = None 

1684 

1685 @HasMemoized.memoized_attribute 

1686 def _version_id_prop(self): 

1687 if self.version_id_col is not None: 

1688 return self._columntoproperty[self.version_id_col] 

1689 else: 

1690 return None 

1691 

1692 @HasMemoized.memoized_attribute 

1693 def _acceptable_polymorphic_identities(self): 

1694 identities = set() 

1695 

1696 stack = deque([self]) 

1697 while stack: 

1698 item = stack.popleft() 

1699 if item.persist_selectable is self.persist_selectable: 

1700 identities.add(item.polymorphic_identity) 

1701 stack.extend(item._inheriting_mappers) 

1702 

1703 return identities 

1704 

1705 @HasMemoized.memoized_attribute 

1706 def _prop_set(self): 

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

1708 

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

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

1711 descriptor_props = util.preloaded.orm_descriptor_props 

1712 

1713 if not self.concrete: 

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

1715 elif key not in self._props: 

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

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

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

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

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

1721 # descriptors that might have side effects when invoked. 

1722 implementing_attribute = self.class_manager._get_class_attr_mro( 

1723 key, prop 

1724 ) 

1725 if implementing_attribute is prop or ( 

1726 isinstance( 

1727 implementing_attribute, attributes.InstrumentedAttribute 

1728 ) 

1729 and implementing_attribute._parententity is prop.parent 

1730 ): 

1731 self._configure_property( 

1732 key, 

1733 descriptor_props.ConcreteInheritedProperty(), 

1734 init=init, 

1735 setparent=True, 

1736 ) 

1737 

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

1739 def _configure_property(self, key, prop, init=True, setparent=True): 

1740 descriptor_props = util.preloaded.orm_descriptor_props 

1741 self._log("_configure_property(%s, %s)", key, prop.__class__.__name__) 

1742 

1743 if not isinstance(prop, MapperProperty): 

1744 prop = self._property_from_column(key, prop) 

1745 

1746 if isinstance(prop, properties.ColumnProperty): 

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

1748 

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

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

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

1752 if col is None and self.inherits: 

1753 path = [self] 

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

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

1756 if col is not None: 

1757 for m2 in path: 

1758 m2.persist_selectable._refresh_for_new_column(col) 

1759 col = self.persist_selectable.corresponding_column( 

1760 prop.columns[0] 

1761 ) 

1762 break 

1763 path.append(m) 

1764 

1765 # subquery expression, column not present in the mapped 

1766 # selectable. 

1767 if col is None: 

1768 col = prop.columns[0] 

1769 

1770 # column is coming in after _readonly_props was 

1771 # initialized; check for 'readonly' 

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

1773 not hasattr(col, "table") 

1774 or col.table not in self._cols_by_table 

1775 ): 

1776 self._readonly_props.add(prop) 

1777 

1778 else: 

1779 # if column is coming in after _cols_by_table was 

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

1781 if ( 

1782 hasattr(self, "_cols_by_table") 

1783 and col.table in self._cols_by_table 

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

1785 ): 

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

1787 

1788 # if this properties.ColumnProperty represents the "polymorphic 

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

1790 # columns in SELECT statements. 

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

1792 prop._is_polymorphic_discriminator = ( 

1793 col is self.polymorphic_on 

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

1795 ) 

1796 

1797 if isinstance(col, expression.Label): 

1798 # new in 1.4, get column property against expressions 

1799 # to be addressable in subqueries 

1800 col.key = col._tq_key_label = key 

1801 

1802 self.columns.add(col, key) 

1803 for col in prop.columns: 

1804 for col in col.proxy_set: 

1805 self._columntoproperty[col] = prop 

1806 

1807 prop.key = key 

1808 

1809 if setparent: 

1810 prop.set_parent(self, init) 

1811 

1812 if key in self._props and getattr( 

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

1814 ): 

1815 syn = self._props[key]._mapped_by_synonym 

1816 raise sa_exc.ArgumentError( 

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

1818 "a ColumnProperty already exists keyed to the name " 

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

1820 ) 

1821 

1822 if ( 

1823 key in self._props 

1824 and not isinstance(prop, properties.ColumnProperty) 

1825 and not isinstance( 

1826 self._props[key], 

1827 ( 

1828 properties.ColumnProperty, 

1829 descriptor_props.ConcreteInheritedProperty, 

1830 ), 

1831 ) 

1832 ): 

1833 util.warn( 

1834 "Property %s on %s being replaced with new " 

1835 "property %s; the old property will be discarded" 

1836 % (self._props[key], self, prop) 

1837 ) 

1838 oldprop = self._props[key] 

1839 self._path_registry.pop(oldprop, None) 

1840 

1841 self._props[key] = prop 

1842 

1843 if not self.non_primary: 

1844 prop.instrument_class(self) 

1845 

1846 for mapper in self._inheriting_mappers: 

1847 mapper._adapt_inherited_property(key, prop, init) 

1848 

1849 if init: 

1850 prop.init() 

1851 prop.post_instrument_class(self) 

1852 

1853 if self.configured: 

1854 self._expire_memoizations() 

1855 

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

1857 def _property_from_column(self, key, prop): 

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

1859 :class:`_schema.Column` object.""" 

1860 descriptor_props = util.preloaded.orm_descriptor_props 

1861 # we were passed a Column or a list of Columns; 

1862 # generate a properties.ColumnProperty 

1863 columns = util.to_list(prop) 

1864 column = columns[0] 

1865 assert isinstance(column, expression.ColumnElement) 

1866 

1867 prop = self._props.get(key, None) 

1868 

1869 if isinstance(prop, properties.ColumnProperty): 

1870 if ( 

1871 ( 

1872 not self._inherits_equated_pairs 

1873 or (prop.columns[0], column) 

1874 not in self._inherits_equated_pairs 

1875 ) 

1876 and not prop.columns[0].shares_lineage(column) 

1877 and prop.columns[0] is not self.version_id_col 

1878 and column is not self.version_id_col 

1879 ): 

1880 warn_only = prop.parent is not self 

1881 msg = ( 

1882 "Implicitly combining column %s with column " 

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

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

1885 "explicitly." % (prop.columns[-1], column, key) 

1886 ) 

1887 if warn_only: 

1888 util.warn(msg) 

1889 else: 

1890 raise sa_exc.InvalidRequestError(msg) 

1891 

1892 # existing properties.ColumnProperty from an inheriting 

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

1894 prop = prop.copy() 

1895 prop.columns.insert(0, column) 

1896 self._log( 

1897 "inserting column to existing list " 

1898 "in properties.ColumnProperty %s" % (key) 

1899 ) 

1900 return prop 

1901 elif prop is None or isinstance( 

1902 prop, descriptor_props.ConcreteInheritedProperty 

1903 ): 

1904 mapped_column = [] 

1905 for c in columns: 

1906 mc = self.persist_selectable.corresponding_column(c) 

1907 if mc is None: 

1908 mc = self.local_table.corresponding_column(c) 

1909 if mc is not None: 

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

1911 # mapped table, this corresponds to adding a 

1912 # column after the fact to the local table. 

1913 # [ticket:1523] 

1914 self.persist_selectable._refresh_for_new_column(mc) 

1915 mc = self.persist_selectable.corresponding_column(c) 

1916 if mc is None: 

1917 raise sa_exc.ArgumentError( 

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

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

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

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

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

1923 ) 

1924 mapped_column.append(mc) 

1925 return properties.ColumnProperty(*mapped_column) 

1926 else: 

1927 raise sa_exc.ArgumentError( 

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

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

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

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

1932 "to remove all awareness of the column entirely " 

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

1934 "use the 'include_properties' or 'exclude_properties' " 

1935 "mapper arguments to control specifically which table " 

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

1937 ) 

1938 

1939 def _check_configure(self): 

1940 if self.registry._new_mappers: 

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

1942 

1943 def _post_configure_properties(self): 

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

1945 attached to this mapper. 

1946 

1947 This is a deferred configuration step which is intended 

1948 to execute once all mappers have been constructed. 

1949 

1950 """ 

1951 

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

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

1954 for key, prop in l: 

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

1956 

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

1958 prop.init() 

1959 

1960 if prop._configure_finished: 

1961 prop.post_instrument_class(self) 

1962 

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

1964 self.configured = True 

1965 

1966 def add_properties(self, dict_of_properties): 

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

1968 using `add_property`. 

1969 

1970 """ 

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

1972 self.add_property(key, value) 

1973 

1974 def add_property(self, key, prop): 

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

1976 

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

1978 property to the initial properties dictionary sent to the 

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

1980 the given MapperProperty is configured immediately. 

1981 

1982 """ 

1983 self._init_properties[key] = prop 

1984 self._configure_property(key, prop, init=self.configured) 

1985 

1986 def _expire_memoizations(self): 

1987 for mapper in self.iterate_to_root(): 

1988 mapper._reset_memoizations() 

1989 

1990 @property 

1991 def _log_desc(self): 

1992 return ( 

1993 "(" 

1994 + self.class_.__name__ 

1995 + "|" 

1996 + ( 

1997 self.local_table is not None 

1998 and self.local_table.description 

1999 or str(self.local_table) 

2000 ) 

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

2002 + ")" 

2003 ) 

2004 

2005 def _log(self, msg, *args): 

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

2007 

2008 def _log_debug(self, msg, *args): 

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

2010 

2011 def __repr__(self): 

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

2013 

2014 def __str__(self): 

2015 return "mapped class %s%s->%s" % ( 

2016 self.class_.__name__, 

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

2018 self.local_table.description 

2019 if self.local_table is not None 

2020 else self.persist_selectable.description, 

2021 ) 

2022 

2023 def _is_orphan(self, state): 

2024 orphan_possible = False 

2025 for mapper in self.iterate_to_root(): 

2026 for (key, cls) in mapper._delete_orphans: 

2027 orphan_possible = True 

2028 

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

2030 state, key, optimistic=state.has_identity 

2031 ) 

2032 

2033 if self.legacy_is_orphan and has_parent: 

2034 return False 

2035 elif not self.legacy_is_orphan and not has_parent: 

2036 return True 

2037 

2038 if self.legacy_is_orphan: 

2039 return orphan_possible 

2040 else: 

2041 return False 

2042 

2043 def has_property(self, key): 

2044 return key in self._props 

2045 

2046 def get_property(self, key, _configure_mappers=True): 

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

2048 

2049 if _configure_mappers: 

2050 self._check_configure() 

2051 

2052 try: 

2053 return self._props[key] 

2054 except KeyError as err: 

2055 util.raise_( 

2056 sa_exc.InvalidRequestError( 

2057 "Mapper '%s' has no property '%s'" % (self, key) 

2058 ), 

2059 replace_context=err, 

2060 ) 

2061 

2062 def get_property_by_column(self, column): 

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

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

2065 

2066 return self._columntoproperty[column] 

2067 

2068 @property 

2069 def iterate_properties(self): 

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

2071 

2072 self._check_configure() 

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

2074 

2075 def _mappers_from_spec(self, spec, selectable): 

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

2077 represents. 

2078 

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

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

2081 

2082 """ 

2083 if spec == "*": 

2084 mappers = list(self.self_and_descendants) 

2085 elif spec: 

2086 mappers = set() 

2087 for m in util.to_list(spec): 

2088 m = _class_to_mapper(m) 

2089 if not m.isa(self): 

2090 raise sa_exc.InvalidRequestError( 

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

2092 ) 

2093 

2094 if selectable is None: 

2095 mappers.update(m.iterate_to_root()) 

2096 else: 

2097 mappers.add(m) 

2098 mappers = [m for m in self.self_and_descendants if m in mappers] 

2099 else: 

2100 mappers = [] 

2101 

2102 if selectable is not None: 

2103 tables = set( 

2104 sql_util.find_tables(selectable, include_aliases=True) 

2105 ) 

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

2107 return mappers 

2108 

2109 def _selectable_from_mappers(self, mappers, innerjoin): 

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

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

2112 mapped tables. 

2113 

2114 """ 

2115 from_obj = self.persist_selectable 

2116 for m in mappers: 

2117 if m is self: 

2118 continue 

2119 if m.concrete: 

2120 raise sa_exc.InvalidRequestError( 

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

2122 "when concrete-inheriting mappers are used." 

2123 ) 

2124 elif not m.single: 

2125 if innerjoin: 

2126 from_obj = from_obj.join( 

2127 m.local_table, m.inherit_condition 

2128 ) 

2129 else: 

2130 from_obj = from_obj.outerjoin( 

2131 m.local_table, m.inherit_condition 

2132 ) 

2133 

2134 return from_obj 

2135 

2136 @HasMemoized.memoized_attribute 

2137 def _single_table_criterion(self): 

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

2139 return self.polymorphic_on._annotate( 

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

2141 ).in_(m.polymorphic_identity for m in self.self_and_descendants) 

2142 else: 

2143 return None 

2144 

2145 @HasMemoized.memoized_attribute 

2146 def _should_select_with_poly_adapter(self): 

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

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

2149 rows for mapped classes and subclasses against this Mapper. 

2150 

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

2152 for this condition. 

2153 

2154 """ 

2155 

2156 # this has been simplified as of #8456. 

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

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

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

2160 # 

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

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

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

2164 # some kind or polymorphic_union. 

2165 # 

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

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

2168 # on it (such as test_join_from_polymorphic_explicit_aliased_three). 

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

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

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

2172 # but our base has a wpoly on it, we turn on adaption. 

2173 return ( 

2174 self.with_polymorphic 

2175 or self._requires_row_aliasing 

2176 or self.base_mapper.with_polymorphic 

2177 or self.base_mapper._requires_row_aliasing 

2178 ) 

2179 

2180 @HasMemoized.memoized_attribute 

2181 def _with_polymorphic_mappers(self): 

2182 self._check_configure() 

2183 

2184 if not self.with_polymorphic: 

2185 return [] 

2186 return self._mappers_from_spec(*self.with_polymorphic) 

2187 

2188 @HasMemoized.memoized_attribute 

2189 def _post_inspect(self): 

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

2191 

2192 E.g. when Query calls: 

2193 

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

2195 

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

2197 

2198 """ 

2199 self._check_configure() 

2200 

2201 @HasMemoized.memoized_attribute 

2202 def _with_polymorphic_selectable(self): 

2203 if not self.with_polymorphic: 

2204 return self.persist_selectable 

2205 

2206 spec, selectable = self.with_polymorphic 

2207 if selectable is not None: 

2208 return selectable 

2209 else: 

2210 return self._selectable_from_mappers( 

2211 self._mappers_from_spec(spec, selectable), False 

2212 ) 

2213 

2214 with_polymorphic_mappers = _with_polymorphic_mappers 

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

2216 default "polymorphic" query. 

2217 

2218 """ 

2219 

2220 @HasMemoized.memoized_attribute 

2221 def _insert_cols_evaluating_none(self): 

2222 return dict( 

2223 ( 

2224 table, 

2225 frozenset( 

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

2227 ), 

2228 ) 

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

2230 ) 

2231 

2232 @HasMemoized.memoized_attribute 

2233 def _insert_cols_as_none(self): 

2234 return dict( 

2235 ( 

2236 table, 

2237 frozenset( 

2238 col.key 

2239 for col in columns 

2240 if not col.primary_key 

2241 and not col.server_default 

2242 and not col.default 

2243 and not col.type.should_evaluate_none 

2244 ), 

2245 ) 

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

2247 ) 

2248 

2249 @HasMemoized.memoized_attribute 

2250 def _propkey_to_col(self): 

2251 return dict( 

2252 ( 

2253 table, 

2254 dict( 

2255 (self._columntoproperty[col].key, col) for col in columns 

2256 ), 

2257 ) 

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

2259 ) 

2260 

2261 @HasMemoized.memoized_attribute 

2262 def _pk_keys_by_table(self): 

2263 return dict( 

2264 (table, frozenset([col.key for col in pks])) 

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

2266 ) 

2267 

2268 @HasMemoized.memoized_attribute 

2269 def _pk_attr_keys_by_table(self): 

2270 return dict( 

2271 ( 

2272 table, 

2273 frozenset([self._columntoproperty[col].key for col in pks]), 

2274 ) 

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

2276 ) 

2277 

2278 @HasMemoized.memoized_attribute 

2279 def _server_default_cols(self): 

2280 return dict( 

2281 ( 

2282 table, 

2283 frozenset( 

2284 [ 

2285 col.key 

2286 for col in columns 

2287 if col.server_default is not None 

2288 ] 

2289 ), 

2290 ) 

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

2292 ) 

2293 

2294 @HasMemoized.memoized_attribute 

2295 def _server_default_plus_onupdate_propkeys(self): 

2296 result = set() 

2297 

2298 for table, columns in self._cols_by_table.items(): 

2299 for col in columns: 

2300 if ( 

2301 col.server_default is not None 

2302 or col.server_onupdate is not None 

2303 ) and col in self._columntoproperty: 

2304 result.add(self._columntoproperty[col].key) 

2305 

2306 return result 

2307 

2308 @HasMemoized.memoized_attribute 

2309 def _server_onupdate_default_cols(self): 

2310 return dict( 

2311 ( 

2312 table, 

2313 frozenset( 

2314 [ 

2315 col.key 

2316 for col in columns 

2317 if col.server_onupdate is not None 

2318 ] 

2319 ), 

2320 ) 

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

2322 ) 

2323 

2324 @HasMemoized.memoized_instancemethod 

2325 def __clause_element__(self): 

2326 

2327 annotations = { 

2328 "entity_namespace": self, 

2329 "parententity": self, 

2330 "parentmapper": self, 

2331 } 

2332 if self.persist_selectable is not self.local_table: 

2333 # joined table inheritance, with polymorphic selectable, 

2334 # etc. 

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

2336 { 

2337 "entity_namespace": self, 

2338 "parententity": self, 

2339 "parentmapper": self, 

2340 } 

2341 )._set_propagate_attrs( 

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

2343 ) 

2344 

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

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

2347 ) 

2348 

2349 @util.memoized_property 

2350 def select_identity_token(self): 

2351 return ( 

2352 expression.null() 

2353 ._annotate( 

2354 { 

2355 "entity_namespace": self, 

2356 "parententity": self, 

2357 "parentmapper": self, 

2358 "identity_token": True, 

2359 } 

2360 ) 

2361 ._set_propagate_attrs( 

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

2363 ) 

2364 ) 

2365 

2366 @property 

2367 def selectable(self): 

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

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

2370 

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

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

2373 full "polymorphic" selectable is returned. 

2374 

2375 """ 

2376 return self._with_polymorphic_selectable 

2377 

2378 def _with_polymorphic_args( 

2379 self, spec=None, selectable=False, innerjoin=False 

2380 ): 

2381 if selectable not in (None, False): 

2382 selectable = coercions.expect( 

2383 roles.StrictFromClauseRole, selectable, allow_select=True 

2384 ) 

2385 

2386 if self.with_polymorphic: 

2387 if not spec: 

2388 spec = self.with_polymorphic[0] 

2389 if selectable is False: 

2390 selectable = self.with_polymorphic[1] 

2391 elif selectable is False: 

2392 selectable = None 

2393 mappers = self._mappers_from_spec(spec, selectable) 

2394 if selectable is not None: 

2395 return mappers, selectable 

2396 else: 

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

2398 

2399 @HasMemoized.memoized_attribute 

2400 def _polymorphic_properties(self): 

2401 return list( 

2402 self._iterate_polymorphic_properties( 

2403 self._with_polymorphic_mappers 

2404 ) 

2405 ) 

2406 

2407 @property 

2408 def _all_column_expressions(self): 

2409 poly_properties = self._polymorphic_properties 

2410 adapter = self._polymorphic_adapter 

2411 

2412 return [ 

2413 adapter.columns[prop.columns[0]] if adapter else prop.columns[0] 

2414 for prop in poly_properties 

2415 if isinstance(prop, properties.ColumnProperty) 

2416 and prop._renders_in_subqueries 

2417 ] 

2418 

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

2420 if polymorphic_mappers: 

2421 poly_properties = self._iterate_polymorphic_properties( 

2422 polymorphic_mappers 

2423 ) 

2424 else: 

2425 poly_properties = self._polymorphic_properties 

2426 

2427 return [ 

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

2429 for prop in poly_properties 

2430 if isinstance(prop, properties.ColumnProperty) 

2431 ] 

2432 

2433 @HasMemoized.memoized_attribute 

2434 def _polymorphic_adapter(self): 

2435 if self.with_polymorphic: 

2436 return sql_util.ColumnAdapter( 

2437 self.selectable, equivalents=self._equivalent_columns 

2438 ) 

2439 else: 

2440 return None 

2441 

2442 def _iterate_polymorphic_properties(self, mappers=None): 

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

2444 a SELECT.""" 

2445 if mappers is None: 

2446 mappers = self._with_polymorphic_mappers 

2447 

2448 if not mappers: 

2449 for c in self.iterate_properties: 

2450 yield c 

2451 else: 

2452 # in the polymorphic case, filter out discriminator columns 

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

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

2455 for c in util.unique_list( 

2456 chain( 

2457 *[ 

2458 list(mapper.iterate_properties) 

2459 for mapper in [self] + mappers 

2460 ] 

2461 ) 

2462 ): 

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

2464 self.polymorphic_on is None 

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

2466 ): 

2467 continue 

2468 yield c 

2469 

2470 @HasMemoized.memoized_attribute 

2471 def attrs(self): 

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

2473 associated this mapper. 

2474 

2475 This is an object that provides each property based on 

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

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

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

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

2480 column. The namespace object can also be iterated, 

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

2482 

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

2484 of this attribute which limit the types of properties 

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

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

2487 

2488 .. warning:: 

2489 

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

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

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

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

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

2495 accessing attributes dynamically, favor using the dict-access 

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

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

2498 

2499 .. seealso:: 

2500 

2501 :attr:`_orm.Mapper.all_orm_descriptors` 

2502 

2503 """ 

2504 

2505 self._check_configure() 

2506 return util.ImmutableProperties(self._props) 

2507 

2508 @HasMemoized.memoized_attribute 

2509 def all_orm_descriptors(self): 

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

2511 with the mapped class. 

2512 

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

2514 associated with the mapped class or its superclasses. 

2515 

2516 This namespace includes attributes that are mapped to the class 

2517 as well as attributes declared by extension modules. 

2518 It includes any Python descriptor type that inherits from 

2519 :class:`.InspectionAttr`. This includes 

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

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

2522 :class:`.AssociationProxy`. 

2523 

2524 To distinguish between mapped attributes and extension attributes, 

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

2526 to a constant that distinguishes between different extension types. 

2527 

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

2529 

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

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

2532 

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

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

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

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

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

2538 or the mapper. 

2539 

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

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

2542 class in which it first appeared. 

2543 

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

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

2546 

2547 .. versionchanged:: 1.3.19 ensured deterministic ordering for 

2548 :meth:`_orm.Mapper.all_orm_descriptors`. 

2549 

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

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

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

2553 referring to the collection of mapped properties via 

2554 :attr:`_orm.Mapper.attrs`. 

2555 

2556 .. warning:: 

2557 

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

2559 accessor namespace is an 

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

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

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

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

2564 accessing attributes dynamically, favor using the dict-access 

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

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

2567 collisions. 

2568 

2569 .. seealso:: 

2570 

2571 :attr:`_orm.Mapper.attrs` 

2572 

2573 """ 

2574 return util.ImmutableProperties( 

2575 dict(self.class_manager._all_sqla_attributes()) 

2576 ) 

2577 

2578 @HasMemoized.memoized_attribute 

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

2580 def _pk_synonyms(self): 

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

2582 all synonyms that refer to primary key columns 

2583 

2584 """ 

2585 descriptor_props = util.preloaded.orm_descriptor_props 

2586 

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

2588 

2589 return { 

2590 syn.key: syn.name 

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

2592 if isinstance(syn, descriptor_props.SynonymProperty) 

2593 and syn.name in pk_keys 

2594 } 

2595 

2596 @HasMemoized.memoized_attribute 

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

2598 def synonyms(self): 

2599 """Return a namespace of all :class:`.SynonymProperty` 

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

2601 

2602 .. seealso:: 

2603 

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

2605 :class:`.MapperProperty` 

2606 objects. 

2607 

2608 """ 

2609 descriptor_props = util.preloaded.orm_descriptor_props 

2610 

2611 return self._filter_properties(descriptor_props.SynonymProperty) 

2612 

2613 @property 

2614 def entity_namespace(self): 

2615 return self.class_ 

2616 

2617 @HasMemoized.memoized_attribute 

2618 def column_attrs(self): 

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

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

2621 

2622 .. seealso:: 

2623 

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

2625 :class:`.MapperProperty` 

2626 objects. 

2627 

2628 """ 

2629 return self._filter_properties(properties.ColumnProperty) 

2630 

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

2632 @HasMemoized.memoized_attribute 

2633 def relationships(self): 

2634 """A namespace of all :class:`.RelationshipProperty` properties 

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

2636 

2637 .. warning:: 

2638 

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

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

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

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

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

2644 accessing attributes dynamically, favor using the dict-access 

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

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

2647 collisions. 

2648 

2649 .. seealso:: 

2650 

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

2652 :class:`.MapperProperty` 

2653 objects. 

2654 

2655 """ 

2656 return self._filter_properties( 

2657 util.preloaded.orm_relationships.RelationshipProperty 

2658 ) 

2659 

2660 @HasMemoized.memoized_attribute 

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

2662 def composites(self): 

2663 """Return a namespace of all :class:`.CompositeProperty` 

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

2665 

2666 .. seealso:: 

2667 

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

2669 :class:`.MapperProperty` 

2670 objects. 

2671 

2672 """ 

2673 return self._filter_properties( 

2674 util.preloaded.orm_descriptor_props.CompositeProperty 

2675 ) 

2676 

2677 def _filter_properties(self, type_): 

2678 self._check_configure() 

2679 return util.ImmutableProperties( 

2680 util.OrderedDict( 

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

2682 ) 

2683 ) 

2684 

2685 @HasMemoized.memoized_attribute 

2686 def _get_clause(self): 

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

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

2689 by primary key. 

2690 

2691 """ 

2692 params = [ 

2693 ( 

2694 primary_key, 

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

2696 ) 

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

2698 ] 

2699 return ( 

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

2701 util.column_dict(params), 

2702 ) 

2703 

2704 @HasMemoized.memoized_attribute 

2705 def _equivalent_columns(self): 

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

2707 the determination of column pairs that are equated to 

2708 one another based on inherit condition. This is designed 

2709 to work with the queries that util.polymorphic_union 

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

2711 the base table directly (including the subclass table columns 

2712 only). 

2713 

2714 The resulting structure is a dictionary of columns mapped 

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

2716 

2717 { 

2718 tablea.col1: 

2719 {tableb.col1, tablec.col1}, 

2720 tablea.col2: 

2721 {tabled.col2} 

2722 } 

2723 

2724 """ 

2725 result = util.column_dict() 

2726 

2727 def visit_binary(binary): 

2728 if binary.operator == operators.eq: 

2729 if binary.left in result: 

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

2731 else: 

2732 result[binary.left] = util.column_set((binary.right,)) 

2733 if binary.right in result: 

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

2735 else: 

2736 result[binary.right] = util.column_set((binary.left,)) 

2737 

2738 for mapper in self.base_mapper.self_and_descendants: 

2739 if mapper.inherit_condition is not None: 

2740 visitors.traverse( 

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

2742 ) 

2743 

2744 return result 

2745 

2746 def _is_userland_descriptor(self, assigned_name, obj): 

2747 if isinstance( 

2748 obj, 

2749 ( 

2750 _MappedAttribute, 

2751 instrumentation.ClassManager, 

2752 expression.ColumnElement, 

2753 ), 

2754 ): 

2755 return False 

2756 else: 

2757 return assigned_name not in self._dataclass_fields 

2758 

2759 @HasMemoized.memoized_attribute 

2760 def _dataclass_fields(self): 

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

2762 

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

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

2765 present on the class. 

2766 

2767 This occurs when properties are propagated from an inherited class, or 

2768 are applied from the columns present in the mapped table. 

2769 

2770 """ 

2771 

2772 # check for class-bound attributes and/or descriptors, 

2773 # either local or from an inherited class 

2774 # ignore dataclass field default values 

2775 if local: 

2776 if self.class_.__dict__.get( 

2777 assigned_name, None 

2778 ) is not None and self._is_userland_descriptor( 

2779 assigned_name, self.class_.__dict__[assigned_name] 

2780 ): 

2781 return True 

2782 else: 

2783 attr = self.class_manager._get_class_attr_mro(assigned_name, None) 

2784 if attr is not None and self._is_userland_descriptor( 

2785 assigned_name, attr 

2786 ): 

2787 return True 

2788 

2789 if ( 

2790 self.include_properties is not None 

2791 and name not in self.include_properties 

2792 and (column is None or column not in self.include_properties) 

2793 ): 

2794 self._log("not including property %s" % (name)) 

2795 return True 

2796 

2797 if self.exclude_properties is not None and ( 

2798 name in self.exclude_properties 

2799 or (column is not None and column in self.exclude_properties) 

2800 ): 

2801 self._log("excluding property %s" % (name)) 

2802 return True 

2803 

2804 return False 

2805 

2806 def common_parent(self, other): 

2807 """Return true if the given mapper shares a 

2808 common inherited parent as this mapper.""" 

2809 

2810 return self.base_mapper is other.base_mapper 

2811 

2812 def is_sibling(self, other): 

2813 """return true if the other mapper is an inheriting sibling to this 

2814 one. common parent but different branch 

2815 

2816 """ 

2817 return ( 

2818 self.base_mapper is other.base_mapper 

2819 and not self.isa(other) 

2820 and not other.isa(self) 

2821 ) 

2822 

2823 def _canload(self, state, allow_subtypes): 

2824 s = self.primary_mapper() 

2825 if self.polymorphic_on is not None or allow_subtypes: 

2826 return _state_mapper(state).isa(s) 

2827 else: 

2828 return _state_mapper(state) is s 

2829 

2830 def isa(self, other): 

2831 """Return True if the this mapper inherits from the given mapper.""" 

2832 

2833 m = self 

2834 while m and m is not other: 

2835 m = m.inherits 

2836 return bool(m) 

2837 

2838 def iterate_to_root(self): 

2839 m = self 

2840 while m: 

2841 yield m 

2842 m = m.inherits 

2843 

2844 @HasMemoized.memoized_attribute 

2845 def self_and_descendants(self): 

2846 """The collection including this mapper and all descendant mappers. 

2847 

2848 This includes not just the immediately inheriting mappers but 

2849 all their inheriting mappers as well. 

2850 

2851 """ 

2852 descendants = [] 

2853 stack = deque([self]) 

2854 while stack: 

2855 item = stack.popleft() 

2856 descendants.append(item) 

2857 stack.extend(item._inheriting_mappers) 

2858 return util.WeakSequence(descendants) 

2859 

2860 def polymorphic_iterator(self): 

2861 """Iterate through the collection including this mapper and 

2862 all descendant mappers. 

2863 

2864 This includes not just the immediately inheriting mappers but 

2865 all their inheriting mappers as well. 

2866 

2867 To iterate through an entire hierarchy, use 

2868 ``mapper.base_mapper.polymorphic_iterator()``. 

2869 

2870 """ 

2871 return iter(self.self_and_descendants) 

2872 

2873 def primary_mapper(self): 

2874 """Return the primary mapper corresponding to this mapper's class key 

2875 (class).""" 

2876 

2877 return self.class_manager.mapper 

2878 

2879 @property 

2880 def primary_base_mapper(self): 

2881 return self.class_manager.mapper.base_mapper 

2882 

2883 def _result_has_identity_key(self, result, adapter=None): 

2884 pk_cols = self.primary_key 

2885 if adapter: 

2886 pk_cols = [adapter.columns[c] for c in pk_cols] 

2887 rk = result.keys() 

2888 for col in pk_cols: 

2889 if col not in rk: 

2890 return False 

2891 else: 

2892 return True 

2893 

2894 def identity_key_from_row(self, row, identity_token=None, adapter=None): 

2895 """Return an identity-map key for use in storing/retrieving an 

2896 item from the identity map. 

2897 

2898 :param row: A :class:`.Row` instance. The columns which are 

2899 mapped by this :class:`_orm.Mapper` should be locatable in the row, 

2900 preferably via the :class:`_schema.Column` 

2901 object directly (as is the case 

2902 when a :func:`_expression.select` construct is executed), or 

2903 via string names of the form ``<tablename>_<colname>``. 

2904 

2905 """ 

2906 pk_cols = self.primary_key 

2907 if adapter: 

2908 pk_cols = [adapter.columns[c] for c in pk_cols] 

2909 

2910 return ( 

2911 self._identity_class, 

2912 tuple(row[column] for column in pk_cols), 

2913 identity_token, 

2914 ) 

2915 

2916 def identity_key_from_primary_key(self, primary_key, identity_token=None): 

2917 """Return an identity-map key for use in storing/retrieving an 

2918 item from an identity map. 

2919 

2920 :param primary_key: A list of values indicating the identifier. 

2921 

2922 """ 

2923 return self._identity_class, tuple(primary_key), identity_token 

2924 

2925 def identity_key_from_instance(self, instance): 

2926 """Return the identity key for the given instance, based on 

2927 its primary key attributes. 

2928 

2929 If the instance's state is expired, calling this method 

2930 will result in a database check to see if the object has been deleted. 

2931 If the row no longer exists, 

2932 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

2933 

2934 This value is typically also found on the instance state under the 

2935 attribute name `key`. 

2936 

2937 """ 

2938 state = attributes.instance_state(instance) 

2939 return self._identity_key_from_state(state, attributes.PASSIVE_OFF) 

2940 

2941 def _identity_key_from_state( 

2942 self, state, passive=attributes.PASSIVE_RETURN_NO_VALUE 

2943 ): 

2944 dict_ = state.dict 

2945 manager = state.manager 

2946 return ( 

2947 self._identity_class, 

2948 tuple( 

2949 [ 

2950 manager[prop.key].impl.get(state, dict_, passive) 

2951 for prop in self._identity_key_props 

2952 ] 

2953 ), 

2954 state.identity_token, 

2955 ) 

2956 

2957 def primary_key_from_instance(self, instance): 

2958 """Return the list of primary key values for the given 

2959 instance. 

2960 

2961 If the instance's state is expired, calling this method 

2962 will result in a database check to see if the object has been deleted. 

2963 If the row no longer exists, 

2964 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 

2965 

2966 """ 

2967 state = attributes.instance_state(instance) 

2968 identity_key = self._identity_key_from_state( 

2969 state, attributes.PASSIVE_OFF 

2970 ) 

2971 return identity_key[1] 

2972 

2973 @HasMemoized.memoized_attribute 

2974 def _persistent_sortkey_fn(self): 

2975 key_fns = [col.type.sort_key_function for col in self.primary_key] 

2976 

2977 if set(key_fns).difference([None]): 

2978 

2979 def key(state): 

2980 return tuple( 

2981 key_fn(val) if key_fn is not None else val 

2982 for key_fn, val in zip(key_fns, state.key[1]) 

2983 ) 

2984 

2985 else: 

2986 

2987 def key(state): 

2988 return state.key[1] 

2989 

2990 return key 

2991 

2992 @HasMemoized.memoized_attribute 

2993 def _identity_key_props(self): 

2994 return [self._columntoproperty[col] for col in self.primary_key] 

2995 

2996 @HasMemoized.memoized_attribute 

2997 def _all_pk_cols(self): 

2998 collection = set() 

2999 for table in self.tables: 

3000 collection.update(self._pks_by_table[table]) 

3001 return collection 

3002 

3003 @HasMemoized.memoized_attribute 

3004 def _should_undefer_in_wildcard(self): 

3005 cols = set(self.primary_key) 

3006 if self.polymorphic_on is not None: 

3007 cols.add(self.polymorphic_on) 

3008 return cols 

3009 

3010 @HasMemoized.memoized_attribute 

3011 def _primary_key_propkeys(self): 

3012 return {self._columntoproperty[col].key for col in self._all_pk_cols} 

3013 

3014 def _get_state_attr_by_column( 

3015 self, state, dict_, column, passive=attributes.PASSIVE_RETURN_NO_VALUE 

3016 ): 

3017 prop = self._columntoproperty[column] 

3018 return state.manager[prop.key].impl.get(state, dict_, passive=passive) 

3019 

3020 def _set_committed_state_attr_by_column(self, state, dict_, column, value): 

3021 prop = self._columntoproperty[column] 

3022 state.manager[prop.key].impl.set_committed_value(state, dict_, value) 

3023 

3024 def _set_state_attr_by_column(self, state, dict_, column, value): 

3025 prop = self._columntoproperty[column] 

3026 state.manager[prop.key].impl.set(state, dict_, value, None) 

3027 

3028 def _get_committed_attr_by_column(self, obj, column): 

3029 state = attributes.instance_state(obj) 

3030 dict_ = attributes.instance_dict(obj) 

3031 return self._get_committed_state_attr_by_column( 

3032 state, dict_, column, passive=attributes.PASSIVE_OFF 

3033 ) 

3034 

3035 def _get_committed_state_attr_by_column( 

3036 self, state, dict_, column, passive=attributes.PASSIVE_RETURN_NO_VALUE 

3037 ): 

3038 

3039 prop = self._columntoproperty[column] 

3040 return state.manager[prop.key].impl.get_committed_value( 

3041 state, dict_, passive=passive 

3042 ) 

3043 

3044 def _optimized_get_statement(self, state, attribute_names): 

3045 """assemble a WHERE clause which retrieves a given state by primary 

3046 key, using a minimized set of tables. 

3047 

3048 Applies to a joined-table inheritance mapper where the 

3049 requested attribute names are only present on joined tables, 

3050 not the base table. The WHERE clause attempts to include 

3051 only those tables to minimize joins. 

3052 

3053 """ 

3054 props = self._props 

3055 

3056 col_attribute_names = set(attribute_names).intersection( 

3057 state.mapper.column_attrs.keys() 

3058 ) 

3059 tables = set( 

3060 chain( 

3061 *[ 

3062 sql_util.find_tables(c, check_columns=True) 

3063 for key in col_attribute_names 

3064 for c in props[key].columns 

3065 ] 

3066 ) 

3067 ) 

3068 

3069 if self.base_mapper.local_table in tables: 

3070 return None 

3071 

3072 def visit_binary(binary): 

3073 leftcol = binary.left 

3074 rightcol = binary.right 

3075 if leftcol is None or rightcol is None: 

3076 return 

3077 

3078 if leftcol.table not in tables: 

3079 leftval = self._get_committed_state_attr_by_column( 

3080 state, 

3081 state.dict, 

3082 leftcol, 

3083 passive=attributes.PASSIVE_NO_INITIALIZE, 

3084 ) 

3085 if leftval in orm_util._none_set: 

3086 raise _OptGetColumnsNotAvailable() 

3087 binary.left = sql.bindparam( 

3088 None, leftval, type_=binary.right.type 

3089 ) 

3090 elif rightcol.table not in tables: 

3091 rightval = self._get_committed_state_attr_by_column( 

3092 state, 

3093 state.dict, 

3094 rightcol, 

3095 passive=attributes.PASSIVE_NO_INITIALIZE, 

3096 ) 

3097 if rightval in orm_util._none_set: 

3098 raise _OptGetColumnsNotAvailable() 

3099 binary.right = sql.bindparam( 

3100 None, rightval, type_=binary.right.type 

3101 ) 

3102 

3103 allconds = [] 

3104 

3105 start = False 

3106 

3107 # as of #7507, from the lowest base table on upwards, 

3108 # we include all intermediary tables. 

3109 

3110 for mapper in reversed(list(self.iterate_to_root())): 

3111 if mapper.local_table in tables: 

3112 start = True 

3113 elif not isinstance(mapper.local_table, expression.TableClause): 

3114 return None 

3115 if start and not mapper.single: 

3116 allconds.append(mapper.inherit_condition) 

3117 tables.add(mapper.local_table) 

3118 

3119 # only the bottom table needs its criteria to be altered to fit 

3120 # the primary key ident - the rest of the tables upwards to the 

3121 # descendant-most class should all be present and joined to each 

3122 # other. 

3123 try: 

3124 allconds[0] = visitors.cloned_traverse( 

3125 allconds[0], {}, {"binary": visit_binary} 

3126 ) 

3127 except _OptGetColumnsNotAvailable: 

3128 return None 

3129 

3130 cond = sql.and_(*allconds) 

3131 

3132 cols = [] 

3133 for key in col_attribute_names: 

3134 cols.extend(props[key].columns) 

3135 return ( 

3136 sql.select(*cols) 

3137 .where(cond) 

3138 .set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL) 

3139 ) 

3140 

3141 def _iterate_to_target_viawpoly(self, mapper): 

3142 if self.isa(mapper): 

3143 prev = self 

3144 for m in self.iterate_to_root(): 

3145 yield m 

3146 

3147 if m is not prev and prev not in m._with_polymorphic_mappers: 

3148 break 

3149 

3150 prev = m 

3151 if m is mapper: 

3152 break 

3153 

3154 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 

3155 if not enabled_via_opt: 

3156 # common case, takes place for all polymorphic loads 

3157 mapper = polymorphic_from 

3158 for m in self._iterate_to_target_viawpoly(mapper): 

3159 if m.polymorphic_load == "selectin": 

3160 return m 

3161 else: 

3162 # uncommon case, selectin load options were used 

3163 enabled_via_opt = set(enabled_via_opt) 

3164 enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 

3165 for entity in enabled_via_opt.union([polymorphic_from]): 

3166 mapper = entity.mapper 

3167 for m in self._iterate_to_target_viawpoly(mapper): 

3168 if ( 

3169 m.polymorphic_load == "selectin" 

3170 or m in enabled_via_opt_mappers 

3171 ): 

3172 return enabled_via_opt_mappers.get(m, m) 

3173 

3174 return None 

3175 

3176 @util.preload_module("sqlalchemy.orm.strategy_options") 

3177 def _subclass_load_via_in(self, entity): 

3178 """Assemble a that can load the columns local to 

3179 this subclass as a SELECT with IN. 

3180 

3181 """ 

3182 strategy_options = util.preloaded.orm_strategy_options 

3183 

3184 assert self.inherits 

3185 

3186 if self.polymorphic_on is not None: 

3187 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 

3188 keep_props = set([polymorphic_prop] + self._identity_key_props) 

3189 else: 

3190 keep_props = set(self._identity_key_props) 

3191 

3192 disable_opt = strategy_options.Load(entity) 

3193 enable_opt = strategy_options.Load(entity) 

3194 

3195 for prop in self.attrs: 

3196 

3197 # skip prop keys that are not instrumented on the mapped class. 

3198 # this is primarily the "_sa_polymorphic_on" property that gets 

3199 # created for an ad-hoc polymorphic_on SQL expression, issue #8704 

3200 if prop.key not in self.class_manager: 

3201 continue 

3202 

3203 if prop.parent is self or prop in keep_props: 

3204 # "enable" options, to turn on the properties that we want to 

3205 # load by default (subject to options from the query) 

3206 if not isinstance(prop, StrategizedProperty): 

3207 continue 

3208 

3209 enable_opt.set_generic_strategy( 

3210 # convert string name to an attribute before passing 

3211 # to loader strategy. note this must be in terms 

3212 # of given entity, such as AliasedClass, etc. 

3213 (getattr(entity.entity_namespace, prop.key),), 

3214 dict(prop.strategy_key), 

3215 ) 

3216 else: 

3217 # "disable" options, to turn off the properties from the 

3218 # superclass that we *don't* want to load, applied after 

3219 # the options from the query to override them 

3220 disable_opt.set_generic_strategy( 

3221 # convert string name to an attribute before passing 

3222 # to loader strategy. note this must be in terms 

3223 # of given entity, such as AliasedClass, etc. 

3224 (getattr(entity.entity_namespace, prop.key),), 

3225 {"do_nothing": True}, 

3226 ) 

3227 

3228 primary_key = [ 

3229 sql_util._deep_annotate(pk, {"_orm_adapt": True}) 

3230 for pk in self.primary_key 

3231 ] 

3232 

3233 if len(primary_key) > 1: 

3234 in_expr = sql.tuple_(*primary_key) 

3235 else: 

3236 in_expr = primary_key[0] 

3237 

3238 if entity.is_aliased_class: 

3239 assert entity.mapper is self 

3240 

3241 q = sql.select(entity).set_label_style( 

3242 LABEL_STYLE_TABLENAME_PLUS_COL 

3243 ) 

3244 

3245 in_expr = entity._adapter.traverse(in_expr) 

3246 primary_key = [entity._adapter.traverse(k) for k in primary_key] 

3247 q = q.where( 

3248 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3249 ).order_by(*primary_key) 

3250 else: 

3251 

3252 q = sql.select(self).set_label_style( 

3253 LABEL_STYLE_TABLENAME_PLUS_COL 

3254 ) 

3255 q = q.where( 

3256 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 

3257 ).order_by(*primary_key) 

3258 

3259 return q, enable_opt, disable_opt 

3260 

3261 @HasMemoized.memoized_attribute 

3262 def _subclass_load_via_in_mapper(self): 

3263 return self._subclass_load_via_in(self) 

3264 

3265 def cascade_iterator(self, type_, state, halt_on=None): 

3266 r"""Iterate each element and its mapper in an object graph, 

3267 for all relationships that meet the given cascade rule. 

3268 

3269 :param type\_: 

3270 The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 

3271 etc.). 

3272 

3273 .. note:: the ``"all"`` cascade is not accepted here. For a generic 

3274 object traversal function, see :ref:`faq_walk_objects`. 

3275 

3276 :param state: 

3277 The lead InstanceState. child items will be processed per 

3278 the relationships defined for this object's mapper. 

3279 

3280 :return: the method yields individual object instances. 

3281 

3282 .. seealso:: 

3283 

3284 :ref:`unitofwork_cascades` 

3285 

3286 :ref:`faq_walk_objects` - illustrates a generic function to 

3287 traverse all objects without relying on cascades. 

3288 

3289 """ 

3290 visited_states = set() 

3291 prp, mpp = object(), object() 

3292 

3293 assert state.mapper.isa(self) 

3294 

3295 visitables = deque( 

3296 [(deque(state.mapper._props.values()), prp, state, state.dict)] 

3297 ) 

3298 

3299 while visitables: 

3300 iterator, item_type, parent_state, parent_dict = visitables[-1] 

3301 if not iterator: 

3302 visitables.pop() 

3303 continue 

3304 

3305 if item_type is prp: 

3306 prop = iterator.popleft() 

3307 if type_ not in prop.cascade: 

3308 continue 

3309 queue = deque( 

3310 prop.cascade_iterator( 

3311 type_, 

3312 parent_state, 

3313 parent_dict, 

3314 visited_states, 

3315 halt_on, 

3316 ) 

3317 ) 

3318 if queue: 

3319 visitables.append((queue, mpp, None, None)) 

3320 elif item_type is mpp: 

3321 ( 

3322 instance, 

3323 instance_mapper, 

3324 corresponding_state, 

3325 corresponding_dict, 

3326 ) = iterator.popleft() 

3327 yield ( 

3328 instance, 

3329 instance_mapper, 

3330 corresponding_state, 

3331 corresponding_dict, 

3332 ) 

3333 visitables.append( 

3334 ( 

3335 deque(instance_mapper._props.values()), 

3336 prp, 

3337 corresponding_state, 

3338 corresponding_dict, 

3339 ) 

3340 ) 

3341 

3342 @HasMemoized.memoized_attribute 

3343 def _compiled_cache(self): 

3344 return util.LRUCache(self._compiled_cache_size) 

3345 

3346 @HasMemoized.memoized_attribute 

3347 def _sorted_tables(self): 

3348 table_to_mapper = {} 

3349 

3350 for mapper in self.base_mapper.self_and_descendants: 

3351 for t in mapper.tables: 

3352 table_to_mapper.setdefault(t, mapper) 

3353 

3354 extra_dependencies = [] 

3355 for table, mapper in table_to_mapper.items(): 

3356 super_ = mapper.inherits 

3357 if super_: 

3358 extra_dependencies.extend( 

3359 [(super_table, table) for super_table in super_.tables] 

3360 ) 

3361 

3362 def skip(fk): 

3363 # attempt to skip dependencies that are not 

3364 # significant to the inheritance chain 

3365 # for two tables that are related by inheritance. 

3366 # while that dependency may be important, it's technically 

3367 # not what we mean to sort on here. 

3368 parent = table_to_mapper.get(fk.parent.table) 

3369 dep = table_to_mapper.get(fk.column.table) 

3370 if ( 

3371 parent is not None 

3372 and dep is not None 

3373 and dep is not parent 

3374 and dep.inherit_condition is not None 

3375 ): 

3376 cols = set(sql_util._find_columns(dep.inherit_condition)) 

3377 if parent.inherit_condition is not None: 

3378 cols = cols.union( 

3379 sql_util._find_columns(parent.inherit_condition) 

3380 ) 

3381 return fk.parent not in cols and fk.column not in cols 

3382 else: 

3383 return fk.parent not in cols 

3384 return False 

3385 

3386 sorted_ = sql_util.sort_tables( 

3387 table_to_mapper, 

3388 skip_fn=skip, 

3389 extra_dependencies=extra_dependencies, 

3390 ) 

3391 

3392 ret = util.OrderedDict() 

3393 for t in sorted_: 

3394 ret[t] = table_to_mapper[t] 

3395 return ret 

3396 

3397 def _memo(self, key, callable_): 

3398 if key in self._memoized_values: 

3399 return self._memoized_values[key] 

3400 else: 

3401 self._memoized_values[key] = value = callable_() 

3402 return value 

3403 

3404 @util.memoized_property 

3405 def _table_to_equated(self): 

3406 """memoized map of tables to collections of columns to be 

3407 synchronized upwards to the base mapper.""" 

3408 

3409 result = util.defaultdict(list) 

3410 

3411 for table in self._sorted_tables: 

3412 cols = set(table.c) 

3413 for m in self.iterate_to_root(): 

3414 if m._inherits_equated_pairs and cols.intersection( 

3415 util.reduce( 

3416 set.union, 

3417 [l.proxy_set for l, r in m._inherits_equated_pairs], 

3418 ) 

3419 ): 

3420 result[table].append((m, m._inherits_equated_pairs)) 

3421 

3422 return result 

3423 

3424 

3425class _OptGetColumnsNotAvailable(Exception): 

3426 pass 

3427 

3428 

3429def configure_mappers(): 

3430 """Initialize the inter-mapper relationships of all mappers that 

3431 have been constructed thus far across all :class:`_orm.registry` 

3432 collections. 

3433 

3434 The configure step is used to reconcile and initialize the 

3435 :func:`_orm.relationship` linkages between mapped classes, as well as to 

3436 invoke configuration events such as the 

3437 :meth:`_orm.MapperEvents.before_configured` and 

3438 :meth:`_orm.MapperEvents.after_configured`, which may be used by ORM 

3439 extensions or user-defined extension hooks. 

3440 

3441 Mapper configuration is normally invoked automatically, the first time 

3442 mappings from a particular :class:`_orm.registry` are used, as well as 

3443 whenever mappings are used and additional not-yet-configured mappers have 

3444 been constructed. The automatic configuration process however is local only 

3445 to the :class:`_orm.registry` involving the target mapper and any related 

3446 :class:`_orm.registry` objects which it may depend on; this is 

3447 equivalent to invoking the :meth:`_orm.registry.configure` method 

3448 on a particular :class:`_orm.registry`. 

3449 

3450 By contrast, the :func:`_orm.configure_mappers` function will invoke the 

3451 configuration process on all :class:`_orm.registry` objects that 

3452 exist in memory, and may be useful for scenarios where many individual 

3453 :class:`_orm.registry` objects that are nonetheless interrelated are 

3454 in use. 

3455 

3456 .. versionchanged:: 1.4 

3457 

3458 As of SQLAlchemy 1.4.0b2, this function works on a 

3459 per-:class:`_orm.registry` basis, locating all :class:`_orm.registry` 

3460 objects present and invoking the :meth:`_orm.registry.configure` method 

3461 on each. The :meth:`_orm.registry.configure` method may be preferred to 

3462 limit the configuration of mappers to those local to a particular 

3463 :class:`_orm.registry` and/or declarative base class. 

3464 

3465 Points at which automatic configuration is invoked include when a mapped 

3466 class is instantiated into an instance, as well as when ORM queries 

3467 are emitted using :meth:`.Session.query` or :meth:`_orm.Session.execute` 

3468 with an ORM-enabled statement. 

3469 

3470 The mapper configure process, whether invoked by 

3471 :func:`_orm.configure_mappers` or from :meth:`_orm.registry.configure`, 

3472 provides several event hooks that can be used to augment the mapper 

3473 configuration step. These hooks include: 

3474 

3475 * :meth:`.MapperEvents.before_configured` - called once before 

3476 :func:`.configure_mappers` or :meth:`_orm.registry.configure` does any 

3477 work; this can be used to establish additional options, properties, or 

3478 related mappings before the operation proceeds. 

3479 

3480 * :meth:`.MapperEvents.mapper_configured` - called as each individual 

3481 :class:`_orm.Mapper` is configured within the process; will include all 

3482 mapper state except for backrefs set up by other mappers that are still 

3483 to be configured. 

3484 

3485 * :meth:`.MapperEvents.after_configured` - called once after 

3486 :func:`.configure_mappers` or :meth:`_orm.registry.configure` is 

3487 complete; at this stage, all :class:`_orm.Mapper` objects that fall 

3488 within the scope of the configuration operation will be fully configured. 

3489 Note that the calling application may still have other mappings that 

3490 haven't been produced yet, such as if they are in modules as yet 

3491 unimported, and may also have mappings that are still to be configured, 

3492 if they are in other :class:`_orm.registry` collections not part of the 

3493 current scope of configuration. 

3494 

3495 """ 

3496 

3497 _configure_registries(_all_registries(), cascade=True) 

3498 

3499 

3500def _configure_registries(registries, cascade): 

3501 for reg in registries: 

3502 if reg._new_mappers: 

3503 break 

3504 else: 

3505 return 

3506 

3507 with _CONFIGURE_MUTEX: 

3508 global _already_compiling 

3509 if _already_compiling: 

3510 return 

3511 _already_compiling = True 

3512 try: 

3513 

3514 # double-check inside mutex 

3515 for reg in registries: 

3516 if reg._new_mappers: 

3517 break 

3518 else: 

3519 return 

3520 

3521 Mapper.dispatch._for_class(Mapper).before_configured() 

3522 # initialize properties on all mappers 

3523 # note that _mapper_registry is unordered, which 

3524 # may randomly conceal/reveal issues related to 

3525 # the order of mapper compilation 

3526 

3527 _do_configure_registries(registries, cascade) 

3528 finally: 

3529 _already_compiling = False 

3530 Mapper.dispatch._for_class(Mapper).after_configured() 

3531 

3532 

3533@util.preload_module("sqlalchemy.orm.decl_api") 

3534def _do_configure_registries(registries, cascade): 

3535 

3536 registry = util.preloaded.orm_decl_api.registry 

3537 

3538 orig = set(registries) 

3539 

3540 for reg in registry._recurse_with_dependencies(registries): 

3541 has_skip = False 

3542 

3543 for mapper in reg._mappers_to_configure(): 

3544 run_configure = None 

3545 for fn in mapper.dispatch.before_mapper_configured: 

3546 run_configure = fn(mapper, mapper.class_) 

3547 if run_configure is EXT_SKIP: 

3548 has_skip = True 

3549 break 

3550 if run_configure is EXT_SKIP: 

3551 continue 

3552 

3553 if getattr(mapper, "_configure_failed", False): 

3554 e = sa_exc.InvalidRequestError( 

3555 "One or more mappers failed to initialize - " 

3556 "can't proceed with initialization of other " 

3557 "mappers. Triggering mapper: '%s'. " 

3558 "Original exception was: %s" 

3559 % (mapper, mapper._configure_failed) 

3560 ) 

3561 e._configure_failed = mapper._configure_failed 

3562 raise e 

3563 

3564 if not mapper.configured: 

3565 try: 

3566 mapper._post_configure_properties() 

3567 mapper._expire_memoizations() 

3568 mapper.dispatch.mapper_configured(mapper, mapper.class_) 

3569 except Exception: 

3570 exc = sys.exc_info()[1] 

3571 if not hasattr(exc, "_configure_failed"): 

3572 mapper._configure_failed = exc 

3573 raise 

3574 if not has_skip: 

3575 reg._new_mappers = False 

3576 

3577 if not cascade and reg._dependencies.difference(orig): 

3578 raise sa_exc.InvalidRequestError( 

3579 "configure was called with cascade=False but " 

3580 "additional registries remain" 

3581 ) 

3582 

3583 

3584@util.preload_module("sqlalchemy.orm.decl_api") 

3585def _dispose_registries(registries, cascade): 

3586 

3587 registry = util.preloaded.orm_decl_api.registry 

3588 

3589 orig = set(registries) 

3590 

3591 for reg in registry._recurse_with_dependents(registries): 

3592 if not cascade and reg._dependents.difference(orig): 

3593 raise sa_exc.InvalidRequestError( 

3594 "Registry has dependent registries that are not disposed; " 

3595 "pass cascade=True to clear these also" 

3596 ) 

3597 

3598 while reg._managers: 

3599 try: 

3600 manager, _ = reg._managers.popitem() 

3601 except KeyError: 

3602 # guard against race between while and popitem 

3603 pass 

3604 else: 

3605 reg._dispose_manager_and_mapper(manager) 

3606 

3607 reg._non_primary_mappers.clear() 

3608 reg._dependents.clear() 

3609 for dep in reg._dependencies: 

3610 dep._dependents.discard(reg) 

3611 reg._dependencies.clear() 

3612 # this wasn't done in the 1.3 clear_mappers() and in fact it 

3613 # was a bug, as it could cause configure_mappers() to invoke 

3614 # the "before_configured" event even though mappers had all been 

3615 # disposed. 

3616 reg._new_mappers = False 

3617 

3618 

3619def reconstructor(fn): 

3620 """Decorate a method as the 'reconstructor' hook. 

3621 

3622 Designates a single method as the "reconstructor", an ``__init__``-like 

3623 method that will be called by the ORM after the instance has been 

3624 loaded from the database or otherwise reconstituted. 

3625 

3626 .. tip:: 

3627 

3628 The :func:`_orm.reconstructor` decorator makes use of the 

3629 :meth:`_orm.InstanceEvents.load` event hook, which can be 

3630 used directly. 

3631 

3632 The reconstructor will be invoked with no arguments. Scalar 

3633 (non-collection) database-mapped attributes of the instance will 

3634 be available for use within the function. Eagerly-loaded 

3635 collections are generally not yet available and will usually only 

3636 contain the first element. ORM state changes made to objects at 

3637 this stage will not be recorded for the next flush() operation, so 

3638 the activity within a reconstructor should be conservative. 

3639 

3640 .. seealso:: 

3641 

3642 :meth:`.InstanceEvents.load` 

3643 

3644 """ 

3645 fn.__sa_reconstructor__ = True 

3646 return fn 

3647 

3648 

3649def validates(*names, **kw): 

3650 r"""Decorate a method as a 'validator' for one or more named properties. 

3651 

3652 Designates a method as a validator, a method which receives the 

3653 name of the attribute as well as a value to be assigned, or in the 

3654 case of a collection, the value to be added to the collection. 

3655 The function can then raise validation exceptions to halt the 

3656 process from continuing (where Python's built-in ``ValueError`` 

3657 and ``AssertionError`` exceptions are reasonable choices), or can 

3658 modify or replace the value before proceeding. The function should 

3659 otherwise return the given value. 

3660 

3661 Note that a validator for a collection **cannot** issue a load of that 

3662 collection within the validation routine - this usage raises 

3663 an assertion to avoid recursion overflows. This is a reentrant 

3664 condition which is not supported. 

3665 

3666 :param \*names: list of attribute names to be validated. 

3667 :param include_removes: if True, "remove" events will be 

3668 sent as well - the validation function must accept an additional 

3669 argument "is_remove" which will be a boolean. 

3670 

3671 :param include_backrefs: defaults to ``True``; if ``False``, the 

3672 validation function will not emit if the originator is an attribute 

3673 event related via a backref. This can be used for bi-directional 

3674 :func:`.validates` usage where only one validator should emit per 

3675 attribute operation. 

3676 

3677 .. versionadded:: 0.9.0 

3678 

3679 .. seealso:: 

3680 

3681 :ref:`simple_validators` - usage examples for :func:`.validates` 

3682 

3683 """ 

3684 include_removes = kw.pop("include_removes", False) 

3685 include_backrefs = kw.pop("include_backrefs", True) 

3686 

3687 def wrap(fn): 

3688 fn.__sa_validators__ = names 

3689 fn.__sa_validation_opts__ = { 

3690 "include_removes": include_removes, 

3691 "include_backrefs": include_backrefs, 

3692 } 

3693 return fn 

3694 

3695 return wrap 

3696 

3697 

3698def _event_on_load(state, ctx): 

3699 instrumenting_mapper = state.manager.mapper 

3700 

3701 if instrumenting_mapper._reconstructor: 

3702 instrumenting_mapper._reconstructor(state.obj()) 

3703 

3704 

3705def _event_on_init(state, args, kwargs): 

3706 """Run init_instance hooks. 

3707 

3708 This also includes mapper compilation, normally not needed 

3709 here but helps with some piecemeal configuration 

3710 scenarios (such as in the ORM tutorial). 

3711 

3712 """ 

3713 

3714 instrumenting_mapper = state.manager.mapper 

3715 if instrumenting_mapper: 

3716 instrumenting_mapper._check_configure() 

3717 if instrumenting_mapper._set_polymorphic_identity: 

3718 instrumenting_mapper._set_polymorphic_identity(state) 

3719 

3720 

3721class _ColumnMapping(dict): 

3722 """Error reporting helper for mapper._columntoproperty.""" 

3723 

3724 __slots__ = ("mapper",) 

3725 

3726 def __init__(self, mapper): 

3727 # TODO: weakref would be a good idea here 

3728 self.mapper = mapper 

3729 

3730 def __missing__(self, column): 

3731 prop = self.mapper._props.get(column) 

3732 if prop: 

3733 raise orm_exc.UnmappedColumnError( 

3734 "Column '%s.%s' is not available, due to " 

3735 "conflicting property '%s':%r" 

3736 % (column.table.name, column.name, column.key, prop) 

3737 ) 

3738 raise orm_exc.UnmappedColumnError( 

3739 "No column %s is configured on mapper %s..." 

3740 % (column, self.mapper) 

3741 )