Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1# orm/mapper.py 

2# Copyright (C) 2005-2020 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: http://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 types 

23import weakref 

24 

25from . import attributes 

26from . import exc as orm_exc 

27from . import instrumentation 

28from . import loading 

29from . import properties 

30from . import util as orm_util 

31from .base import _class_to_mapper 

32from .base import _INSTRUMENTOR 

33from .base import _state_mapper 

34from .base import class_mapper 

35from .base import state_str 

36from .interfaces import _MappedAttribute 

37from .interfaces import EXT_SKIP 

38from .interfaces import InspectionAttr 

39from .interfaces import MapperProperty 

40from .path_registry import PathRegistry 

41from .. import event 

42from .. import exc as sa_exc 

43from .. import inspection 

44from .. import log 

45from .. import schema 

46from .. import sql 

47from .. import util 

48from ..sql import expression 

49from ..sql import operators 

50from ..sql import util as sql_util 

51from ..sql import visitors 

52 

53 

54_mapper_registry = weakref.WeakKeyDictionary() 

55_already_compiling = False 

56 

57_memoized_configured_property = util.group_expirable_memoized_property() 

58 

59 

60# a constant returned by _get_attr_by_column to indicate 

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

62# column 

63NO_ATTRIBUTE = util.symbol("NO_ATTRIBUTE") 

64 

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

66_CONFIGURE_MUTEX = util.threading.RLock() 

67 

68 

69@inspection._self_inspects 

70@log.class_logger 

71class Mapper(InspectionAttr): 

72 """Define the correlation of class attributes to database table 

73 columns. 

74 

75 The :class:`_orm.Mapper` object is instantiated using the 

76 :func:`~sqlalchemy.orm.mapper` function. For information 

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

78 that function's documentation. 

79 

80 

81 When :func:`.mapper` is used 

82 explicitly to link a user defined class with table 

83 metadata, this is referred to as *classical mapping*. 

84 Modern SQLAlchemy usage tends to favor the 

85 :mod:`sqlalchemy.ext.declarative` extension for class 

86 configuration, which 

87 makes usage of :func:`.mapper` behind the scenes. 

88 

89 Given a particular class known to be mapped by the ORM, 

90 the :class:`_orm.Mapper` which maintains it can be acquired 

91 using the :func:`_sa.inspect` function:: 

92 

93 from sqlalchemy import inspect 

94 

95 mapper = inspect(MyClass) 

96 

97 A class which was mapped by the :mod:`sqlalchemy.ext.declarative` 

98 extension will also have its mapper available via the ``__mapper__`` 

99 attribute. 

100 

101 

102 """ 

103 

104 _new_mappers = False 

105 _dispose_called = False 

106 

107 @util.deprecated_params( 

108 extension=( 

109 "0.7", 

110 ":class:`.MapperExtension` is deprecated in favor of the " 

111 ":class:`.MapperEvents` listener interface. The " 

112 ":paramref:`.mapper.extension` parameter will be " 

113 "removed in a future release.", 

114 ), 

115 order_by=( 

116 "1.1", 

117 "The :paramref:`.mapper.order_by` parameter " 

118 "is deprecated, and will be removed in a future release. " 

119 "Use :meth:`_query.Query.order_by` " 

120 "to determine the ordering of a " 

121 "result set.", 

122 ), 

123 non_primary=( 

124 "1.3", 

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

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

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

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

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

130 ), 

131 ) 

132 def __init__( 

133 self, 

134 class_, 

135 local_table=None, 

136 properties=None, 

137 primary_key=None, 

138 non_primary=False, 

139 inherits=None, 

140 inherit_condition=None, 

141 inherit_foreign_keys=None, 

142 extension=None, 

143 order_by=False, 

144 always_refresh=False, 

145 version_id_col=None, 

146 version_id_generator=None, 

147 polymorphic_on=None, 

148 _polymorphic_map=None, 

149 polymorphic_identity=None, 

150 concrete=False, 

151 with_polymorphic=None, 

152 polymorphic_load=None, 

153 allow_partial_pks=True, 

154 batch=True, 

155 column_prefix=None, 

156 include_properties=None, 

157 exclude_properties=None, 

158 passive_updates=True, 

159 passive_deletes=False, 

160 confirm_deleted_rows=True, 

161 eager_defaults=False, 

162 legacy_is_orphan=False, 

163 _compiled_cache_size=100, 

164 ): 

165 r"""Return a new :class:`_orm.Mapper` object. 

166 

167 This function is typically used behind the scenes 

168 via the Declarative extension. When using Declarative, 

169 many of the usual :func:`.mapper` arguments are handled 

170 by the Declarative extension itself, including ``class_``, 

171 ``local_table``, ``properties``, and ``inherits``. 

172 Other options are passed to :func:`.mapper` using 

173 the ``__mapper_args__`` class variable:: 

174 

175 class MyClass(Base): 

176 __tablename__ = 'my_table' 

177 id = Column(Integer, primary_key=True) 

178 type = Column(String(50)) 

179 alt = Column("some_alt", Integer) 

180 

181 __mapper_args__ = { 

182 'polymorphic_on' : type 

183 } 

184 

185 

186 Explicit use of :func:`.mapper` 

187 is often referred to as *classical mapping*. The above 

188 declarative example is equivalent in classical form to:: 

189 

190 my_table = Table("my_table", metadata, 

191 Column('id', Integer, primary_key=True), 

192 Column('type', String(50)), 

193 Column("some_alt", Integer) 

194 ) 

195 

196 class MyClass(object): 

197 pass 

198 

199 mapper(MyClass, my_table, 

200 polymorphic_on=my_table.c.type, 

201 properties={ 

202 'alt':my_table.c.some_alt 

203 }) 

204 

205 .. seealso:: 

206 

207 :ref:`classical_mapping` - discussion of direct usage of 

208 :func:`.mapper` 

209 

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

211 this argument is automatically passed as the declared class 

212 itself. 

213 

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

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

216 this mapper inherits from another mapper using single-table 

217 inheritance. When using Declarative, this argument is 

218 automatically passed by the extension, based on what 

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

220 :class:`_schema.Table` 

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

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

223 

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

225 class will overwrite all data within object instances that already 

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

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

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

229 :meth:`_query.Query.populate_existing`. 

230 

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

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

233 possibly existing within the database. This affects whether a 

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

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

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

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

238 

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

240 of multiple entities can be batched together for efficiency. 

241 Setting to False indicates 

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

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

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

245 in between individual row persistence operations. 

246 

247 :param column_prefix: A string which will be prepended 

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

249 objects are automatically assigned as attributes to the 

250 mapped class. Does not affect explicitly specified 

251 column-based properties. 

252 

253 See the section :ref:`column_prefix` for an example. 

254 

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

256 table inheritance with its parent mapper. 

257 

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

259 

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

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

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

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

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

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

266 exception in a future release. 

267 

268 .. versionadded:: 0.9.4 - added 

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

270 matched row checking on delete. 

271 

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

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

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

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

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

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

278 inserted or updated, which note can add significant performance 

279 overhead. However, if the 

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

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

282 greatly enhance performance for an application that needs frequent 

283 access to just-generated server defaults. 

284 

285 .. seealso:: 

286 

287 :ref:`orm_server_defaults` 

288 

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

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

291 

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

293 be excluded from mapping. 

294 

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

296 

297 :param extension: A :class:`.MapperExtension` instance or 

298 list of :class:`.MapperExtension` instances which will be applied 

299 to all operations by this :class:`_orm.Mapper`. 

300 

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

302 names to map. 

303 

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

305 

306 :param inherits: A mapped class or the corresponding 

307 :class:`_orm.Mapper` 

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

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

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

311 is passed automatically as a result of the natural class 

312 hierarchy of the declared classes. 

313 

314 .. seealso:: 

315 

316 :ref:`inheritance_toplevel` 

317 

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

319 expression which will 

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

321 between the two tables. 

322 

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

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

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

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

327 

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

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

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

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

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

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

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

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

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

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

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

339 orphanable object has been flushed yet or not. 

340 

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

342 for more detail on this change. 

343 

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

345 is in addition 

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

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

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

349 only. 

350 

351 :paramref:`_orm.Mapper.non_primary` is not an often used option, but 

352 is useful in some specific :func:`_orm.relationship` cases. 

353 

354 .. seealso:: 

355 

356 :ref:`relationship_non_primary_mapper` 

357 

358 :param order_by: A single :class:`_schema.Column` or list of 

359 :class:`_schema.Column` 

360 objects for which selection operations should use as the default 

361 ordering for entities. By default mappers have no pre-defined 

362 ordering. 

363 

364 :param passive_deletes: Indicates DELETE behavior of foreign key 

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

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

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

368 on the superclass mapper. 

369 

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

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

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

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

374 superclass table, and not this table. 

375 

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

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

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

379 validate these attributes; note that the primary key columns 

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

381 the object as a whole. 

382 

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

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

385 to specify passive_deletes without this taking effect for 

386 all subclass mappers. 

387 

388 .. versionadded:: 1.1 

389 

390 .. seealso:: 

391 

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

393 used with :func:`_orm.relationship` 

394 

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

396 CASCADE for joined-table inheritance mappers 

397 

398 :param passive_updates: Indicates UPDATE behavior of foreign key 

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

400 inheritance mapping. Defaults to ``True``. 

401 

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

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

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

405 on joined-table rows. 

406 

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

408 referential integrity and will not be issuing its own CASCADE 

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

410 emit an UPDATE statement for the dependent columns during a 

411 primary key change. 

412 

413 .. seealso:: 

414 

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

416 used with :func:`_orm.relationship` 

417 

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

419 CASCADE for joined-table inheritance mappers 

420 

421 :param polymorphic_load: Specifies "polymorphic loading" behavior 

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

423 table inheritance only). Valid values are: 

424 

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

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

427 in a SELECT query against the base. 

428 

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

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

431 the columns specific to this subclass. The SELECT uses 

432 IN to fetch multiple subclasses at once. 

433 

434 .. versionadded:: 1.2 

435 

436 .. seealso:: 

437 

438 :ref:`with_polymorphic_mapper_config` 

439 

440 :ref:`polymorphic_selectin` 

441 

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

443 SQL expression used to determine the target class for an 

444 incoming row, when inheriting classes are present. 

445 

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

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

448 

449 class Employee(Base): 

450 __tablename__ = 'employee' 

451 

452 id = Column(Integer, primary_key=True) 

453 discriminator = Column(String(50)) 

454 

455 __mapper_args__ = { 

456 "polymorphic_on":discriminator, 

457 "polymorphic_identity":"employee" 

458 } 

459 

460 It may also be specified 

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

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

463 approach:: 

464 

465 class Employee(Base): 

466 __tablename__ = 'employee' 

467 

468 id = Column(Integer, primary_key=True) 

469 discriminator = Column(String(50)) 

470 

471 __mapper_args__ = { 

472 "polymorphic_on":case([ 

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

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

475 ], else_="employee"), 

476 "polymorphic_identity":"employee" 

477 } 

478 

479 It may also refer to any attribute 

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

481 string name of one:: 

482 

483 class Employee(Base): 

484 __tablename__ = 'employee' 

485 

486 id = Column(Integer, primary_key=True) 

487 discriminator = Column(String(50)) 

488 employee_type = column_property( 

489 case([ 

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

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

492 ], else_="employee") 

493 ) 

494 

495 __mapper_args__ = { 

496 "polymorphic_on":employee_type, 

497 "polymorphic_identity":"employee" 

498 } 

499 

500 When setting ``polymorphic_on`` to reference an 

501 attribute or expression that's not present in the 

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

503 of the discriminator should be persisted to the database, 

504 the value of the 

505 discriminator is not automatically set on new 

506 instances; this must be handled by the user, 

507 either through manual means or via event listeners. 

508 A typical approach to establishing such a listener 

509 looks like:: 

510 

511 from sqlalchemy import event 

512 from sqlalchemy.orm import object_mapper 

513 

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

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

516 mapper = object_mapper(instance) 

517 instance.discriminator = mapper.polymorphic_identity 

518 

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

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

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

522 in the database. 

523 

524 .. warning:: 

525 

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

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

528 columns are not yet supported. 

529 

530 .. seealso:: 

531 

532 :ref:`inheritance_toplevel` 

533 

534 :param polymorphic_identity: Specifies the value which 

535 identifies this particular class as returned by the 

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

537 setting. As rows are received, the value corresponding 

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

539 to this value, indicating which subclass should 

540 be used for the newly reconstructed object. 

541 

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

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

544 persistence behavior of that attribute. Note that 

545 :class:`_schema.Column` 

546 objects present in 

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

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

549 When using Declarative, this argument is passed automatically, 

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

551 in the declared class body. 

552 

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

554 objects which define 

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

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

557 can be overridden here. 

558 

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

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

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

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

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

564 version id, a 

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

566 thrown. 

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

568 unless ``version_id_generator`` specifies an alternative version 

569 generator. 

570 

571 .. seealso:: 

572 

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

574 and rationale. 

575 

576 :param version_id_generator: Define how new version ids should 

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

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

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

580 

581 def generate_version(version): 

582 return next_version 

583 

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

585 or programmatic versioning schemes outside of the version id 

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

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

588 of important points when using this option. 

589 

590 .. versionadded:: 0.9.0 ``version_id_generator`` supports 

591 server-side version number generation. 

592 

593 .. seealso:: 

594 

595 :ref:`custom_version_counter` 

596 

597 :ref:`server_side_version_counter` 

598 

599 

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

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

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

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

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

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

606 loaded immediately. The second tuple argument <selectable> 

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

608 classes. 

609 

610 .. seealso:: 

611 

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

613 techniques. 

614 

615 """ 

616 

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

618 

619 self.class_manager = None 

620 

621 self._primary_key_argument = util.to_list(primary_key) 

622 self.non_primary = non_primary 

623 

624 if order_by is not False: 

625 self.order_by = util.to_list(order_by) 

626 else: 

627 self.order_by = order_by 

628 

629 self.always_refresh = always_refresh 

630 

631 if isinstance(version_id_col, MapperProperty): 

632 self.version_id_prop = version_id_col 

633 self.version_id_col = None 

634 else: 

635 self.version_id_col = version_id_col 

636 if version_id_generator is False: 

637 self.version_id_generator = False 

638 elif version_id_generator is None: 

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

640 else: 

641 self.version_id_generator = version_id_generator 

642 

643 self.concrete = concrete 

644 self.single = False 

645 self.inherits = inherits 

646 self.local_table = local_table 

647 self.inherit_condition = inherit_condition 

648 self.inherit_foreign_keys = inherit_foreign_keys 

649 self._init_properties = properties or {} 

650 self._delete_orphans = [] 

651 self.batch = batch 

652 self.eager_defaults = eager_defaults 

653 self.column_prefix = column_prefix 

654 self.polymorphic_on = expression._clause_element_as_expr( 

655 polymorphic_on 

656 ) 

657 self._dependency_processors = [] 

658 self.validators = util.immutabledict() 

659 self.passive_updates = passive_updates 

660 self.passive_deletes = passive_deletes 

661 self.legacy_is_orphan = legacy_is_orphan 

662 self._clause_adapter = None 

663 self._requires_row_aliasing = False 

664 self._inherits_equated_pairs = None 

665 self._memoized_values = {} 

666 self._compiled_cache_size = _compiled_cache_size 

667 self._reconstructor = None 

668 self._deprecated_extensions = util.to_list(extension or []) 

669 self.allow_partial_pks = allow_partial_pks 

670 

671 if self.inherits and not self.concrete: 

672 self.confirm_deleted_rows = False 

673 else: 

674 self.confirm_deleted_rows = confirm_deleted_rows 

675 

676 if isinstance(self.local_table, expression.SelectBase): 

677 raise sa_exc.InvalidRequestError( 

678 "When mapping against a select() construct, map against " 

679 "an alias() of the construct instead." 

680 "This because several databases don't allow a " 

681 "SELECT from a subquery that does not have an alias." 

682 ) 

683 

684 self._set_with_polymorphic(with_polymorphic) 

685 self.polymorphic_load = polymorphic_load 

686 

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

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

689 # the object instance for that row. 

690 self.polymorphic_identity = polymorphic_identity 

691 

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

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

694 # upon a select operation. 

695 if _polymorphic_map is None: 

696 self.polymorphic_map = {} 

697 else: 

698 self.polymorphic_map = _polymorphic_map 

699 

700 if include_properties is not None: 

701 self.include_properties = util.to_set(include_properties) 

702 else: 

703 self.include_properties = None 

704 if exclude_properties: 

705 self.exclude_properties = util.to_set(exclude_properties) 

706 else: 

707 self.exclude_properties = None 

708 

709 self.configured = False 

710 

711 # prevent this mapper from being constructed 

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

713 # configure_mappers() until construction succeeds) 

714 _CONFIGURE_MUTEX.acquire() 

715 try: 

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

717 self._configure_inheritance() 

718 self._configure_legacy_instrument_class() 

719 self._configure_class_instrumentation() 

720 self._configure_listeners() 

721 self._configure_properties() 

722 self._configure_polymorphic_setter() 

723 self._configure_pks() 

724 Mapper._new_mappers = True 

725 self._log("constructed") 

726 self._expire_memoizations() 

727 finally: 

728 _CONFIGURE_MUTEX.release() 

729 

730 # major attributes initialized at the classlevel so that 

731 # they can be Sphinx-documented. 

732 

733 is_mapper = True 

734 """Part of the inspection API.""" 

735 

736 represents_outer_join = False 

737 

738 @property 

739 def mapper(self): 

740 """Part of the inspection API. 

741 

742 Returns self. 

743 

744 """ 

745 return self 

746 

747 @property 

748 def entity(self): 

749 r"""Part of the inspection API. 

750 

751 Returns self.class\_. 

752 

753 """ 

754 return self.class_ 

755 

756 local_table = None 

757 """The :class:`expression.Selectable` which this :class:`_orm.Mapper` 

758 manages. 

759 

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

761 :class:`_expression.Alias`. 

762 May also be ``None``. 

763 

764 The "local" table is the 

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

766 managing from an attribute access and flush perspective. For 

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

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

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

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

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

772 

773 .. seealso:: 

774 

775 :attr:`_orm.Mapper.persist_selectable`. 

776 

777 """ 

778 

779 persist_selectable = None 

780 """The :class:`expression.Selectable` to which this :class:`_orm.Mapper` 

781 is mapped. 

782 

783 Typically an instance of :class:`_schema.Table`, :class:`_expression.Join` 

784 , or 

785 :class:`_expression.Alias`. 

786 

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

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

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

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

791 which are in fact mapped on subclasses only. 

792 

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

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

795 

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

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

798 are locally mapped on this class directly. 

799 

800 

801 .. seealso:: 

802 

803 :attr:`_orm.Mapper.selectable`. 

804 

805 :attr:`_orm.Mapper.local_table`. 

806 

807 """ 

808 

809 inherits = None 

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

811 inherits from, if any. 

812 

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

814 Behavior is undefined if directly modified. 

815 

816 """ 

817 

818 configured = None 

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

820 

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

822 Behavior is undefined if directly modified. 

823 

824 .. seealso:: 

825 

826 :func:`.configure_mappers`. 

827 

828 """ 

829 

830 concrete = None 

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

832 inheritance mapper. 

833 

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

835 Behavior is undefined if directly modified. 

836 

837 """ 

838 

839 tables = None 

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

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

842 

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

844 :class:`_expression.Alias` 

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

846 :class:`_schema.Table` 

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

848 

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

850 Behavior is undefined if directly modified. 

851 

852 """ 

853 

854 primary_key = None 

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

856 objects 

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

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

859 

860 This list is against the selectable in 

861 :attr:`_orm.Mapper.persist_selectable`. 

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

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

864 :class:`_expression.Join`, the 

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

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

867 

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

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

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

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

872 

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

874 Behavior is undefined if directly modified. 

875 

876 """ 

877 

878 class_ = None 

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

880 

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

882 Behavior is undefined if directly modified. 

883 

884 """ 

885 

886 class_manager = None 

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

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

889 

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

891 Behavior is undefined if directly modified. 

892 

893 """ 

894 

895 single = None 

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

897 inheritance mapper. 

898 

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

900 

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

902 Behavior is undefined if directly modified. 

903 

904 """ 

905 

906 non_primary = None 

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

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

909 persistence management. 

910 

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

912 Behavior is undefined if directly modified. 

913 

914 """ 

915 

916 polymorphic_on = None 

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

918 ``polymorphic_on`` argument 

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

920 

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

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

923 :func:`.cast`. 

924 

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

926 Behavior is undefined if directly modified. 

927 

928 """ 

929 

930 polymorphic_map = None 

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

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

933 

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

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

936 

937 An inheritance chain of mappers will all reference the same 

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

939 result rows to target mappers. 

940 

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

942 Behavior is undefined if directly modified. 

943 

944 """ 

945 

946 polymorphic_identity = None 

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

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

949 

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

951 comparable to the type of column represented by 

952 :attr:`_orm.Mapper.polymorphic_on`. 

953 

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

955 Behavior is undefined if directly modified. 

956 

957 """ 

958 

959 base_mapper = None 

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

961 

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

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

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

965 objects in the inheritance chain. 

966 

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

968 Behavior is undefined if directly modified. 

969 

970 """ 

971 

972 columns = None 

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

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

975 

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

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

978 except that only those columns included in 

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

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

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

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

983 

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

985 Behavior is undefined if directly modified. 

986 

987 """ 

988 

989 validators = None 

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

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

992 

993 The dictionary contains string attribute names as keys 

994 mapped to the actual validation method. 

995 

996 """ 

997 

998 c = None 

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

1000 

1001 @property 

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

1003 def mapped_table(self): 

1004 return self.persist_selectable 

1005 

1006 @util.memoized_property 

1007 def _path_registry(self): 

1008 return PathRegistry.per_mapper(self) 

1009 

1010 def _configure_inheritance(self): 

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

1012 being present.""" 

1013 

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

1015 self._inheriting_mappers = util.WeakSequence() 

1016 

1017 if self.inherits: 

1018 if isinstance(self.inherits, type): 

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

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

1021 raise sa_exc.ArgumentError( 

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

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

1024 ) 

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

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

1027 raise sa_exc.ArgumentError( 

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

1029 "only allowed from a %s mapper" 

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

1031 ) 

1032 # inherit_condition is optional. 

1033 if self.local_table is None: 

1034 self.local_table = self.inherits.local_table 

1035 self.persist_selectable = self.inherits.persist_selectable 

1036 self.single = True 

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

1038 if self.concrete: 

1039 self.persist_selectable = self.local_table 

1040 for mapper in self.iterate_to_root(): 

1041 if mapper.polymorphic_on is not None: 

1042 mapper._requires_row_aliasing = True 

1043 else: 

1044 if self.inherit_condition is None: 

1045 # figure out inherit condition from our table to the 

1046 # immediate table of the inherited mapper, not its 

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

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

1049 self.inherit_condition = sql_util.join_condition( 

1050 self.inherits.local_table, self.local_table 

1051 ) 

1052 self.persist_selectable = sql.join( 

1053 self.inherits.persist_selectable, 

1054 self.local_table, 

1055 self.inherit_condition, 

1056 ) 

1057 

1058 fks = util.to_set(self.inherit_foreign_keys) 

1059 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 

1060 self.persist_selectable.onclause, 

1061 consider_as_foreign_keys=fks, 

1062 ) 

1063 else: 

1064 self.persist_selectable = self.local_table 

1065 

1066 if self.polymorphic_identity is not None and not self.concrete: 

1067 self._identity_class = self.inherits._identity_class 

1068 else: 

1069 self._identity_class = self.class_ 

1070 

1071 if self.version_id_col is None: 

1072 self.version_id_col = self.inherits.version_id_col 

1073 self.version_id_generator = self.inherits.version_id_generator 

1074 elif ( 

1075 self.inherits.version_id_col is not None 

1076 and self.version_id_col is not self.inherits.version_id_col 

1077 ): 

1078 util.warn( 

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

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

1081 "the inherited versioning column. " 

1082 "version_id_col should only be specified on " 

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

1084 % ( 

1085 self.version_id_col.description, 

1086 self.inherits.version_id_col.description, 

1087 ) 

1088 ) 

1089 

1090 if ( 

1091 self.order_by is False 

1092 and not self.concrete 

1093 and self.inherits.order_by is not False 

1094 ): 

1095 self.order_by = self.inherits.order_by 

1096 

1097 self.polymorphic_map = self.inherits.polymorphic_map 

1098 self.batch = self.inherits.batch 

1099 self.inherits._inheriting_mappers.append(self) 

1100 self.base_mapper = self.inherits.base_mapper 

1101 self.passive_updates = self.inherits.passive_updates 

1102 self.passive_deletes = ( 

1103 self.inherits.passive_deletes or self.passive_deletes 

1104 ) 

1105 self._all_tables = self.inherits._all_tables 

1106 

1107 if self.polymorphic_identity is not None: 

1108 if self.polymorphic_identity in self.polymorphic_map: 

1109 util.warn( 

1110 "Reassigning polymorphic association for identity %r " 

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

1112 "value for polymorphic_identity." 

1113 % ( 

1114 self.polymorphic_identity, 

1115 self.polymorphic_map[self.polymorphic_identity], 

1116 self, 

1117 self.polymorphic_identity, 

1118 ) 

1119 ) 

1120 self.polymorphic_map[self.polymorphic_identity] = self 

1121 

1122 if self.polymorphic_load and self.concrete: 

1123 raise sa_exc.ArgumentError( 

1124 "polymorphic_load is not currently supported " 

1125 "with concrete table inheritance" 

1126 ) 

1127 if self.polymorphic_load == "inline": 

1128 self.inherits._add_with_polymorphic_subclass(self) 

1129 elif self.polymorphic_load == "selectin": 

1130 pass 

1131 elif self.polymorphic_load is not None: 

1132 raise sa_exc.ArgumentError( 

1133 "unknown argument for polymorphic_load: %r" 

1134 % self.polymorphic_load 

1135 ) 

1136 

1137 else: 

1138 self._all_tables = set() 

1139 self.base_mapper = self 

1140 self.persist_selectable = self.local_table 

1141 if self.polymorphic_identity is not None: 

1142 self.polymorphic_map[self.polymorphic_identity] = self 

1143 self._identity_class = self.class_ 

1144 

1145 if self.persist_selectable is None: 

1146 raise sa_exc.ArgumentError( 

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

1148 % self 

1149 ) 

1150 

1151 def _set_with_polymorphic(self, with_polymorphic): 

1152 if with_polymorphic == "*": 

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

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

1155 if isinstance( 

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

1157 ): 

1158 self.with_polymorphic = with_polymorphic 

1159 else: 

1160 self.with_polymorphic = (with_polymorphic, None) 

1161 elif with_polymorphic is not None: 

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

1163 else: 

1164 self.with_polymorphic = None 

1165 

1166 if isinstance(self.local_table, expression.SelectBase): 

1167 raise sa_exc.InvalidRequestError( 

1168 "When mapping against a select() construct, map against " 

1169 "an alias() of the construct instead." 

1170 "This because several databases don't allow a " 

1171 "SELECT from a subquery that does not have an alias." 

1172 ) 

1173 

1174 if self.with_polymorphic and isinstance( 

1175 self.with_polymorphic[1], expression.SelectBase 

1176 ): 

1177 self.with_polymorphic = ( 

1178 self.with_polymorphic[0], 

1179 self.with_polymorphic[1].alias(), 

1180 ) 

1181 

1182 if self.configured: 

1183 self._expire_memoizations() 

1184 

1185 def _add_with_polymorphic_subclass(self, mapper): 

1186 subcl = mapper.class_ 

1187 if self.with_polymorphic is None: 

1188 self._set_with_polymorphic((subcl,)) 

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

1190 self._set_with_polymorphic( 

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

1192 ) 

1193 

1194 def _set_concrete_base(self, mapper): 

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

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

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

1198 

1199 assert self.concrete 

1200 assert not self.inherits 

1201 assert isinstance(mapper, Mapper) 

1202 self.inherits = mapper 

1203 self.inherits.polymorphic_map.update(self.polymorphic_map) 

1204 self.polymorphic_map = self.inherits.polymorphic_map 

1205 for mapper in self.iterate_to_root(): 

1206 if mapper.polymorphic_on is not None: 

1207 mapper._requires_row_aliasing = True 

1208 self.batch = self.inherits.batch 

1209 for mp in self.self_and_descendants: 

1210 mp.base_mapper = self.inherits.base_mapper 

1211 self.inherits._inheriting_mappers.append(self) 

1212 self.passive_updates = self.inherits.passive_updates 

1213 self._all_tables = self.inherits._all_tables 

1214 

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

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

1217 key, key, local=False, column=None 

1218 ): 

1219 self._adapt_inherited_property(key, prop, False) 

1220 

1221 def _set_polymorphic_on(self, polymorphic_on): 

1222 self.polymorphic_on = polymorphic_on 

1223 self._configure_polymorphic_setter(True) 

1224 

1225 def _configure_legacy_instrument_class(self): 

1226 

1227 if self.inherits: 

1228 self.dispatch._update(self.inherits.dispatch) 

1229 super_extensions = set( 

1230 chain( 

1231 *[ 

1232 m._deprecated_extensions 

1233 for m in self.inherits.iterate_to_root() 

1234 ] 

1235 ) 

1236 ) 

1237 else: 

1238 super_extensions = set() 

1239 

1240 for ext in self._deprecated_extensions: 

1241 if ext not in super_extensions: 

1242 ext._adapt_instrument_class(self, ext) 

1243 

1244 def _configure_listeners(self): 

1245 if self.inherits: 

1246 super_extensions = set( 

1247 chain( 

1248 *[ 

1249 m._deprecated_extensions 

1250 for m in self.inherits.iterate_to_root() 

1251 ] 

1252 ) 

1253 ) 

1254 else: 

1255 super_extensions = set() 

1256 

1257 for ext in self._deprecated_extensions: 

1258 if ext not in super_extensions: 

1259 ext._adapt_listener(self, ext) 

1260 

1261 def _configure_class_instrumentation(self): 

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

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

1264 given class and entity name. 

1265 

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

1267 name combination will return this mapper. Also decorate the 

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

1269 auto-session attachment logic. 

1270 

1271 """ 

1272 

1273 manager = attributes.manager_of_class(self.class_) 

1274 

1275 if self.non_primary: 

1276 if not manager or not manager.is_mapped: 

1277 raise sa_exc.InvalidRequestError( 

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

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

1280 "Mapper." % self.class_ 

1281 ) 

1282 self.class_manager = manager 

1283 self._identity_class = manager.mapper._identity_class 

1284 _mapper_registry[self] = True 

1285 return 

1286 

1287 if manager is not None: 

1288 assert manager.class_ is self.class_ 

1289 if manager.is_mapped: 

1290 raise sa_exc.ArgumentError( 

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

1292 "Use non_primary=True to " 

1293 "create a non primary Mapper. clear_mappers() will " 

1294 "remove *all* current mappers from all classes." 

1295 % self.class_ 

1296 ) 

1297 # else: 

1298 # a ClassManager may already exist as 

1299 # ClassManager.instrument_attribute() creates 

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

1301 

1302 _mapper_registry[self] = True 

1303 

1304 # note: this *must be called before instrumentation.register_class* 

1305 # to maintain the documented behavior of instrument_class 

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

1307 

1308 if manager is None: 

1309 manager = instrumentation.register_class(self.class_) 

1310 

1311 self.class_manager = manager 

1312 

1313 manager.mapper = self 

1314 manager.deferred_scalar_loader = util.partial( 

1315 loading.load_scalar_attributes, self 

1316 ) 

1317 

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

1319 # e_name None or not. 

1320 if manager.info.get(_INSTRUMENTOR, False): 

1321 return 

1322 

1323 event.listen(manager, "first_init", _event_on_first_init, raw=True) 

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

1325 

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

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

1328 method = method._sa_original_init 

1329 if isinstance(method, types.MethodType): 

1330 method = method.im_func 

1331 if isinstance(method, types.FunctionType): 

1332 if hasattr(method, "__sa_reconstructor__"): 

1333 self._reconstructor = method 

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

1335 elif hasattr(method, "__sa_validators__"): 

1336 validation_opts = method.__sa_validation_opts__ 

1337 for name in method.__sa_validators__: 

1338 if name in self.validators: 

1339 raise sa_exc.InvalidRequestError( 

1340 "A validation function for mapped " 

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

1342 % (name, self) 

1343 ) 

1344 self.validators = self.validators.union( 

1345 {name: (method, validation_opts)} 

1346 ) 

1347 

1348 manager.info[_INSTRUMENTOR] = self 

1349 

1350 @classmethod 

1351 def _configure_all(cls): 

1352 """Class-level path to the :func:`.configure_mappers` call. 

1353 """ 

1354 configure_mappers() 

1355 

1356 def dispose(self): 

1357 # Disable any attribute-based compilation. 

1358 self.configured = True 

1359 self._dispose_called = True 

1360 

1361 if hasattr(self, "_configure_failed"): 

1362 del self._configure_failed 

1363 

1364 if ( 

1365 not self.non_primary 

1366 and self.class_manager is not None 

1367 and self.class_manager.is_mapped 

1368 and self.class_manager.mapper is self 

1369 ): 

1370 instrumentation.unregister_class(self.class_) 

1371 

1372 def _configure_pks(self): 

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

1374 

1375 self._pks_by_table = {} 

1376 self._cols_by_table = {} 

1377 

1378 all_cols = util.column_set( 

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

1380 ) 

1381 

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

1383 

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

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

1386 self._all_tables.update(tables) 

1387 for t in tables: 

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

1389 # ordering is important since it determines the ordering of 

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

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

1392 t.primary_key 

1393 ).intersection(pk_cols) 

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

1395 all_cols 

1396 ) 

1397 

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

1399 # primary key mappings 

1400 if self._primary_key_argument: 

1401 for k in self._primary_key_argument: 

1402 if k.table not in self._pks_by_table: 

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

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

1405 

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

1407 elif ( 

1408 self.persist_selectable not in self._pks_by_table 

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

1410 ): 

1411 raise sa_exc.ArgumentError( 

1412 "Mapper %s could not assemble any primary " 

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

1414 % (self, self.persist_selectable.description) 

1415 ) 

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

1417 self.local_table, schema.Table 

1418 ): 

1419 util.warn( 

1420 "Could not assemble any primary " 

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

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

1423 % self.local_table.description 

1424 ) 

1425 

1426 if ( 

1427 self.inherits 

1428 and not self.concrete 

1429 and not self._primary_key_argument 

1430 ): 

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

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

1433 self.primary_key = self.inherits.primary_key 

1434 else: 

1435 # determine primary key from argument or persist_selectable pks - 

1436 # reduce to the minimal set of columns 

1437 if self._primary_key_argument: 

1438 primary_key = sql_util.reduce_columns( 

1439 [ 

1440 self.persist_selectable.corresponding_column(c) 

1441 for c in self._primary_key_argument 

1442 ], 

1443 ignore_nonexistent_tables=True, 

1444 ) 

1445 else: 

1446 primary_key = sql_util.reduce_columns( 

1447 self._pks_by_table[self.persist_selectable], 

1448 ignore_nonexistent_tables=True, 

1449 ) 

1450 

1451 if len(primary_key) == 0: 

1452 raise sa_exc.ArgumentError( 

1453 "Mapper %s could not assemble any primary " 

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

1455 % (self, self.persist_selectable.description) 

1456 ) 

1457 

1458 self.primary_key = tuple(primary_key) 

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

1460 

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

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

1463 self._readonly_props = set( 

1464 self._columntoproperty[col] 

1465 for col in self._columntoproperty 

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

1467 and ( 

1468 not hasattr(col, "table") 

1469 or col.table not in self._cols_by_table 

1470 ) 

1471 ) 

1472 

1473 def _configure_properties(self): 

1474 # Column and other ClauseElement objects which are mapped 

1475 self.columns = self.c = util.OrderedProperties() 

1476 

1477 # object attribute names mapped to MapperProperty objects 

1478 self._props = util.OrderedDict() 

1479 

1480 # table columns mapped to lists of MapperProperty objects 

1481 # using a list allows a single column to be defined as 

1482 # populating multiple object attributes 

1483 self._columntoproperty = _ColumnMapping(self) 

1484 

1485 # load custom properties 

1486 if self._init_properties: 

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

1488 self._configure_property(key, prop, False) 

1489 

1490 # pull properties from the inherited mapper if any. 

1491 if self.inherits: 

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

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

1494 key, key, local=False, column=None 

1495 ): 

1496 self._adapt_inherited_property(key, prop, False) 

1497 

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

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

1500 for column in self.persist_selectable.columns: 

1501 if column in self._columntoproperty: 

1502 continue 

1503 

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

1505 

1506 if self._should_exclude( 

1507 column.key, 

1508 column_key, 

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

1510 column=column, 

1511 ): 

1512 continue 

1513 

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

1515 # of the inheriting mapper 

1516 for mapper in self.iterate_to_root(): 

1517 if column in mapper._columntoproperty: 

1518 column_key = mapper._columntoproperty[column].key 

1519 

1520 self._configure_property( 

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

1522 ) 

1523 

1524 def _configure_polymorphic_setter(self, init=False): 

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

1526 'polymorphic_on' column, if applicable, and not 

1527 already generated by _configure_properties (which is typical). 

1528 

1529 Also create a setter function which will assign this 

1530 attribute to the value of the 'polymorphic_identity' 

1531 upon instance construction, also if applicable. This 

1532 routine will run when an instance is created. 

1533 

1534 """ 

1535 setter = False 

1536 

1537 if self.polymorphic_on is not None: 

1538 setter = True 

1539 

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

1541 # polymorphic_on specified as a string - link 

1542 # it to mapped ColumnProperty 

1543 try: 

1544 self.polymorphic_on = self._props[self.polymorphic_on] 

1545 except KeyError as err: 

1546 util.raise_( 

1547 sa_exc.ArgumentError( 

1548 "Can't determine polymorphic_on " 

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

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

1551 ), 

1552 replace_context=err, 

1553 ) 

1554 

1555 if self.polymorphic_on in self._columntoproperty: 

1556 # polymorphic_on is a column that is already mapped 

1557 # to a ColumnProperty 

1558 prop = self._columntoproperty[self.polymorphic_on] 

1559 elif isinstance(self.polymorphic_on, MapperProperty): 

1560 # polymorphic_on is directly a MapperProperty, 

1561 # ensure it's a ColumnProperty 

1562 if not isinstance( 

1563 self.polymorphic_on, properties.ColumnProperty 

1564 ): 

1565 raise sa_exc.ArgumentError( 

1566 "Only direct column-mapped " 

1567 "property or SQL expression " 

1568 "can be passed for polymorphic_on" 

1569 ) 

1570 prop = self.polymorphic_on 

1571 elif not expression._is_column(self.polymorphic_on): 

1572 # polymorphic_on is not a Column and not a ColumnProperty; 

1573 # not supported right now. 

1574 raise sa_exc.ArgumentError( 

1575 "Only direct column-mapped " 

1576 "property or SQL expression " 

1577 "can be passed for polymorphic_on" 

1578 ) 

1579 else: 

1580 # polymorphic_on is a Column or SQL expression and 

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

1582 # only present in the with_polymorphic selectable or 

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

1584 # hope is compatible with this mapper's persist_selectable 

1585 col = self.persist_selectable.corresponding_column( 

1586 self.polymorphic_on 

1587 ) 

1588 if col is None: 

1589 # polymorphic_on doesn't derive from any 

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

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

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

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

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

1595 # represented somehow in either persist_selectable or 

1596 # with_polymorphic. Otherwise as of 0.7.4 we 

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

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

1599 setter = False 

1600 instrument = False 

1601 col = self.polymorphic_on 

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

1603 self.with_polymorphic is None 

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

1605 is None 

1606 ): 

1607 raise sa_exc.InvalidRequestError( 

1608 "Could not map polymorphic_on column " 

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

1610 "loads will not function properly" 

1611 % col.description 

1612 ) 

1613 else: 

1614 # column/expression that polymorphic_on derives from 

1615 # is present in our mapped table 

1616 # and is probably mapped, but polymorphic_on itself 

1617 # is not. This happens when 

1618 # the polymorphic_on is only directly present in the 

1619 # with_polymorphic selectable, as when use 

1620 # polymorphic_union. 

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

1622 instrument = True 

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

1624 if key: 

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

1626 raise sa_exc.InvalidRequestError( 

1627 "Cannot exclude or override the " 

1628 "discriminator column %r" % col.key 

1629 ) 

1630 else: 

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

1632 key = col.key 

1633 

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

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

1636 

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

1638 # column in the property 

1639 self.polymorphic_on = prop.columns[0] 

1640 polymorphic_key = prop.key 

1641 

1642 else: 

1643 # no polymorphic_on was set. 

1644 # check inheriting mappers for one. 

1645 for mapper in self.iterate_to_root(): 

1646 # determine if polymorphic_on of the parent 

1647 # should be propagated here. If the col 

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

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

1650 # inheritance), we can use it 

1651 if mapper.polymorphic_on is not None: 

1652 if self.persist_selectable is mapper.persist_selectable: 

1653 self.polymorphic_on = mapper.polymorphic_on 

1654 else: 

1655 self.polymorphic_on = ( 

1656 self.persist_selectable 

1657 ).corresponding_column(mapper.polymorphic_on) 

1658 # we can use the parent mapper's _set_polymorphic_identity 

1659 # directly; it ensures the polymorphic_identity of the 

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

1661 if self.polymorphic_on is not None: 

1662 self._set_polymorphic_identity = ( 

1663 mapper._set_polymorphic_identity 

1664 ) 

1665 self._validate_polymorphic_identity = ( 

1666 mapper._validate_polymorphic_identity 

1667 ) 

1668 else: 

1669 self._set_polymorphic_identity = None 

1670 return 

1671 

1672 if setter: 

1673 

1674 def _set_polymorphic_identity(state): 

1675 dict_ = state.dict 

1676 state.get_impl(polymorphic_key).set( 

1677 state, 

1678 dict_, 

1679 state.manager.mapper.polymorphic_identity, 

1680 None, 

1681 ) 

1682 

1683 def _validate_polymorphic_identity(mapper, state, dict_): 

1684 if ( 

1685 polymorphic_key in dict_ 

1686 and dict_[polymorphic_key] 

1687 not in mapper._acceptable_polymorphic_identities 

1688 ): 

1689 util.warn_limited( 

1690 "Flushing object %s with " 

1691 "incompatible polymorphic identity %r; the " 

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

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

1694 ) 

1695 

1696 self._set_polymorphic_identity = _set_polymorphic_identity 

1697 self._validate_polymorphic_identity = ( 

1698 _validate_polymorphic_identity 

1699 ) 

1700 else: 

1701 self._set_polymorphic_identity = None 

1702 

1703 _validate_polymorphic_identity = None 

1704 

1705 @_memoized_configured_property 

1706 def _version_id_prop(self): 

1707 if self.version_id_col is not None: 

1708 return self._columntoproperty[self.version_id_col] 

1709 else: 

1710 return None 

1711 

1712 @_memoized_configured_property 

1713 def _acceptable_polymorphic_identities(self): 

1714 identities = set() 

1715 

1716 stack = deque([self]) 

1717 while stack: 

1718 item = stack.popleft() 

1719 if item.persist_selectable is self.persist_selectable: 

1720 identities.add(item.polymorphic_identity) 

1721 stack.extend(item._inheriting_mappers) 

1722 

1723 return identities 

1724 

1725 @_memoized_configured_property 

1726 def _prop_set(self): 

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

1728 

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

1730 if not self.concrete: 

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

1732 elif key not in self._props: 

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

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

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

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

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

1738 # descriptors that might have side effects when invoked. 

1739 implementing_attribute = self.class_manager._get_class_attr_mro( 

1740 key, prop 

1741 ) 

1742 if implementing_attribute is prop or ( 

1743 isinstance( 

1744 implementing_attribute, attributes.InstrumentedAttribute 

1745 ) 

1746 and implementing_attribute._parententity is prop.parent 

1747 ): 

1748 self._configure_property( 

1749 key, 

1750 properties.ConcreteInheritedProperty(), 

1751 init=init, 

1752 setparent=True, 

1753 ) 

1754 

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

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

1757 

1758 if not isinstance(prop, MapperProperty): 

1759 prop = self._property_from_column(key, prop) 

1760 

1761 if isinstance(prop, properties.ColumnProperty): 

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

1763 

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

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

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

1767 if col is None and self.inherits: 

1768 path = [self] 

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

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

1771 if col is not None: 

1772 for m2 in path: 

1773 m2.persist_selectable._reset_exported() 

1774 col = self.persist_selectable.corresponding_column( 

1775 prop.columns[0] 

1776 ) 

1777 break 

1778 path.append(m) 

1779 

1780 # subquery expression, column not present in the mapped 

1781 # selectable. 

1782 if col is None: 

1783 col = prop.columns[0] 

1784 

1785 # column is coming in after _readonly_props was 

1786 # initialized; check for 'readonly' 

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

1788 not hasattr(col, "table") 

1789 or col.table not in self._cols_by_table 

1790 ): 

1791 self._readonly_props.add(prop) 

1792 

1793 else: 

1794 # if column is coming in after _cols_by_table was 

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

1796 if ( 

1797 hasattr(self, "_cols_by_table") 

1798 and col.table in self._cols_by_table 

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

1800 ): 

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

1802 

1803 # if this properties.ColumnProperty represents the "polymorphic 

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

1805 # columns in SELECT statements. 

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

1807 prop._is_polymorphic_discriminator = ( 

1808 col is self.polymorphic_on 

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

1810 ) 

1811 

1812 self.columns[key] = col 

1813 for col in prop.columns + prop._orig_columns: 

1814 for col in col.proxy_set: 

1815 self._columntoproperty[col] = prop 

1816 

1817 prop.key = key 

1818 

1819 if setparent: 

1820 prop.set_parent(self, init) 

1821 

1822 if key in self._props and getattr( 

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

1824 ): 

1825 syn = self._props[key]._mapped_by_synonym 

1826 raise sa_exc.ArgumentError( 

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

1828 "a ColumnProperty already exists keyed to the name " 

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

1830 ) 

1831 

1832 if ( 

1833 key in self._props 

1834 and not isinstance(prop, properties.ColumnProperty) 

1835 and not isinstance( 

1836 self._props[key], 

1837 ( 

1838 properties.ColumnProperty, 

1839 properties.ConcreteInheritedProperty, 

1840 ), 

1841 ) 

1842 ): 

1843 util.warn( 

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

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

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

1847 ) 

1848 oldprop = self._props[key] 

1849 self._path_registry.pop(oldprop, None) 

1850 

1851 self._props[key] = prop 

1852 

1853 if not self.non_primary: 

1854 prop.instrument_class(self) 

1855 

1856 for mapper in self._inheriting_mappers: 

1857 mapper._adapt_inherited_property(key, prop, init) 

1858 

1859 if init: 

1860 prop.init() 

1861 prop.post_instrument_class(self) 

1862 

1863 if self.configured: 

1864 self._expire_memoizations() 

1865 

1866 def _property_from_column(self, key, prop): 

1867 """generate/update a :class:`.ColumnProprerty` given a 

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

1869 

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

1871 # generate a properties.ColumnProperty 

1872 columns = util.to_list(prop) 

1873 column = columns[0] 

1874 if not expression._is_column(column): 

1875 raise sa_exc.ArgumentError( 

1876 "%s=%r is not an instance of MapperProperty or Column" 

1877 % (key, prop) 

1878 ) 

1879 

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

1881 

1882 if isinstance(prop, properties.ColumnProperty): 

1883 if ( 

1884 ( 

1885 not self._inherits_equated_pairs 

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

1887 not in self._inherits_equated_pairs 

1888 ) 

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

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

1891 and column is not self.version_id_col 

1892 ): 

1893 warn_only = prop.parent is not self 

1894 msg = ( 

1895 "Implicitly combining column %s with column " 

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

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

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

1899 ) 

1900 if warn_only: 

1901 util.warn(msg) 

1902 else: 

1903 raise sa_exc.InvalidRequestError(msg) 

1904 

1905 # existing properties.ColumnProperty from an inheriting 

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

1907 prop = prop.copy() 

1908 prop.columns.insert(0, column) 

1909 self._log( 

1910 "inserting column to existing list " 

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

1912 ) 

1913 return prop 

1914 elif prop is None or isinstance( 

1915 prop, properties.ConcreteInheritedProperty 

1916 ): 

1917 mapped_column = [] 

1918 for c in columns: 

1919 mc = self.persist_selectable.corresponding_column(c) 

1920 if mc is None: 

1921 mc = self.local_table.corresponding_column(c) 

1922 if mc is not None: 

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

1924 # mapped table, this corresponds to adding a 

1925 # column after the fact to the local table. 

1926 # [ticket:1523] 

1927 self.persist_selectable._reset_exported() 

1928 mc = self.persist_selectable.corresponding_column(c) 

1929 if mc is None: 

1930 raise sa_exc.ArgumentError( 

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

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

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

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

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

1936 ) 

1937 mapped_column.append(mc) 

1938 return properties.ColumnProperty(*mapped_column) 

1939 else: 

1940 raise sa_exc.ArgumentError( 

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

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

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

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

1945 "to remove all awareness of the column entirely " 

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

1947 "use the 'include_properties' or 'exclude_properties' " 

1948 "mapper arguments to control specifically which table " 

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

1950 ) 

1951 

1952 def _post_configure_properties(self): 

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

1954 attached to this mapper. 

1955 

1956 This is a deferred configuration step which is intended 

1957 to execute once all mappers have been constructed. 

1958 

1959 """ 

1960 

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

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

1963 for key, prop in l: 

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

1965 

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

1967 prop.init() 

1968 

1969 if prop._configure_finished: 

1970 prop.post_instrument_class(self) 

1971 

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

1973 self.configured = True 

1974 

1975 def add_properties(self, dict_of_properties): 

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

1977 using `add_property`. 

1978 

1979 """ 

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

1981 self.add_property(key, value) 

1982 

1983 def add_property(self, key, prop): 

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

1985 

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

1987 property to the initial properties dictionary sent to the 

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

1989 the given MapperProperty is configured immediately. 

1990 

1991 """ 

1992 self._init_properties[key] = prop 

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

1994 

1995 def _expire_memoizations(self): 

1996 for mapper in self.iterate_to_root(): 

1997 _memoized_configured_property.expire_instance(mapper) 

1998 

1999 @property 

2000 def _log_desc(self): 

2001 return ( 

2002 "(" 

2003 + self.class_.__name__ 

2004 + "|" 

2005 + ( 

2006 self.local_table is not None 

2007 and self.local_table.description 

2008 or str(self.local_table) 

2009 ) 

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

2011 + ")" 

2012 ) 

2013 

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

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

2016 

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

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

2019 

2020 def __repr__(self): 

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

2022 

2023 def __str__(self): 

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

2025 self.class_.__name__, 

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

2027 self.local_table.description 

2028 if self.local_table is not None 

2029 else self.persist_selectable.description, 

2030 ) 

2031 

2032 def _is_orphan(self, state): 

2033 orphan_possible = False 

2034 for mapper in self.iterate_to_root(): 

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

2036 orphan_possible = True 

2037 

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

2039 state, key, optimistic=state.has_identity 

2040 ) 

2041 

2042 if self.legacy_is_orphan and has_parent: 

2043 return False 

2044 elif not self.legacy_is_orphan and not has_parent: 

2045 return True 

2046 

2047 if self.legacy_is_orphan: 

2048 return orphan_possible 

2049 else: 

2050 return False 

2051 

2052 def has_property(self, key): 

2053 return key in self._props 

2054 

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

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

2057 """ 

2058 

2059 if _configure_mappers and Mapper._new_mappers: 

2060 configure_mappers() 

2061 

2062 try: 

2063 return self._props[key] 

2064 except KeyError as err: 

2065 util.raise_( 

2066 sa_exc.InvalidRequestError( 

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

2068 ), 

2069 replace_context=err, 

2070 ) 

2071 

2072 def get_property_by_column(self, column): 

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

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

2075 

2076 return self._columntoproperty[column] 

2077 

2078 @property 

2079 def iterate_properties(self): 

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

2081 if Mapper._new_mappers: 

2082 configure_mappers() 

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

2084 

2085 def _mappers_from_spec(self, spec, selectable): 

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

2087 represents. 

2088 

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

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

2091 

2092 """ 

2093 if spec == "*": 

2094 mappers = list(self.self_and_descendants) 

2095 elif spec: 

2096 mappers = set() 

2097 for m in util.to_list(spec): 

2098 m = _class_to_mapper(m) 

2099 if not m.isa(self): 

2100 raise sa_exc.InvalidRequestError( 

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

2102 ) 

2103 

2104 if selectable is None: 

2105 mappers.update(m.iterate_to_root()) 

2106 else: 

2107 mappers.add(m) 

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

2109 else: 

2110 mappers = [] 

2111 

2112 if selectable is not None: 

2113 tables = set( 

2114 sql_util.find_tables(selectable, include_aliases=True) 

2115 ) 

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

2117 return mappers 

2118 

2119 def _selectable_from_mappers(self, mappers, innerjoin): 

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

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

2122 mapped tables. 

2123 

2124 """ 

2125 from_obj = self.persist_selectable 

2126 for m in mappers: 

2127 if m is self: 

2128 continue 

2129 if m.concrete: 

2130 raise sa_exc.InvalidRequestError( 

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

2132 "when concrete-inheriting mappers are used." 

2133 ) 

2134 elif not m.single: 

2135 if innerjoin: 

2136 from_obj = from_obj.join( 

2137 m.local_table, m.inherit_condition 

2138 ) 

2139 else: 

2140 from_obj = from_obj.outerjoin( 

2141 m.local_table, m.inherit_condition 

2142 ) 

2143 

2144 return from_obj 

2145 

2146 @_memoized_configured_property 

2147 def _single_table_criterion(self): 

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

2149 return self.polymorphic_on._annotate({"parentmapper": self}).in_( 

2150 m.polymorphic_identity for m in self.self_and_descendants 

2151 ) 

2152 else: 

2153 return None 

2154 

2155 @_memoized_configured_property 

2156 def _with_polymorphic_mappers(self): 

2157 if Mapper._new_mappers: 

2158 configure_mappers() 

2159 if not self.with_polymorphic: 

2160 return [] 

2161 return self._mappers_from_spec(*self.with_polymorphic) 

2162 

2163 @_memoized_configured_property 

2164 def _with_polymorphic_selectable(self): 

2165 if not self.with_polymorphic: 

2166 return self.persist_selectable 

2167 

2168 spec, selectable = self.with_polymorphic 

2169 if selectable is not None: 

2170 return selectable 

2171 else: 

2172 return self._selectable_from_mappers( 

2173 self._mappers_from_spec(spec, selectable), False 

2174 ) 

2175 

2176 with_polymorphic_mappers = _with_polymorphic_mappers 

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

2178 default "polymorphic" query. 

2179 

2180 """ 

2181 

2182 @_memoized_configured_property 

2183 def _insert_cols_evaluating_none(self): 

2184 return dict( 

2185 ( 

2186 table, 

2187 frozenset( 

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

2189 ), 

2190 ) 

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

2192 ) 

2193 

2194 @_memoized_configured_property 

2195 def _insert_cols_as_none(self): 

2196 return dict( 

2197 ( 

2198 table, 

2199 frozenset( 

2200 col.key 

2201 for col in columns 

2202 if not col.primary_key 

2203 and not col.server_default 

2204 and not col.default 

2205 and not col.type.should_evaluate_none 

2206 ), 

2207 ) 

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

2209 ) 

2210 

2211 @_memoized_configured_property 

2212 def _propkey_to_col(self): 

2213 return dict( 

2214 ( 

2215 table, 

2216 dict( 

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

2218 ), 

2219 ) 

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

2221 ) 

2222 

2223 @_memoized_configured_property 

2224 def _pk_keys_by_table(self): 

2225 return dict( 

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

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

2228 ) 

2229 

2230 @_memoized_configured_property 

2231 def _pk_attr_keys_by_table(self): 

2232 return dict( 

2233 ( 

2234 table, 

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

2236 ) 

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

2238 ) 

2239 

2240 @_memoized_configured_property 

2241 def _server_default_cols(self): 

2242 return dict( 

2243 ( 

2244 table, 

2245 frozenset( 

2246 [ 

2247 col.key 

2248 for col in columns 

2249 if col.server_default is not None 

2250 ] 

2251 ), 

2252 ) 

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

2254 ) 

2255 

2256 @_memoized_configured_property 

2257 def _server_default_plus_onupdate_propkeys(self): 

2258 result = set() 

2259 

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

2261 for col in columns: 

2262 if ( 

2263 col.server_default is not None 

2264 or col.server_onupdate is not None 

2265 ) and col in self._columntoproperty: 

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

2267 

2268 return result 

2269 

2270 @_memoized_configured_property 

2271 def _server_onupdate_default_cols(self): 

2272 return dict( 

2273 ( 

2274 table, 

2275 frozenset( 

2276 [ 

2277 col.key 

2278 for col in columns 

2279 if col.server_onupdate is not None 

2280 ] 

2281 ), 

2282 ) 

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

2284 ) 

2285 

2286 @property 

2287 def selectable(self): 

2288 """The :func:`_expression.select` construct this :class:`_orm.Mapper` 

2289 selects from 

2290 by default. 

2291 

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

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

2294 full "polymorphic" selectable is returned. 

2295 

2296 """ 

2297 return self._with_polymorphic_selectable 

2298 

2299 def _with_polymorphic_args( 

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

2301 ): 

2302 if self.with_polymorphic: 

2303 if not spec: 

2304 spec = self.with_polymorphic[0] 

2305 if selectable is False: 

2306 selectable = self.with_polymorphic[1] 

2307 elif selectable is False: 

2308 selectable = None 

2309 mappers = self._mappers_from_spec(spec, selectable) 

2310 if selectable is not None: 

2311 return mappers, selectable 

2312 else: 

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

2314 

2315 @_memoized_configured_property 

2316 def _polymorphic_properties(self): 

2317 return list( 

2318 self._iterate_polymorphic_properties( 

2319 self._with_polymorphic_mappers 

2320 ) 

2321 ) 

2322 

2323 def _iterate_polymorphic_properties(self, mappers=None): 

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

2325 a SELECT.""" 

2326 if mappers is None: 

2327 mappers = self._with_polymorphic_mappers 

2328 

2329 if not mappers: 

2330 for c in self.iterate_properties: 

2331 yield c 

2332 else: 

2333 # in the polymorphic case, filter out discriminator columns 

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

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

2336 for c in util.unique_list( 

2337 chain( 

2338 *[ 

2339 list(mapper.iterate_properties) 

2340 for mapper in [self] + mappers 

2341 ] 

2342 ) 

2343 ): 

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

2345 self.polymorphic_on is None 

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

2347 ): 

2348 continue 

2349 yield c 

2350 

2351 @_memoized_configured_property 

2352 def attrs(self): 

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

2354 associated this mapper. 

2355 

2356 This is an object that provides each property based on 

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

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

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

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

2361 column. The namespace object can also be iterated, 

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

2363 

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

2365 of this attribute which limit the types of properties 

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

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

2368 

2369 .. warning:: 

2370 

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

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

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

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

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

2376 accessing attributes dynamically, favor using the dict-access 

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

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

2379 

2380 .. seealso:: 

2381 

2382 :attr:`_orm.Mapper.all_orm_descriptors` 

2383 

2384 """ 

2385 if Mapper._new_mappers: 

2386 configure_mappers() 

2387 return util.ImmutableProperties(self._props) 

2388 

2389 @_memoized_configured_property 

2390 def all_orm_descriptors(self): 

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

2392 with the mapped class. 

2393 

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

2395 associated with the mapped class or its superclasses. 

2396 

2397 This namespace includes attributes that are mapped to the class 

2398 as well as attributes declared by extension modules. 

2399 It includes any Python descriptor type that inherits from 

2400 :class:`.InspectionAttr`. This includes 

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

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

2403 :class:`.AssociationProxy`. 

2404 

2405 To distinguish between mapped attributes and extension attributes, 

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

2407 to a constant that distinguishes between different extension types. 

2408 

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

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

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

2412 referring to the collection of mapped properties via 

2413 :attr:`_orm.Mapper.attrs`. 

2414 

2415 .. warning:: 

2416 

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

2418 accessor namespace is an 

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

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

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

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

2423 accessing attributes dynamically, favor using the dict-access 

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

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

2426 collisions. 

2427 

2428 .. seealso:: 

2429 

2430 :attr:`_orm.Mapper.attrs` 

2431 

2432 """ 

2433 return util.ImmutableProperties( 

2434 dict(self.class_manager._all_sqla_attributes()) 

2435 ) 

2436 

2437 @_memoized_configured_property 

2438 def synonyms(self): 

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

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

2441 

2442 .. seealso:: 

2443 

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

2445 :class:`.MapperProperty` 

2446 objects. 

2447 

2448 """ 

2449 return self._filter_properties(properties.SynonymProperty) 

2450 

2451 @_memoized_configured_property 

2452 def column_attrs(self): 

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

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

2455 

2456 .. seealso:: 

2457 

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

2459 :class:`.MapperProperty` 

2460 objects. 

2461 

2462 """ 

2463 return self._filter_properties(properties.ColumnProperty) 

2464 

2465 @_memoized_configured_property 

2466 def relationships(self): 

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

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

2469 

2470 .. warning:: 

2471 

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

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

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

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

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

2477 accessing attributes dynamically, favor using the dict-access 

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

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

2480 collisions. 

2481 

2482 .. seealso:: 

2483 

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

2485 :class:`.MapperProperty` 

2486 objects. 

2487 

2488 """ 

2489 return self._filter_properties(properties.RelationshipProperty) 

2490 

2491 @_memoized_configured_property 

2492 def composites(self): 

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

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

2495 

2496 .. seealso:: 

2497 

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

2499 :class:`.MapperProperty` 

2500 objects. 

2501 

2502 """ 

2503 return self._filter_properties(properties.CompositeProperty) 

2504 

2505 def _filter_properties(self, type_): 

2506 if Mapper._new_mappers: 

2507 configure_mappers() 

2508 return util.ImmutableProperties( 

2509 util.OrderedDict( 

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

2511 ) 

2512 ) 

2513 

2514 @_memoized_configured_property 

2515 def _get_clause(self): 

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

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

2518 by primary key. 

2519 

2520 """ 

2521 params = [ 

2522 (primary_key, sql.bindparam(None, type_=primary_key.type)) 

2523 for primary_key in self.primary_key 

2524 ] 

2525 return ( 

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

2527 util.column_dict(params), 

2528 ) 

2529 

2530 @_memoized_configured_property 

2531 def _equivalent_columns(self): 

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

2533 the determination of column pairs that are equated to 

2534 one another based on inherit condition. This is designed 

2535 to work with the queries that util.polymorphic_union 

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

2537 the base table directly (including the subclass table columns 

2538 only). 

2539 

2540 The resulting structure is a dictionary of columns mapped 

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

2542 

2543 { 

2544 tablea.col1: 

2545 {tableb.col1, tablec.col1}, 

2546 tablea.col2: 

2547 {tabled.col2} 

2548 } 

2549 

2550 """ 

2551 result = util.column_dict() 

2552 

2553 def visit_binary(binary): 

2554 if binary.operator == operators.eq: 

2555 if binary.left in result: 

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

2557 else: 

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

2559 if binary.right in result: 

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

2561 else: 

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

2563 

2564 for mapper in self.base_mapper.self_and_descendants: 

2565 if mapper.inherit_condition is not None: 

2566 visitors.traverse( 

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

2568 ) 

2569 

2570 return result 

2571 

2572 def _is_userland_descriptor(self, obj): 

2573 if isinstance( 

2574 obj, 

2575 ( 

2576 _MappedAttribute, 

2577 instrumentation.ClassManager, 

2578 expression.ColumnElement, 

2579 ), 

2580 ): 

2581 return False 

2582 else: 

2583 return True 

2584 

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

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

2587 present on the class. 

2588 

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

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

2591 

2592 """ 

2593 

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

2595 # either local or from an inherited class 

2596 if local: 

2597 if self.class_.__dict__.get( 

2598 assigned_name, None 

2599 ) is not None and self._is_userland_descriptor( 

2600 self.class_.__dict__[assigned_name] 

2601 ): 

2602 return True 

2603 else: 

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

2605 if attr is not None and self._is_userland_descriptor(attr): 

2606 return True 

2607 

2608 if ( 

2609 self.include_properties is not None 

2610 and name not in self.include_properties 

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

2612 ): 

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

2614 return True 

2615 

2616 if self.exclude_properties is not None and ( 

2617 name in self.exclude_properties 

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

2619 ): 

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

2621 return True 

2622 

2623 return False 

2624 

2625 def common_parent(self, other): 

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

2627 common inherited parent as this mapper.""" 

2628 

2629 return self.base_mapper is other.base_mapper 

2630 

2631 def _canload(self, state, allow_subtypes): 

2632 s = self.primary_mapper() 

2633 if self.polymorphic_on is not None or allow_subtypes: 

2634 return _state_mapper(state).isa(s) 

2635 else: 

2636 return _state_mapper(state) is s 

2637 

2638 def isa(self, other): 

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

2640 

2641 m = self 

2642 while m and m is not other: 

2643 m = m.inherits 

2644 return bool(m) 

2645 

2646 def iterate_to_root(self): 

2647 m = self 

2648 while m: 

2649 yield m 

2650 m = m.inherits 

2651 

2652 @_memoized_configured_property 

2653 def self_and_descendants(self): 

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

2655 

2656 This includes not just the immediately inheriting mappers but 

2657 all their inheriting mappers as well. 

2658 

2659 """ 

2660 descendants = [] 

2661 stack = deque([self]) 

2662 while stack: 

2663 item = stack.popleft() 

2664 descendants.append(item) 

2665 stack.extend(item._inheriting_mappers) 

2666 return util.WeakSequence(descendants) 

2667 

2668 def polymorphic_iterator(self): 

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

2670 all descendant mappers. 

2671 

2672 This includes not just the immediately inheriting mappers but 

2673 all their inheriting mappers as well. 

2674 

2675 To iterate through an entire hierarchy, use 

2676 ``mapper.base_mapper.polymorphic_iterator()``. 

2677 

2678 """ 

2679 return iter(self.self_and_descendants) 

2680 

2681 def primary_mapper(self): 

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

2683 (class).""" 

2684 

2685 return self.class_manager.mapper 

2686 

2687 @property 

2688 def primary_base_mapper(self): 

2689 return self.class_manager.mapper.base_mapper 

2690 

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

2692 pk_cols = self.primary_key 

2693 if adapter: 

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

2695 for col in pk_cols: 

2696 if not result._has_key(col): 

2697 return False 

2698 else: 

2699 return True 

2700 

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

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

2703 item from the identity map. 

2704 

2705 :param row: A :class:`.RowProxy` instance. The columns which are 

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

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

2708 object directly (as is the case 

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

2710 or via string names of 

2711 the form ``<tablename>_<colname>``. 

2712 

2713 """ 

2714 pk_cols = self.primary_key 

2715 if adapter: 

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

2717 

2718 return ( 

2719 self._identity_class, 

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

2721 identity_token, 

2722 ) 

2723 

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

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

2726 item from an identity map. 

2727 

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

2729 

2730 """ 

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

2732 

2733 def identity_key_from_instance(self, instance): 

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

2735 its primary key attributes. 

2736 

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

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

2739 If the row no longer exists, 

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

2741 

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

2743 attribute name `key`. 

2744 

2745 """ 

2746 state = attributes.instance_state(instance) 

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

2748 

2749 def _identity_key_from_state( 

2750 self, state, passive=attributes.PASSIVE_RETURN_NEVER_SET 

2751 ): 

2752 dict_ = state.dict 

2753 manager = state.manager 

2754 return ( 

2755 self._identity_class, 

2756 tuple( 

2757 [ 

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

2759 for prop in self._identity_key_props 

2760 ] 

2761 ), 

2762 state.identity_token, 

2763 ) 

2764 

2765 def primary_key_from_instance(self, instance): 

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

2767 instance. 

2768 

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

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

2771 If the row no longer exists, 

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

2773 

2774 """ 

2775 state = attributes.instance_state(instance) 

2776 identity_key = self._identity_key_from_state( 

2777 state, attributes.PASSIVE_OFF 

2778 ) 

2779 return identity_key[1] 

2780 

2781 @_memoized_configured_property 

2782 def _persistent_sortkey_fn(self): 

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

2784 

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

2786 

2787 def key(state): 

2788 return tuple( 

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

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

2791 ) 

2792 

2793 else: 

2794 

2795 def key(state): 

2796 return state.key[1] 

2797 

2798 return key 

2799 

2800 @_memoized_configured_property 

2801 def _identity_key_props(self): 

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

2803 

2804 @_memoized_configured_property 

2805 def _all_pk_props(self): 

2806 collection = set() 

2807 for table in self.tables: 

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

2809 return collection 

2810 

2811 @_memoized_configured_property 

2812 def _should_undefer_in_wildcard(self): 

2813 cols = set(self.primary_key) 

2814 if self.polymorphic_on is not None: 

2815 cols.add(self.polymorphic_on) 

2816 return cols 

2817 

2818 @_memoized_configured_property 

2819 def _primary_key_propkeys(self): 

2820 return {prop.key for prop in self._all_pk_props} 

2821 

2822 def _get_state_attr_by_column( 

2823 self, state, dict_, column, passive=attributes.PASSIVE_RETURN_NEVER_SET 

2824 ): 

2825 prop = self._columntoproperty[column] 

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

2827 

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

2829 prop = self._columntoproperty[column] 

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

2831 

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

2833 prop = self._columntoproperty[column] 

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

2835 

2836 def _get_committed_attr_by_column(self, obj, column): 

2837 state = attributes.instance_state(obj) 

2838 dict_ = attributes.instance_dict(obj) 

2839 return self._get_committed_state_attr_by_column( 

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

2841 ) 

2842 

2843 def _get_committed_state_attr_by_column( 

2844 self, state, dict_, column, passive=attributes.PASSIVE_RETURN_NEVER_SET 

2845 ): 

2846 

2847 prop = self._columntoproperty[column] 

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

2849 state, dict_, passive=passive 

2850 ) 

2851 

2852 def _optimized_get_statement(self, state, attribute_names): 

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

2854 key, using a minimized set of tables. 

2855 

2856 Applies to a joined-table inheritance mapper where the 

2857 requested attribute names are only present on joined tables, 

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

2859 only those tables to minimize joins. 

2860 

2861 """ 

2862 props = self._props 

2863 

2864 tables = set( 

2865 chain( 

2866 *[ 

2867 sql_util.find_tables(c, check_columns=True) 

2868 for key in attribute_names 

2869 for c in props[key].columns 

2870 ] 

2871 ) 

2872 ) 

2873 

2874 if self.base_mapper.local_table in tables: 

2875 return None 

2876 

2877 def visit_binary(binary): 

2878 leftcol = binary.left 

2879 rightcol = binary.right 

2880 if leftcol is None or rightcol is None: 

2881 return 

2882 

2883 if leftcol.table not in tables: 

2884 leftval = self._get_committed_state_attr_by_column( 

2885 state, 

2886 state.dict, 

2887 leftcol, 

2888 passive=attributes.PASSIVE_NO_INITIALIZE, 

2889 ) 

2890 if leftval in orm_util._none_set: 

2891 raise _OptGetColumnsNotAvailable() 

2892 binary.left = sql.bindparam( 

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

2894 ) 

2895 elif rightcol.table not in tables: 

2896 rightval = self._get_committed_state_attr_by_column( 

2897 state, 

2898 state.dict, 

2899 rightcol, 

2900 passive=attributes.PASSIVE_NO_INITIALIZE, 

2901 ) 

2902 if rightval in orm_util._none_set: 

2903 raise _OptGetColumnsNotAvailable() 

2904 binary.right = sql.bindparam( 

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

2906 ) 

2907 

2908 allconds = [] 

2909 

2910 try: 

2911 start = False 

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

2913 if mapper.local_table in tables: 

2914 start = True 

2915 elif not isinstance( 

2916 mapper.local_table, expression.TableClause 

2917 ): 

2918 return None 

2919 if start and not mapper.single: 

2920 allconds.append( 

2921 visitors.cloned_traverse( 

2922 mapper.inherit_condition, 

2923 {}, 

2924 {"binary": visit_binary}, 

2925 ) 

2926 ) 

2927 except _OptGetColumnsNotAvailable: 

2928 return None 

2929 

2930 cond = sql.and_(*allconds) 

2931 

2932 cols = [] 

2933 for key in attribute_names: 

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

2935 return sql.select(cols, cond, use_labels=True) 

2936 

2937 def _iterate_to_target_viawpoly(self, mapper): 

2938 if self.isa(mapper): 

2939 prev = self 

2940 for m in self.iterate_to_root(): 

2941 yield m 

2942 

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

2944 break 

2945 

2946 prev = m 

2947 if m is mapper: 

2948 break 

2949 

2950 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 

2951 if not enabled_via_opt: 

2952 # common case, takes place for all polymorphic loads 

2953 mapper = polymorphic_from 

2954 for m in self._iterate_to_target_viawpoly(mapper): 

2955 if m.polymorphic_load == "selectin": 

2956 return m 

2957 else: 

2958 # uncommon case, selectin load options were used 

2959 enabled_via_opt = set(enabled_via_opt) 

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

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

2962 mapper = entity.mapper 

2963 for m in self._iterate_to_target_viawpoly(mapper): 

2964 if ( 

2965 m.polymorphic_load == "selectin" 

2966 or m in enabled_via_opt_mappers 

2967 ): 

2968 return enabled_via_opt_mappers.get(m, m) 

2969 

2970 return None 

2971 

2972 @util.dependencies( 

2973 "sqlalchemy.ext.baked", "sqlalchemy.orm.strategy_options" 

2974 ) 

2975 def _subclass_load_via_in(self, baked, strategy_options, entity): 

2976 """Assemble a BakedQuery that can load the columns local to 

2977 this subclass as a SELECT with IN. 

2978 

2979 """ 

2980 assert self.inherits 

2981 

2982 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 

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

2984 

2985 disable_opt = strategy_options.Load(entity) 

2986 enable_opt = strategy_options.Load(entity) 

2987 

2988 for prop in self.attrs: 

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

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

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

2992 enable_opt.set_generic_strategy( 

2993 (prop.key,), dict(prop.strategy_key) 

2994 ) 

2995 else: 

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

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

2998 # the options from the query to override them 

2999 disable_opt.set_generic_strategy( 

3000 (prop.key,), {"do_nothing": True} 

3001 ) 

3002 

3003 if len(self.primary_key) > 1: 

3004 in_expr = sql.tuple_(*self.primary_key) 

3005 else: 

3006 in_expr = self.primary_key[0] 

3007 

3008 if entity.is_aliased_class: 

3009 assert entity.mapper is self 

3010 q = baked.BakedQuery( 

3011 self._compiled_cache, 

3012 lambda session: session.query(entity) 

3013 .select_entity_from(entity.selectable) 

3014 ._adapt_all_clauses(), 

3015 (self,), 

3016 ) 

3017 q.spoil() 

3018 else: 

3019 q = baked.BakedQuery( 

3020 self._compiled_cache, 

3021 lambda session: session.query(self), 

3022 (self,), 

3023 ) 

3024 

3025 q += lambda q: q.filter( 

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

3027 ).order_by(*self.primary_key) 

3028 

3029 return q, enable_opt, disable_opt 

3030 

3031 @_memoized_configured_property 

3032 def _subclass_load_via_in_mapper(self): 

3033 return self._subclass_load_via_in(self) 

3034 

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

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

3037 for all relationships that meet the given cascade rule. 

3038 

3039 :param type\_: 

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

3041 etc.). 

3042 

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

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

3045 

3046 :param state: 

3047 The lead InstanceState. child items will be processed per 

3048 the relationships defined for this object's mapper. 

3049 

3050 :return: the method yields individual object instances. 

3051 

3052 .. seealso:: 

3053 

3054 :ref:`unitofwork_cascades` 

3055 

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

3057 traverse all objects without relying on cascades. 

3058 

3059 """ 

3060 visited_states = set() 

3061 prp, mpp = object(), object() 

3062 

3063 assert state.mapper.isa(self) 

3064 

3065 visitables = deque( 

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

3067 ) 

3068 

3069 while visitables: 

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

3071 if not iterator: 

3072 visitables.pop() 

3073 continue 

3074 

3075 if item_type is prp: 

3076 prop = iterator.popleft() 

3077 if type_ not in prop.cascade: 

3078 continue 

3079 queue = deque( 

3080 prop.cascade_iterator( 

3081 type_, 

3082 parent_state, 

3083 parent_dict, 

3084 visited_states, 

3085 halt_on, 

3086 ) 

3087 ) 

3088 if queue: 

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

3090 elif item_type is mpp: 

3091 ( 

3092 instance, 

3093 instance_mapper, 

3094 corresponding_state, 

3095 corresponding_dict, 

3096 ) = iterator.popleft() 

3097 yield ( 

3098 instance, 

3099 instance_mapper, 

3100 corresponding_state, 

3101 corresponding_dict, 

3102 ) 

3103 visitables.append( 

3104 ( 

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

3106 prp, 

3107 corresponding_state, 

3108 corresponding_dict, 

3109 ) 

3110 ) 

3111 

3112 @_memoized_configured_property 

3113 def _compiled_cache(self): 

3114 return util.LRUCache(self._compiled_cache_size) 

3115 

3116 @_memoized_configured_property 

3117 def _sorted_tables(self): 

3118 table_to_mapper = {} 

3119 

3120 for mapper in self.base_mapper.self_and_descendants: 

3121 for t in mapper.tables: 

3122 table_to_mapper.setdefault(t, mapper) 

3123 

3124 extra_dependencies = [] 

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

3126 super_ = mapper.inherits 

3127 if super_: 

3128 extra_dependencies.extend( 

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

3130 ) 

3131 

3132 def skip(fk): 

3133 # attempt to skip dependencies that are not 

3134 # significant to the inheritance chain 

3135 # for two tables that are related by inheritance. 

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

3137 # not what we mean to sort on here. 

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

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

3140 if ( 

3141 parent is not None 

3142 and dep is not None 

3143 and dep is not parent 

3144 and dep.inherit_condition is not None 

3145 ): 

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

3147 if parent.inherit_condition is not None: 

3148 cols = cols.union( 

3149 sql_util._find_columns(parent.inherit_condition) 

3150 ) 

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

3152 else: 

3153 return fk.parent not in cols 

3154 return False 

3155 

3156 sorted_ = sql_util.sort_tables( 

3157 table_to_mapper, 

3158 skip_fn=skip, 

3159 extra_dependencies=extra_dependencies, 

3160 ) 

3161 

3162 ret = util.OrderedDict() 

3163 for t in sorted_: 

3164 ret[t] = table_to_mapper[t] 

3165 return ret 

3166 

3167 def _memo(self, key, callable_): 

3168 if key in self._memoized_values: 

3169 return self._memoized_values[key] 

3170 else: 

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

3172 return value 

3173 

3174 @util.memoized_property 

3175 def _table_to_equated(self): 

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

3177 synchronized upwards to the base mapper.""" 

3178 

3179 result = util.defaultdict(list) 

3180 

3181 for table in self._sorted_tables: 

3182 cols = set(table.c) 

3183 for m in self.iterate_to_root(): 

3184 if m._inherits_equated_pairs and cols.intersection( 

3185 util.reduce( 

3186 set.union, 

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

3188 ) 

3189 ): 

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

3191 

3192 return result 

3193 

3194 

3195class _OptGetColumnsNotAvailable(Exception): 

3196 pass 

3197 

3198 

3199def configure_mappers(): 

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

3201 have been constructed thus far. 

3202 

3203 This function can be called any number of times, but in 

3204 most cases is invoked automatically, the first time mappings are used, 

3205 as well as whenever mappings are used and additional not-yet-configured 

3206 mappers have been constructed. 

3207 

3208 Points at which this occur include when a mapped class is instantiated 

3209 into an instance, as well as when the :meth:`.Session.query` method 

3210 is used. 

3211 

3212 The :func:`.configure_mappers` function provides several event hooks 

3213 that can be used to augment its functionality. These methods include: 

3214 

3215 * :meth:`.MapperEvents.before_configured` - called once before 

3216 :func:`.configure_mappers` does any work; this can be used to establish 

3217 additional options, properties, or related mappings before the operation 

3218 proceeds. 

3219 

3220 * :meth:`.MapperEvents.mapper_configured` - called as each individual 

3221 :class:`_orm.Mapper` is configured within the process; will include all 

3222 mapper state except for backrefs set up by other mappers that are still 

3223 to be configured. 

3224 

3225 * :meth:`.MapperEvents.after_configured` - called once after 

3226 :func:`.configure_mappers` is complete; at this stage, all 

3227 :class:`_orm.Mapper` objects that are known to SQLAlchemy will be fully 

3228 configured. Note that the calling application may still have other 

3229 mappings that haven't been produced yet, such as if they are in modules 

3230 as yet unimported. 

3231 

3232 """ 

3233 

3234 if not Mapper._new_mappers: 

3235 return 

3236 

3237 _CONFIGURE_MUTEX.acquire() 

3238 try: 

3239 global _already_compiling 

3240 if _already_compiling: 

3241 return 

3242 _already_compiling = True 

3243 try: 

3244 

3245 # double-check inside mutex 

3246 if not Mapper._new_mappers: 

3247 return 

3248 

3249 has_skip = False 

3250 

3251 Mapper.dispatch._for_class(Mapper).before_configured() 

3252 # initialize properties on all mappers 

3253 # note that _mapper_registry is unordered, which 

3254 # may randomly conceal/reveal issues related to 

3255 # the order of mapper compilation 

3256 

3257 for mapper in list(_mapper_registry): 

3258 run_configure = None 

3259 for fn in mapper.dispatch.before_mapper_configured: 

3260 run_configure = fn(mapper, mapper.class_) 

3261 if run_configure is EXT_SKIP: 

3262 has_skip = True 

3263 break 

3264 if run_configure is EXT_SKIP: 

3265 continue 

3266 

3267 if getattr(mapper, "_configure_failed", False): 

3268 e = sa_exc.InvalidRequestError( 

3269 "One or more mappers failed to initialize - " 

3270 "can't proceed with initialization of other " 

3271 "mappers. Triggering mapper: '%s'. " 

3272 "Original exception was: %s" 

3273 % (mapper, mapper._configure_failed) 

3274 ) 

3275 e._configure_failed = mapper._configure_failed 

3276 raise e 

3277 

3278 if not mapper.configured: 

3279 try: 

3280 mapper._post_configure_properties() 

3281 mapper._expire_memoizations() 

3282 mapper.dispatch.mapper_configured( 

3283 mapper, mapper.class_ 

3284 ) 

3285 except Exception: 

3286 exc = sys.exc_info()[1] 

3287 if not hasattr(exc, "_configure_failed"): 

3288 mapper._configure_failed = exc 

3289 raise 

3290 

3291 if not has_skip: 

3292 Mapper._new_mappers = False 

3293 finally: 

3294 _already_compiling = False 

3295 finally: 

3296 _CONFIGURE_MUTEX.release() 

3297 Mapper.dispatch._for_class(Mapper).after_configured() 

3298 

3299 

3300def reconstructor(fn): 

3301 """Decorate a method as the 'reconstructor' hook. 

3302 

3303 Designates a method as the "reconstructor", an ``__init__``-like 

3304 method that will be called by the ORM after the instance has been 

3305 loaded from the database or otherwise reconstituted. 

3306 

3307 The reconstructor will be invoked with no arguments. Scalar 

3308 (non-collection) database-mapped attributes of the instance will 

3309 be available for use within the function. Eagerly-loaded 

3310 collections are generally not yet available and will usually only 

3311 contain the first element. ORM state changes made to objects at 

3312 this stage will not be recorded for the next flush() operation, so 

3313 the activity within a reconstructor should be conservative. 

3314 

3315 .. seealso:: 

3316 

3317 :ref:`mapping_constructors` 

3318 

3319 :meth:`.InstanceEvents.load` 

3320 

3321 """ 

3322 fn.__sa_reconstructor__ = True 

3323 return fn 

3324 

3325 

3326def validates(*names, **kw): 

3327 r"""Decorate a method as a 'validator' for one or more named properties. 

3328 

3329 Designates a method as a validator, a method which receives the 

3330 name of the attribute as well as a value to be assigned, or in the 

3331 case of a collection, the value to be added to the collection. 

3332 The function can then raise validation exceptions to halt the 

3333 process from continuing (where Python's built-in ``ValueError`` 

3334 and ``AssertionError`` exceptions are reasonable choices), or can 

3335 modify or replace the value before proceeding. The function should 

3336 otherwise return the given value. 

3337 

3338 Note that a validator for a collection **cannot** issue a load of that 

3339 collection within the validation routine - this usage raises 

3340 an assertion to avoid recursion overflows. This is a reentrant 

3341 condition which is not supported. 

3342 

3343 :param \*names: list of attribute names to be validated. 

3344 :param include_removes: if True, "remove" events will be 

3345 sent as well - the validation function must accept an additional 

3346 argument "is_remove" which will be a boolean. 

3347 

3348 :param include_backrefs: defaults to ``True``; if ``False``, the 

3349 validation function will not emit if the originator is an attribute 

3350 event related via a backref. This can be used for bi-directional 

3351 :func:`.validates` usage where only one validator should emit per 

3352 attribute operation. 

3353 

3354 .. versionadded:: 0.9.0 

3355 

3356 .. seealso:: 

3357 

3358 :ref:`simple_validators` - usage examples for :func:`.validates` 

3359 

3360 """ 

3361 include_removes = kw.pop("include_removes", False) 

3362 include_backrefs = kw.pop("include_backrefs", True) 

3363 

3364 def wrap(fn): 

3365 fn.__sa_validators__ = names 

3366 fn.__sa_validation_opts__ = { 

3367 "include_removes": include_removes, 

3368 "include_backrefs": include_backrefs, 

3369 } 

3370 return fn 

3371 

3372 return wrap 

3373 

3374 

3375def _event_on_load(state, ctx): 

3376 instrumenting_mapper = state.manager.info[_INSTRUMENTOR] 

3377 if instrumenting_mapper._reconstructor: 

3378 instrumenting_mapper._reconstructor(state.obj()) 

3379 

3380 

3381def _event_on_first_init(manager, cls): 

3382 """Initial mapper compilation trigger. 

3383 

3384 instrumentation calls this one when InstanceState 

3385 is first generated, and is needed for legacy mutable 

3386 attributes to work. 

3387 """ 

3388 

3389 instrumenting_mapper = manager.info.get(_INSTRUMENTOR) 

3390 if instrumenting_mapper: 

3391 if Mapper._new_mappers: 

3392 configure_mappers() 

3393 

3394 

3395def _event_on_init(state, args, kwargs): 

3396 """Run init_instance hooks. 

3397 

3398 This also includes mapper compilation, normally not needed 

3399 here but helps with some piecemeal configuration 

3400 scenarios (such as in the ORM tutorial). 

3401 

3402 """ 

3403 

3404 instrumenting_mapper = state.manager.info.get(_INSTRUMENTOR) 

3405 if instrumenting_mapper: 

3406 if Mapper._new_mappers: 

3407 configure_mappers() 

3408 if instrumenting_mapper._set_polymorphic_identity: 

3409 instrumenting_mapper._set_polymorphic_identity(state) 

3410 

3411 

3412class _ColumnMapping(dict): 

3413 """Error reporting helper for mapper._columntoproperty.""" 

3414 

3415 __slots__ = ("mapper",) 

3416 

3417 def __init__(self, mapper): 

3418 self.mapper = mapper 

3419 

3420 def __missing__(self, column): 

3421 prop = self.mapper._props.get(column) 

3422 if prop: 

3423 raise orm_exc.UnmappedColumnError( 

3424 "Column '%s.%s' is not available, due to " 

3425 "conflicting property '%s':%r" 

3426 % (column.table.name, column.name, column.key, prop) 

3427 ) 

3428 raise orm_exc.UnmappedColumnError( 

3429 "No column %s is configured on mapper %s..." 

3430 % (column, self.mapper) 

3431 )