1# orm/mapper.py 2# Copyright (C) 2005-2021 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 orphan 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`, 784 :class:`_expression.Join`, or :class:`_expression.Alias`. 785 786 The :attr:`_orm.Mapper.persist_selectable` is separate from 787 :attr:`_orm.Mapper.selectable` in that the former represents columns 788 that are mapped on this class or its superclasses, whereas the 789 latter may be a "polymorphic" selectable that contains additional columns 790 which are in fact mapped on subclasses only. 791 792 "persist selectable" is the "thing the mapper writes to" and 793 "selectable" is the "thing the mapper selects from". 794 795 :attr:`_orm.Mapper.persist_selectable` is also separate from 796 :attr:`_orm.Mapper.local_table`, which represents the set of columns that 797 are locally mapped on this class directly. 798 799 800 .. seealso:: 801 802 :attr:`_orm.Mapper.selectable`. 803 804 :attr:`_orm.Mapper.local_table`. 805 806 """ 807 808 inherits = None 809 """References the :class:`_orm.Mapper` which this :class:`_orm.Mapper` 810 inherits from, if any. 811 812 This is a *read only* attribute determined during mapper construction. 813 Behavior is undefined if directly modified. 814 815 """ 816 817 configured = None 818 """Represent ``True`` if this :class:`_orm.Mapper` has been configured. 819 820 This is a *read only* attribute determined during mapper construction. 821 Behavior is undefined if directly modified. 822 823 .. seealso:: 824 825 :func:`.configure_mappers`. 826 827 """ 828 829 concrete = None 830 """Represent ``True`` if this :class:`_orm.Mapper` is a concrete 831 inheritance mapper. 832 833 This is a *read only* attribute determined during mapper construction. 834 Behavior is undefined if directly modified. 835 836 """ 837 838 tables = None 839 """An iterable containing the collection of :class:`_schema.Table` objects 840 which this :class:`_orm.Mapper` is aware of. 841 842 If the mapper is mapped to a :class:`_expression.Join`, or an 843 :class:`_expression.Alias` 844 representing a :class:`_expression.Select`, the individual 845 :class:`_schema.Table` 846 objects that comprise the full construct will be represented here. 847 848 This is a *read only* attribute determined during mapper construction. 849 Behavior is undefined if directly modified. 850 851 """ 852 853 primary_key = None 854 """An iterable containing the collection of :class:`_schema.Column` 855 objects 856 which comprise the 'primary key' of the mapped table, from the 857 perspective of this :class:`_orm.Mapper`. 858 859 This list is against the selectable in 860 :attr:`_orm.Mapper.persist_selectable`. 861 In the case of inheriting mappers, some columns may be managed by a 862 superclass mapper. For example, in the case of a 863 :class:`_expression.Join`, the 864 primary key is determined by all of the primary key columns across all 865 tables referenced by the :class:`_expression.Join`. 866 867 The list is also not necessarily the same as the primary key column 868 collection associated with the underlying tables; the :class:`_orm.Mapper` 869 features a ``primary_key`` argument that can override what the 870 :class:`_orm.Mapper` considers as primary key columns. 871 872 This is a *read only* attribute determined during mapper construction. 873 Behavior is undefined if directly modified. 874 875 """ 876 877 class_ = None 878 """The Python class which this :class:`_orm.Mapper` maps. 879 880 This is a *read only* attribute determined during mapper construction. 881 Behavior is undefined if directly modified. 882 883 """ 884 885 class_manager = None 886 """The :class:`.ClassManager` which maintains event listeners 887 and class-bound descriptors for this :class:`_orm.Mapper`. 888 889 This is a *read only* attribute determined during mapper construction. 890 Behavior is undefined if directly modified. 891 892 """ 893 894 single = None 895 """Represent ``True`` if this :class:`_orm.Mapper` is a single table 896 inheritance mapper. 897 898 :attr:`_orm.Mapper.local_table` will be ``None`` if this flag is set. 899 900 This is a *read only* attribute determined during mapper construction. 901 Behavior is undefined if directly modified. 902 903 """ 904 905 non_primary = None 906 """Represent ``True`` if this :class:`_orm.Mapper` is a "non-primary" 907 mapper, e.g. a mapper that is used only to select rows but not for 908 persistence management. 909 910 This is a *read only* attribute determined during mapper construction. 911 Behavior is undefined if directly modified. 912 913 """ 914 915 polymorphic_on = None 916 """The :class:`_schema.Column` or SQL expression specified as the 917 ``polymorphic_on`` argument 918 for this :class:`_orm.Mapper`, within an inheritance scenario. 919 920 This attribute is normally a :class:`_schema.Column` instance but 921 may also be an expression, such as one derived from 922 :func:`.cast`. 923 924 This is a *read only* attribute determined during mapper construction. 925 Behavior is undefined if directly modified. 926 927 """ 928 929 polymorphic_map = None 930 """A mapping of "polymorphic identity" identifiers mapped to 931 :class:`_orm.Mapper` instances, within an inheritance scenario. 932 933 The identifiers can be of any type which is comparable to the 934 type of column represented by :attr:`_orm.Mapper.polymorphic_on`. 935 936 An inheritance chain of mappers will all reference the same 937 polymorphic map object. The object is used to correlate incoming 938 result rows to target mappers. 939 940 This is a *read only* attribute determined during mapper construction. 941 Behavior is undefined if directly modified. 942 943 """ 944 945 polymorphic_identity = None 946 """Represent an identifier which is matched against the 947 :attr:`_orm.Mapper.polymorphic_on` column during result row loading. 948 949 Used only with inheritance, this object can be of any type which is 950 comparable to the type of column represented by 951 :attr:`_orm.Mapper.polymorphic_on`. 952 953 This is a *read only* attribute determined during mapper construction. 954 Behavior is undefined if directly modified. 955 956 """ 957 958 base_mapper = None 959 """The base-most :class:`_orm.Mapper` in an inheritance chain. 960 961 In a non-inheriting scenario, this attribute will always be this 962 :class:`_orm.Mapper`. In an inheritance scenario, it references 963 the :class:`_orm.Mapper` which is parent to all other :class:`_orm.Mapper` 964 objects in the inheritance chain. 965 966 This is a *read only* attribute determined during mapper construction. 967 Behavior is undefined if directly modified. 968 969 """ 970 971 columns = None 972 """A collection of :class:`_schema.Column` or other scalar expression 973 objects maintained by this :class:`_orm.Mapper`. 974 975 The collection behaves the same as that of the ``c`` attribute on 976 any :class:`_schema.Table` object, 977 except that only those columns included in 978 this mapping are present, and are keyed based on the attribute name 979 defined in the mapping, not necessarily the ``key`` attribute of the 980 :class:`_schema.Column` itself. Additionally, scalar expressions mapped 981 by :func:`.column_property` are also present here. 982 983 This is a *read only* attribute determined during mapper construction. 984 Behavior is undefined if directly modified. 985 986 """ 987 988 validators = None 989 """An immutable dictionary of attributes which have been decorated 990 using the :func:`_orm.validates` decorator. 991 992 The dictionary contains string attribute names as keys 993 mapped to the actual validation method. 994 995 """ 996 997 c = None 998 """A synonym for :attr:`_orm.Mapper.columns`.""" 999 1000 @property 1001 @util.deprecated("1.3", "Use .persist_selectable") 1002 def mapped_table(self): 1003 return self.persist_selectable 1004 1005 @util.memoized_property 1006 def _path_registry(self): 1007 return PathRegistry.per_mapper(self) 1008 1009 def _configure_inheritance(self): 1010 """Configure settings related to inheriting and/or inherited mappers 1011 being present.""" 1012 1013 # a set of all mappers which inherit from this one. 1014 self._inheriting_mappers = util.WeakSequence() 1015 1016 if self.inherits: 1017 if isinstance(self.inherits, type): 1018 self.inherits = class_mapper(self.inherits, configure=False) 1019 if not issubclass(self.class_, self.inherits.class_): 1020 raise sa_exc.ArgumentError( 1021 "Class '%s' does not inherit from '%s'" 1022 % (self.class_.__name__, self.inherits.class_.__name__) 1023 ) 1024 if self.non_primary != self.inherits.non_primary: 1025 np = not self.non_primary and "primary" or "non-primary" 1026 raise sa_exc.ArgumentError( 1027 "Inheritance of %s mapper for class '%s' is " 1028 "only allowed from a %s mapper" 1029 % (np, self.class_.__name__, np) 1030 ) 1031 # inherit_condition is optional. 1032 if self.local_table is None: 1033 self.local_table = self.inherits.local_table 1034 self.persist_selectable = self.inherits.persist_selectable 1035 self.single = True 1036 elif self.local_table is not self.inherits.local_table: 1037 if self.concrete: 1038 self.persist_selectable = self.local_table 1039 for mapper in self.iterate_to_root(): 1040 if mapper.polymorphic_on is not None: 1041 mapper._requires_row_aliasing = True 1042 else: 1043 if self.inherit_condition is None: 1044 # figure out inherit condition from our table to the 1045 # immediate table of the inherited mapper, not its 1046 # full table which could pull in other stuff we don't 1047 # want (allows test/inheritance.InheritTest4 to pass) 1048 self.inherit_condition = sql_util.join_condition( 1049 self.inherits.local_table, self.local_table 1050 ) 1051 self.persist_selectable = sql.join( 1052 self.inherits.persist_selectable, 1053 self.local_table, 1054 self.inherit_condition, 1055 ) 1056 1057 fks = util.to_set(self.inherit_foreign_keys) 1058 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 1059 self.persist_selectable.onclause, 1060 consider_as_foreign_keys=fks, 1061 ) 1062 else: 1063 self.persist_selectable = self.local_table 1064 1065 if self.polymorphic_identity is not None and not self.concrete: 1066 self._identity_class = self.inherits._identity_class 1067 else: 1068 self._identity_class = self.class_ 1069 1070 if self.version_id_col is None: 1071 self.version_id_col = self.inherits.version_id_col 1072 self.version_id_generator = self.inherits.version_id_generator 1073 elif ( 1074 self.inherits.version_id_col is not None 1075 and self.version_id_col is not self.inherits.version_id_col 1076 ): 1077 util.warn( 1078 "Inheriting version_id_col '%s' does not match inherited " 1079 "version_id_col '%s' and will not automatically populate " 1080 "the inherited versioning column. " 1081 "version_id_col should only be specified on " 1082 "the base-most mapper that includes versioning." 1083 % ( 1084 self.version_id_col.description, 1085 self.inherits.version_id_col.description, 1086 ) 1087 ) 1088 1089 if ( 1090 self.order_by is False 1091 and not self.concrete 1092 and self.inherits.order_by is not False 1093 ): 1094 self.order_by = self.inherits.order_by 1095 1096 self.polymorphic_map = self.inherits.polymorphic_map 1097 self.batch = self.inherits.batch 1098 self.inherits._inheriting_mappers.append(self) 1099 self.base_mapper = self.inherits.base_mapper 1100 self.passive_updates = self.inherits.passive_updates 1101 self.passive_deletes = ( 1102 self.inherits.passive_deletes or self.passive_deletes 1103 ) 1104 self._all_tables = self.inherits._all_tables 1105 1106 if self.polymorphic_identity is not None: 1107 if self.polymorphic_identity in self.polymorphic_map: 1108 util.warn( 1109 "Reassigning polymorphic association for identity %r " 1110 "from %r to %r: Check for duplicate use of %r as " 1111 "value for polymorphic_identity." 1112 % ( 1113 self.polymorphic_identity, 1114 self.polymorphic_map[self.polymorphic_identity], 1115 self, 1116 self.polymorphic_identity, 1117 ) 1118 ) 1119 self.polymorphic_map[self.polymorphic_identity] = self 1120 1121 if self.polymorphic_load and self.concrete: 1122 raise sa_exc.ArgumentError( 1123 "polymorphic_load is not currently supported " 1124 "with concrete table inheritance" 1125 ) 1126 if self.polymorphic_load == "inline": 1127 self.inherits._add_with_polymorphic_subclass(self) 1128 elif self.polymorphic_load == "selectin": 1129 pass 1130 elif self.polymorphic_load is not None: 1131 raise sa_exc.ArgumentError( 1132 "unknown argument for polymorphic_load: %r" 1133 % self.polymorphic_load 1134 ) 1135 1136 else: 1137 self._all_tables = set() 1138 self.base_mapper = self 1139 self.persist_selectable = self.local_table 1140 if self.polymorphic_identity is not None: 1141 self.polymorphic_map[self.polymorphic_identity] = self 1142 self._identity_class = self.class_ 1143 1144 if self.persist_selectable is None: 1145 raise sa_exc.ArgumentError( 1146 "Mapper '%s' does not have a persist_selectable specified." 1147 % self 1148 ) 1149 1150 def _set_with_polymorphic(self, with_polymorphic): 1151 if with_polymorphic == "*": 1152 self.with_polymorphic = ("*", None) 1153 elif isinstance(with_polymorphic, (tuple, list)): 1154 if isinstance( 1155 with_polymorphic[0], util.string_types + (tuple, list) 1156 ): 1157 self.with_polymorphic = with_polymorphic 1158 else: 1159 self.with_polymorphic = (with_polymorphic, None) 1160 elif with_polymorphic is not None: 1161 raise sa_exc.ArgumentError("Invalid setting for with_polymorphic") 1162 else: 1163 self.with_polymorphic = None 1164 1165 if isinstance(self.local_table, expression.SelectBase): 1166 raise sa_exc.InvalidRequestError( 1167 "When mapping against a select() construct, map against " 1168 "an alias() of the construct instead." 1169 "This because several databases don't allow a " 1170 "SELECT from a subquery that does not have an alias." 1171 ) 1172 1173 if self.with_polymorphic and isinstance( 1174 self.with_polymorphic[1], expression.SelectBase 1175 ): 1176 self.with_polymorphic = ( 1177 self.with_polymorphic[0], 1178 self.with_polymorphic[1].alias(), 1179 ) 1180 1181 if self.configured: 1182 self._expire_memoizations() 1183 1184 def _add_with_polymorphic_subclass(self, mapper): 1185 subcl = mapper.class_ 1186 if self.with_polymorphic is None: 1187 self._set_with_polymorphic((subcl,)) 1188 elif self.with_polymorphic[0] != "*": 1189 self._set_with_polymorphic( 1190 (self.with_polymorphic[0] + (subcl,), self.with_polymorphic[1]) 1191 ) 1192 1193 def _set_concrete_base(self, mapper): 1194 """Set the given :class:`_orm.Mapper` as the 'inherits' for this 1195 :class:`_orm.Mapper`, assuming this :class:`_orm.Mapper` is concrete 1196 and does not already have an inherits.""" 1197 1198 assert self.concrete 1199 assert not self.inherits 1200 assert isinstance(mapper, Mapper) 1201 self.inherits = mapper 1202 self.inherits.polymorphic_map.update(self.polymorphic_map) 1203 self.polymorphic_map = self.inherits.polymorphic_map 1204 for mapper in self.iterate_to_root(): 1205 if mapper.polymorphic_on is not None: 1206 mapper._requires_row_aliasing = True 1207 self.batch = self.inherits.batch 1208 for mp in self.self_and_descendants: 1209 mp.base_mapper = self.inherits.base_mapper 1210 self.inherits._inheriting_mappers.append(self) 1211 self.passive_updates = self.inherits.passive_updates 1212 self._all_tables = self.inherits._all_tables 1213 1214 for key, prop in mapper._props.items(): 1215 if key not in self._props and not self._should_exclude( 1216 key, key, local=False, column=None 1217 ): 1218 self._adapt_inherited_property(key, prop, False) 1219 1220 def _set_polymorphic_on(self, polymorphic_on): 1221 self.polymorphic_on = polymorphic_on 1222 self._configure_polymorphic_setter(True) 1223 1224 def _configure_legacy_instrument_class(self): 1225 1226 if self.inherits: 1227 self.dispatch._update(self.inherits.dispatch) 1228 super_extensions = set( 1229 chain( 1230 *[ 1231 m._deprecated_extensions 1232 for m in self.inherits.iterate_to_root() 1233 ] 1234 ) 1235 ) 1236 else: 1237 super_extensions = set() 1238 1239 for ext in self._deprecated_extensions: 1240 if ext not in super_extensions: 1241 ext._adapt_instrument_class(self, ext) 1242 1243 def _configure_listeners(self): 1244 if self.inherits: 1245 super_extensions = set( 1246 chain( 1247 *[ 1248 m._deprecated_extensions 1249 for m in self.inherits.iterate_to_root() 1250 ] 1251 ) 1252 ) 1253 else: 1254 super_extensions = set() 1255 1256 for ext in self._deprecated_extensions: 1257 if ext not in super_extensions: 1258 ext._adapt_listener(self, ext) 1259 1260 def _configure_class_instrumentation(self): 1261 """If this mapper is to be a primary mapper (i.e. the 1262 non_primary flag is not set), associate this Mapper with the 1263 given class and entity name. 1264 1265 Subsequent calls to ``class_mapper()`` for the ``class_`` / ``entity`` 1266 name combination will return this mapper. Also decorate the 1267 `__init__` method on the mapped class to include optional 1268 auto-session attachment logic. 1269 1270 """ 1271 1272 manager = attributes.manager_of_class(self.class_) 1273 1274 if self.non_primary: 1275 if not manager or not manager.is_mapped: 1276 raise sa_exc.InvalidRequestError( 1277 "Class %s has no primary mapper configured. Configure " 1278 "a primary mapper first before setting up a non primary " 1279 "Mapper." % self.class_ 1280 ) 1281 self.class_manager = manager 1282 self._identity_class = manager.mapper._identity_class 1283 _mapper_registry[self] = True 1284 return 1285 1286 if manager is not None: 1287 assert manager.class_ is self.class_ 1288 if manager.is_mapped: 1289 raise sa_exc.ArgumentError( 1290 "Class '%s' already has a primary mapper defined. " 1291 "Use non_primary=True to " 1292 "create a non primary Mapper. clear_mappers() will " 1293 "remove *all* current mappers from all classes." 1294 % self.class_ 1295 ) 1296 # else: 1297 # a ClassManager may already exist as 1298 # ClassManager.instrument_attribute() creates 1299 # new managers for each subclass if they don't yet exist. 1300 1301 _mapper_registry[self] = True 1302 1303 # note: this *must be called before instrumentation.register_class* 1304 # to maintain the documented behavior of instrument_class 1305 self.dispatch.instrument_class(self, self.class_) 1306 1307 if manager is None: 1308 manager = instrumentation.register_class(self.class_) 1309 1310 self.class_manager = manager 1311 1312 manager.mapper = self 1313 manager.deferred_scalar_loader = util.partial( 1314 loading.load_scalar_attributes, self 1315 ) 1316 1317 # The remaining members can be added by any mapper, 1318 # e_name None or not. 1319 if manager.info.get(_INSTRUMENTOR, False): 1320 return 1321 1322 event.listen(manager, "first_init", _event_on_first_init, raw=True) 1323 event.listen(manager, "init", _event_on_init, raw=True) 1324 1325 for key, method in util.iterate_attributes(self.class_): 1326 if key == "__init__" and hasattr(method, "_sa_original_init"): 1327 method = method._sa_original_init 1328 if isinstance(method, types.MethodType): 1329 method = method.im_func 1330 if isinstance(method, types.FunctionType): 1331 if hasattr(method, "__sa_reconstructor__"): 1332 self._reconstructor = method 1333 event.listen(manager, "load", _event_on_load, raw=True) 1334 elif hasattr(method, "__sa_validators__"): 1335 validation_opts = method.__sa_validation_opts__ 1336 for name in method.__sa_validators__: 1337 if name in self.validators: 1338 raise sa_exc.InvalidRequestError( 1339 "A validation function for mapped " 1340 "attribute %r on mapper %s already exists." 1341 % (name, self) 1342 ) 1343 self.validators = self.validators.union( 1344 {name: (method, validation_opts)} 1345 ) 1346 1347 manager.info[_INSTRUMENTOR] = self 1348 1349 @classmethod 1350 def _configure_all(cls): 1351 """Class-level path to the :func:`.configure_mappers` call.""" 1352 configure_mappers() 1353 1354 def dispose(self): 1355 # Disable any attribute-based compilation. 1356 self.configured = True 1357 self._dispose_called = True 1358 1359 if hasattr(self, "_configure_failed"): 1360 del self._configure_failed 1361 1362 if ( 1363 not self.non_primary 1364 and self.class_manager is not None 1365 and self.class_manager.is_mapped 1366 and self.class_manager.mapper is self 1367 ): 1368 instrumentation.unregister_class(self.class_) 1369 1370 def _configure_pks(self): 1371 self.tables = sql_util.find_tables(self.persist_selectable) 1372 1373 self._pks_by_table = {} 1374 self._cols_by_table = {} 1375 1376 all_cols = util.column_set( 1377 chain(*[col.proxy_set for col in self._columntoproperty]) 1378 ) 1379 1380 pk_cols = util.column_set(c for c in all_cols if c.primary_key) 1381 1382 # identify primary key columns which are also mapped by this mapper. 1383 tables = set(self.tables + [self.persist_selectable]) 1384 self._all_tables.update(tables) 1385 for t in tables: 1386 if t.primary_key and pk_cols.issuperset(t.primary_key): 1387 # ordering is important since it determines the ordering of 1388 # mapper.primary_key (and therefore query.get()) 1389 self._pks_by_table[t] = util.ordered_column_set( 1390 t.primary_key 1391 ).intersection(pk_cols) 1392 self._cols_by_table[t] = util.ordered_column_set(t.c).intersection( 1393 all_cols 1394 ) 1395 1396 # if explicit PK argument sent, add those columns to the 1397 # primary key mappings 1398 if self._primary_key_argument: 1399 for k in self._primary_key_argument: 1400 if k.table not in self._pks_by_table: 1401 self._pks_by_table[k.table] = util.OrderedSet() 1402 self._pks_by_table[k.table].add(k) 1403 1404 # otherwise, see that we got a full PK for the mapped table 1405 elif ( 1406 self.persist_selectable not in self._pks_by_table 1407 or len(self._pks_by_table[self.persist_selectable]) == 0 1408 ): 1409 raise sa_exc.ArgumentError( 1410 "Mapper %s could not assemble any primary " 1411 "key columns for mapped table '%s'" 1412 % (self, self.persist_selectable.description) 1413 ) 1414 elif self.local_table not in self._pks_by_table and isinstance( 1415 self.local_table, schema.Table 1416 ): 1417 util.warn( 1418 "Could not assemble any primary " 1419 "keys for locally mapped table '%s' - " 1420 "no rows will be persisted in this Table." 1421 % self.local_table.description 1422 ) 1423 1424 if ( 1425 self.inherits 1426 and not self.concrete 1427 and not self._primary_key_argument 1428 ): 1429 # if inheriting, the "primary key" for this mapper is 1430 # that of the inheriting (unless concrete or explicit) 1431 self.primary_key = self.inherits.primary_key 1432 else: 1433 # determine primary key from argument or persist_selectable pks - 1434 # reduce to the minimal set of columns 1435 if self._primary_key_argument: 1436 primary_key = sql_util.reduce_columns( 1437 [ 1438 self.persist_selectable.corresponding_column(c) 1439 for c in self._primary_key_argument 1440 ], 1441 ignore_nonexistent_tables=True, 1442 ) 1443 else: 1444 primary_key = sql_util.reduce_columns( 1445 self._pks_by_table[self.persist_selectable], 1446 ignore_nonexistent_tables=True, 1447 ) 1448 1449 if len(primary_key) == 0: 1450 raise sa_exc.ArgumentError( 1451 "Mapper %s could not assemble any primary " 1452 "key columns for mapped table '%s'" 1453 % (self, self.persist_selectable.description) 1454 ) 1455 1456 self.primary_key = tuple(primary_key) 1457 self._log("Identified primary key columns: %s", primary_key) 1458 1459 # determine cols that aren't expressed within our tables; mark these 1460 # as "read only" properties which are refreshed upon INSERT/UPDATE 1461 self._readonly_props = set( 1462 self._columntoproperty[col] 1463 for col in self._columntoproperty 1464 if self._columntoproperty[col] not in self._identity_key_props 1465 and ( 1466 not hasattr(col, "table") 1467 or col.table not in self._cols_by_table 1468 ) 1469 ) 1470 1471 def _configure_properties(self): 1472 # Column and other ClauseElement objects which are mapped 1473 self.columns = self.c = util.OrderedProperties() 1474 1475 # object attribute names mapped to MapperProperty objects 1476 self._props = util.OrderedDict() 1477 1478 # table columns mapped to lists of MapperProperty objects 1479 # using a list allows a single column to be defined as 1480 # populating multiple object attributes 1481 self._columntoproperty = _ColumnMapping(self) 1482 1483 # load custom properties 1484 if self._init_properties: 1485 for key, prop in self._init_properties.items(): 1486 self._configure_property(key, prop, False) 1487 1488 # pull properties from the inherited mapper if any. 1489 if self.inherits: 1490 for key, prop in self.inherits._props.items(): 1491 if key not in self._props and not self._should_exclude( 1492 key, key, local=False, column=None 1493 ): 1494 self._adapt_inherited_property(key, prop, False) 1495 1496 # create properties for each column in the mapped table, 1497 # for those columns which don't already map to a property 1498 for column in self.persist_selectable.columns: 1499 if column in self._columntoproperty: 1500 continue 1501 1502 column_key = (self.column_prefix or "") + column.key 1503 1504 if self._should_exclude( 1505 column.key, 1506 column_key, 1507 local=self.local_table.c.contains_column(column), 1508 column=column, 1509 ): 1510 continue 1511 1512 # adjust the "key" used for this column to that 1513 # of the inheriting mapper 1514 for mapper in self.iterate_to_root(): 1515 if column in mapper._columntoproperty: 1516 column_key = mapper._columntoproperty[column].key 1517 1518 self._configure_property( 1519 column_key, column, init=False, setparent=True 1520 ) 1521 1522 def _configure_polymorphic_setter(self, init=False): 1523 """Configure an attribute on the mapper representing the 1524 'polymorphic_on' column, if applicable, and not 1525 already generated by _configure_properties (which is typical). 1526 1527 Also create a setter function which will assign this 1528 attribute to the value of the 'polymorphic_identity' 1529 upon instance construction, also if applicable. This 1530 routine will run when an instance is created. 1531 1532 """ 1533 setter = False 1534 1535 if self.polymorphic_on is not None: 1536 setter = True 1537 1538 if isinstance(self.polymorphic_on, util.string_types): 1539 # polymorphic_on specified as a string - link 1540 # it to mapped ColumnProperty 1541 try: 1542 self.polymorphic_on = self._props[self.polymorphic_on] 1543 except KeyError as err: 1544 util.raise_( 1545 sa_exc.ArgumentError( 1546 "Can't determine polymorphic_on " 1547 "value '%s' - no attribute is " 1548 "mapped to this name." % self.polymorphic_on 1549 ), 1550 replace_context=err, 1551 ) 1552 1553 if self.polymorphic_on in self._columntoproperty: 1554 # polymorphic_on is a column that is already mapped 1555 # to a ColumnProperty 1556 prop = self._columntoproperty[self.polymorphic_on] 1557 elif isinstance(self.polymorphic_on, MapperProperty): 1558 # polymorphic_on is directly a MapperProperty, 1559 # ensure it's a ColumnProperty 1560 if not isinstance( 1561 self.polymorphic_on, properties.ColumnProperty 1562 ): 1563 raise sa_exc.ArgumentError( 1564 "Only direct column-mapped " 1565 "property or SQL expression " 1566 "can be passed for polymorphic_on" 1567 ) 1568 prop = self.polymorphic_on 1569 elif not expression._is_column(self.polymorphic_on): 1570 # polymorphic_on is not a Column and not a ColumnProperty; 1571 # not supported right now. 1572 raise sa_exc.ArgumentError( 1573 "Only direct column-mapped " 1574 "property or SQL expression " 1575 "can be passed for polymorphic_on" 1576 ) 1577 else: 1578 # polymorphic_on is a Column or SQL expression and 1579 # doesn't appear to be mapped. this means it can be 1. 1580 # only present in the with_polymorphic selectable or 1581 # 2. a totally standalone SQL expression which we'd 1582 # hope is compatible with this mapper's persist_selectable 1583 col = self.persist_selectable.corresponding_column( 1584 self.polymorphic_on 1585 ) 1586 if col is None: 1587 # polymorphic_on doesn't derive from any 1588 # column/expression isn't present in the mapped 1589 # table. we will make a "hidden" ColumnProperty 1590 # for it. Just check that if it's directly a 1591 # schema.Column and we have with_polymorphic, it's 1592 # likely a user error if the schema.Column isn't 1593 # represented somehow in either persist_selectable or 1594 # with_polymorphic. Otherwise as of 0.7.4 we 1595 # just go with it and assume the user wants it 1596 # that way (i.e. a CASE statement) 1597 setter = False 1598 instrument = False 1599 col = self.polymorphic_on 1600 if isinstance(col, schema.Column) and ( 1601 self.with_polymorphic is None 1602 or self.with_polymorphic[1].corresponding_column(col) 1603 is None 1604 ): 1605 raise sa_exc.InvalidRequestError( 1606 "Could not map polymorphic_on column " 1607 "'%s' to the mapped table - polymorphic " 1608 "loads will not function properly" 1609 % col.description 1610 ) 1611 else: 1612 # column/expression that polymorphic_on derives from 1613 # is present in our mapped table 1614 # and is probably mapped, but polymorphic_on itself 1615 # is not. This happens when 1616 # the polymorphic_on is only directly present in the 1617 # with_polymorphic selectable, as when use 1618 # polymorphic_union. 1619 # we'll make a separate ColumnProperty for it. 1620 instrument = True 1621 key = getattr(col, "key", None) 1622 if key: 1623 if self._should_exclude(col.key, col.key, False, col): 1624 raise sa_exc.InvalidRequestError( 1625 "Cannot exclude or override the " 1626 "discriminator column %r" % col.key 1627 ) 1628 else: 1629 self.polymorphic_on = col = col.label("_sa_polymorphic_on") 1630 key = col.key 1631 1632 prop = properties.ColumnProperty(col, _instrument=instrument) 1633 self._configure_property(key, prop, init=init, setparent=True) 1634 1635 # the actual polymorphic_on should be the first public-facing 1636 # column in the property 1637 self.polymorphic_on = prop.columns[0] 1638 polymorphic_key = prop.key 1639 1640 else: 1641 # no polymorphic_on was set. 1642 # check inheriting mappers for one. 1643 for mapper in self.iterate_to_root(): 1644 # determine if polymorphic_on of the parent 1645 # should be propagated here. If the col 1646 # is present in our mapped table, or if our mapped 1647 # table is the same as the parent (i.e. single table 1648 # inheritance), we can use it 1649 if mapper.polymorphic_on is not None: 1650 if self.persist_selectable is mapper.persist_selectable: 1651 self.polymorphic_on = mapper.polymorphic_on 1652 else: 1653 self.polymorphic_on = ( 1654 self.persist_selectable 1655 ).corresponding_column(mapper.polymorphic_on) 1656 # we can use the parent mapper's _set_polymorphic_identity 1657 # directly; it ensures the polymorphic_identity of the 1658 # instance's mapper is used so is portable to subclasses. 1659 if self.polymorphic_on is not None: 1660 self._set_polymorphic_identity = ( 1661 mapper._set_polymorphic_identity 1662 ) 1663 self._validate_polymorphic_identity = ( 1664 mapper._validate_polymorphic_identity 1665 ) 1666 else: 1667 self._set_polymorphic_identity = None 1668 return 1669 1670 if setter: 1671 1672 def _set_polymorphic_identity(state): 1673 dict_ = state.dict 1674 state.get_impl(polymorphic_key).set( 1675 state, 1676 dict_, 1677 state.manager.mapper.polymorphic_identity, 1678 None, 1679 ) 1680 1681 def _validate_polymorphic_identity(mapper, state, dict_): 1682 if ( 1683 polymorphic_key in dict_ 1684 and dict_[polymorphic_key] 1685 not in mapper._acceptable_polymorphic_identities 1686 ): 1687 util.warn_limited( 1688 "Flushing object %s with " 1689 "incompatible polymorphic identity %r; the " 1690 "object may not refresh and/or load correctly", 1691 (state_str(state), dict_[polymorphic_key]), 1692 ) 1693 1694 self._set_polymorphic_identity = _set_polymorphic_identity 1695 self._validate_polymorphic_identity = ( 1696 _validate_polymorphic_identity 1697 ) 1698 else: 1699 self._set_polymorphic_identity = None 1700 1701 _validate_polymorphic_identity = None 1702 1703 @_memoized_configured_property 1704 def _version_id_prop(self): 1705 if self.version_id_col is not None: 1706 return self._columntoproperty[self.version_id_col] 1707 else: 1708 return None 1709 1710 @_memoized_configured_property 1711 def _acceptable_polymorphic_identities(self): 1712 identities = set() 1713 1714 stack = deque([self]) 1715 while stack: 1716 item = stack.popleft() 1717 if item.persist_selectable is self.persist_selectable: 1718 identities.add(item.polymorphic_identity) 1719 stack.extend(item._inheriting_mappers) 1720 1721 return identities 1722 1723 @_memoized_configured_property 1724 def _prop_set(self): 1725 return frozenset(self._props.values()) 1726 1727 def _adapt_inherited_property(self, key, prop, init): 1728 if not self.concrete: 1729 self._configure_property(key, prop, init=False, setparent=False) 1730 elif key not in self._props: 1731 # determine if the class implements this attribute; if not, 1732 # or if it is implemented by the attribute that is handling the 1733 # given superclass-mapped property, then we need to report that we 1734 # can't use this at the instance level since we are a concrete 1735 # mapper and we don't map this. don't trip user-defined 1736 # descriptors that might have side effects when invoked. 1737 implementing_attribute = self.class_manager._get_class_attr_mro( 1738 key, prop 1739 ) 1740 if implementing_attribute is prop or ( 1741 isinstance( 1742 implementing_attribute, attributes.InstrumentedAttribute 1743 ) 1744 and implementing_attribute._parententity is prop.parent 1745 ): 1746 self._configure_property( 1747 key, 1748 properties.ConcreteInheritedProperty(), 1749 init=init, 1750 setparent=True, 1751 ) 1752 1753 def _configure_property(self, key, prop, init=True, setparent=True): 1754 self._log("_configure_property(%s, %s)", key, prop.__class__.__name__) 1755 1756 if not isinstance(prop, MapperProperty): 1757 prop = self._property_from_column(key, prop) 1758 1759 if isinstance(prop, properties.ColumnProperty): 1760 col = self.persist_selectable.corresponding_column(prop.columns[0]) 1761 1762 # if the column is not present in the mapped table, 1763 # test if a column has been added after the fact to the 1764 # parent table (or their parent, etc.) [ticket:1570] 1765 if col is None and self.inherits: 1766 path = [self] 1767 for m in self.inherits.iterate_to_root(): 1768 col = m.local_table.corresponding_column(prop.columns[0]) 1769 if col is not None: 1770 for m2 in path: 1771 m2.persist_selectable._reset_exported() 1772 col = self.persist_selectable.corresponding_column( 1773 prop.columns[0] 1774 ) 1775 break 1776 path.append(m) 1777 1778 # subquery expression, column not present in the mapped 1779 # selectable. 1780 if col is None: 1781 col = prop.columns[0] 1782 1783 # column is coming in after _readonly_props was 1784 # initialized; check for 'readonly' 1785 if hasattr(self, "_readonly_props") and ( 1786 not hasattr(col, "table") 1787 or col.table not in self._cols_by_table 1788 ): 1789 self._readonly_props.add(prop) 1790 1791 else: 1792 # if column is coming in after _cols_by_table was 1793 # initialized, ensure the col is in the right set 1794 if ( 1795 hasattr(self, "_cols_by_table") 1796 and col.table in self._cols_by_table 1797 and col not in self._cols_by_table[col.table] 1798 ): 1799 self._cols_by_table[col.table].add(col) 1800 1801 # if this properties.ColumnProperty represents the "polymorphic 1802 # discriminator" column, mark it. We'll need this when rendering 1803 # columns in SELECT statements. 1804 if not hasattr(prop, "_is_polymorphic_discriminator"): 1805 prop._is_polymorphic_discriminator = ( 1806 col is self.polymorphic_on 1807 or prop.columns[0] is self.polymorphic_on 1808 ) 1809 1810 self.columns[key] = col 1811 for col in prop.columns + prop._orig_columns: 1812 for col in col.proxy_set: 1813 self._columntoproperty[col] = prop 1814 1815 prop.key = key 1816 1817 if setparent: 1818 prop.set_parent(self, init) 1819 1820 if key in self._props and getattr( 1821 self._props[key], "_mapped_by_synonym", False 1822 ): 1823 syn = self._props[key]._mapped_by_synonym 1824 raise sa_exc.ArgumentError( 1825 "Can't call map_column=True for synonym %r=%r, " 1826 "a ColumnProperty already exists keyed to the name " 1827 "%r for column %r" % (syn, key, key, syn) 1828 ) 1829 1830 if ( 1831 key in self._props 1832 and not isinstance(prop, properties.ColumnProperty) 1833 and not isinstance( 1834 self._props[key], 1835 ( 1836 properties.ColumnProperty, 1837 properties.ConcreteInheritedProperty, 1838 ), 1839 ) 1840 ): 1841 util.warn( 1842 "Property %s on %s being replaced with new " 1843 "property %s; the old property will be discarded" 1844 % (self._props[key], self, prop) 1845 ) 1846 oldprop = self._props[key] 1847 self._path_registry.pop(oldprop, None) 1848 1849 self._props[key] = prop 1850 1851 if not self.non_primary: 1852 prop.instrument_class(self) 1853 1854 for mapper in self._inheriting_mappers: 1855 mapper._adapt_inherited_property(key, prop, init) 1856 1857 if init: 1858 prop.init() 1859 prop.post_instrument_class(self) 1860 1861 if self.configured: 1862 self._expire_memoizations() 1863 1864 def _property_from_column(self, key, prop): 1865 """generate/update a :class:`.ColumnProprerty` given a 1866 :class:`_schema.Column` object.""" 1867 1868 # we were passed a Column or a list of Columns; 1869 # generate a properties.ColumnProperty 1870 columns = util.to_list(prop) 1871 column = columns[0] 1872 if not expression._is_column(column): 1873 raise sa_exc.ArgumentError( 1874 "%s=%r is not an instance of MapperProperty or Column" 1875 % (key, prop) 1876 ) 1877 1878 prop = self._props.get(key, None) 1879 1880 if isinstance(prop, properties.ColumnProperty): 1881 if ( 1882 ( 1883 not self._inherits_equated_pairs 1884 or (prop.columns[0], column) 1885 not in self._inherits_equated_pairs 1886 ) 1887 and not prop.columns[0].shares_lineage(column) 1888 and prop.columns[0] is not self.version_id_col 1889 and column is not self.version_id_col 1890 ): 1891 warn_only = prop.parent is not self 1892 msg = ( 1893 "Implicitly combining column %s with column " 1894 "%s under attribute '%s'. Please configure one " 1895 "or more attributes for these same-named columns " 1896 "explicitly." % (prop.columns[-1], column, key) 1897 ) 1898 if warn_only: 1899 util.warn(msg) 1900 else: 1901 raise sa_exc.InvalidRequestError(msg) 1902 1903 # existing properties.ColumnProperty from an inheriting 1904 # mapper. make a copy and append our column to it 1905 prop = prop.copy() 1906 prop.columns.insert(0, column) 1907 self._log( 1908 "inserting column to existing list " 1909 "in properties.ColumnProperty %s" % (key) 1910 ) 1911 return prop 1912 elif prop is None or isinstance( 1913 prop, properties.ConcreteInheritedProperty 1914 ): 1915 mapped_column = [] 1916 for c in columns: 1917 mc = self.persist_selectable.corresponding_column(c) 1918 if mc is None: 1919 mc = self.local_table.corresponding_column(c) 1920 if mc is not None: 1921 # if the column is in the local table but not the 1922 # mapped table, this corresponds to adding a 1923 # column after the fact to the local table. 1924 # [ticket:1523] 1925 self.persist_selectable._reset_exported() 1926 mc = self.persist_selectable.corresponding_column(c) 1927 if mc is None: 1928 raise sa_exc.ArgumentError( 1929 "When configuring property '%s' on %s, " 1930 "column '%s' is not represented in the mapper's " 1931 "table. Use the `column_property()` function to " 1932 "force this column to be mapped as a read-only " 1933 "attribute." % (key, self, c) 1934 ) 1935 mapped_column.append(mc) 1936 return properties.ColumnProperty(*mapped_column) 1937 else: 1938 raise sa_exc.ArgumentError( 1939 "WARNING: when configuring property '%s' on %s, " 1940 "column '%s' conflicts with property '%r'. " 1941 "To resolve this, map the column to the class under a " 1942 "different name in the 'properties' dictionary. Or, " 1943 "to remove all awareness of the column entirely " 1944 "(including its availability as a foreign key), " 1945 "use the 'include_properties' or 'exclude_properties' " 1946 "mapper arguments to control specifically which table " 1947 "columns get mapped." % (key, self, column.key, prop) 1948 ) 1949 1950 def _post_configure_properties(self): 1951 """Call the ``init()`` method on all ``MapperProperties`` 1952 attached to this mapper. 1953 1954 This is a deferred configuration step which is intended 1955 to execute once all mappers have been constructed. 1956 1957 """ 1958 1959 self._log("_post_configure_properties() started") 1960 l = [(key, prop) for key, prop in self._props.items()] 1961 for key, prop in l: 1962 self._log("initialize prop %s", key) 1963 1964 if prop.parent is self and not prop._configure_started: 1965 prop.init() 1966 1967 if prop._configure_finished: 1968 prop.post_instrument_class(self) 1969 1970 self._log("_post_configure_properties() complete") 1971 self.configured = True 1972 1973 def add_properties(self, dict_of_properties): 1974 """Add the given dictionary of properties to this mapper, 1975 using `add_property`. 1976 1977 """ 1978 for key, value in dict_of_properties.items(): 1979 self.add_property(key, value) 1980 1981 def add_property(self, key, prop): 1982 """Add an individual MapperProperty to this mapper. 1983 1984 If the mapper has not been configured yet, just adds the 1985 property to the initial properties dictionary sent to the 1986 constructor. If this Mapper has already been configured, then 1987 the given MapperProperty is configured immediately. 1988 1989 """ 1990 self._init_properties[key] = prop 1991 self._configure_property(key, prop, init=self.configured) 1992 1993 def _expire_memoizations(self): 1994 for mapper in self.iterate_to_root(): 1995 _memoized_configured_property.expire_instance(mapper) 1996 1997 @property 1998 def _log_desc(self): 1999 return ( 2000 "(" 2001 + self.class_.__name__ 2002 + "|" 2003 + ( 2004 self.local_table is not None 2005 and self.local_table.description 2006 or str(self.local_table) 2007 ) 2008 + (self.non_primary and "|non-primary" or "") 2009 + ")" 2010 ) 2011 2012 def _log(self, msg, *args): 2013 self.logger.info("%s " + msg, *((self._log_desc,) + args)) 2014 2015 def _log_debug(self, msg, *args): 2016 self.logger.debug("%s " + msg, *((self._log_desc,) + args)) 2017 2018 def __repr__(self): 2019 return "<Mapper at 0x%x; %s>" % (id(self), self.class_.__name__) 2020 2021 def __str__(self): 2022 return "mapped class %s%s->%s" % ( 2023 self.class_.__name__, 2024 self.non_primary and " (non-primary)" or "", 2025 self.local_table.description 2026 if self.local_table is not None 2027 else self.persist_selectable.description, 2028 ) 2029 2030 def _is_orphan(self, state): 2031 orphan_possible = False 2032 for mapper in self.iterate_to_root(): 2033 for (key, cls) in mapper._delete_orphans: 2034 orphan_possible = True 2035 2036 has_parent = attributes.manager_of_class(cls).has_parent( 2037 state, key, optimistic=state.has_identity 2038 ) 2039 2040 if self.legacy_is_orphan and has_parent: 2041 return False 2042 elif not self.legacy_is_orphan and not has_parent: 2043 return True 2044 2045 if self.legacy_is_orphan: 2046 return orphan_possible 2047 else: 2048 return False 2049 2050 def has_property(self, key): 2051 return key in self._props 2052 2053 def get_property(self, key, _configure_mappers=True): 2054 """return a MapperProperty associated with the given key.""" 2055 2056 if _configure_mappers and Mapper._new_mappers: 2057 configure_mappers() 2058 2059 try: 2060 return self._props[key] 2061 except KeyError as err: 2062 util.raise_( 2063 sa_exc.InvalidRequestError( 2064 "Mapper '%s' has no property '%s'" % (self, key) 2065 ), 2066 replace_context=err, 2067 ) 2068 2069 def get_property_by_column(self, column): 2070 """Given a :class:`_schema.Column` object, return the 2071 :class:`.MapperProperty` which maps this column.""" 2072 2073 return self._columntoproperty[column] 2074 2075 @property 2076 def iterate_properties(self): 2077 """return an iterator of all MapperProperty objects.""" 2078 if Mapper._new_mappers: 2079 configure_mappers() 2080 return iter(self._props.values()) 2081 2082 def _mappers_from_spec(self, spec, selectable): 2083 """given a with_polymorphic() argument, return the set of mappers it 2084 represents. 2085 2086 Trims the list of mappers to just those represented within the given 2087 selectable, if present. This helps some more legacy-ish mappings. 2088 2089 """ 2090 if spec == "*": 2091 mappers = list(self.self_and_descendants) 2092 elif spec: 2093 mappers = set() 2094 for m in util.to_list(spec): 2095 m = _class_to_mapper(m) 2096 if not m.isa(self): 2097 raise sa_exc.InvalidRequestError( 2098 "%r does not inherit from %r" % (m, self) 2099 ) 2100 2101 if selectable is None: 2102 mappers.update(m.iterate_to_root()) 2103 else: 2104 mappers.add(m) 2105 mappers = [m for m in self.self_and_descendants if m in mappers] 2106 else: 2107 mappers = [] 2108 2109 if selectable is not None: 2110 tables = set( 2111 sql_util.find_tables(selectable, include_aliases=True) 2112 ) 2113 mappers = [m for m in mappers if m.local_table in tables] 2114 return mappers 2115 2116 def _selectable_from_mappers(self, mappers, innerjoin): 2117 """given a list of mappers (assumed to be within this mapper's 2118 inheritance hierarchy), construct an outerjoin amongst those mapper's 2119 mapped tables. 2120 2121 """ 2122 from_obj = self.persist_selectable 2123 for m in mappers: 2124 if m is self: 2125 continue 2126 if m.concrete: 2127 raise sa_exc.InvalidRequestError( 2128 "'with_polymorphic()' requires 'selectable' argument " 2129 "when concrete-inheriting mappers are used." 2130 ) 2131 elif not m.single: 2132 if innerjoin: 2133 from_obj = from_obj.join( 2134 m.local_table, m.inherit_condition 2135 ) 2136 else: 2137 from_obj = from_obj.outerjoin( 2138 m.local_table, m.inherit_condition 2139 ) 2140 2141 return from_obj 2142 2143 @_memoized_configured_property 2144 def _single_table_criterion(self): 2145 if self.single and self.inherits and self.polymorphic_on is not None: 2146 return self.polymorphic_on._annotate({"parentmapper": self}).in_( 2147 m.polymorphic_identity for m in self.self_and_descendants 2148 ) 2149 else: 2150 return None 2151 2152 @_memoized_configured_property 2153 def _with_polymorphic_mappers(self): 2154 if Mapper._new_mappers: 2155 configure_mappers() 2156 if not self.with_polymorphic: 2157 return [] 2158 return self._mappers_from_spec(*self.with_polymorphic) 2159 2160 @_memoized_configured_property 2161 def _with_polymorphic_selectable(self): 2162 if not self.with_polymorphic: 2163 return self.persist_selectable 2164 2165 spec, selectable = self.with_polymorphic 2166 if selectable is not None: 2167 return selectable 2168 else: 2169 return self._selectable_from_mappers( 2170 self._mappers_from_spec(spec, selectable), False 2171 ) 2172 2173 with_polymorphic_mappers = _with_polymorphic_mappers 2174 """The list of :class:`_orm.Mapper` objects included in the 2175 default "polymorphic" query. 2176 2177 """ 2178 2179 @_memoized_configured_property 2180 def _insert_cols_evaluating_none(self): 2181 return dict( 2182 ( 2183 table, 2184 frozenset( 2185 col for col in columns if col.type.should_evaluate_none 2186 ), 2187 ) 2188 for table, columns in self._cols_by_table.items() 2189 ) 2190 2191 @_memoized_configured_property 2192 def _insert_cols_as_none(self): 2193 return dict( 2194 ( 2195 table, 2196 frozenset( 2197 col.key 2198 for col in columns 2199 if not col.primary_key 2200 and not col.server_default 2201 and not col.default 2202 and not col.type.should_evaluate_none 2203 ), 2204 ) 2205 for table, columns in self._cols_by_table.items() 2206 ) 2207 2208 @_memoized_configured_property 2209 def _propkey_to_col(self): 2210 return dict( 2211 ( 2212 table, 2213 dict( 2214 (self._columntoproperty[col].key, col) for col in columns 2215 ), 2216 ) 2217 for table, columns in self._cols_by_table.items() 2218 ) 2219 2220 @_memoized_configured_property 2221 def _pk_keys_by_table(self): 2222 return dict( 2223 (table, frozenset([col.key for col in pks])) 2224 for table, pks in self._pks_by_table.items() 2225 ) 2226 2227 @_memoized_configured_property 2228 def _pk_attr_keys_by_table(self): 2229 return dict( 2230 ( 2231 table, 2232 frozenset([self._columntoproperty[col].key for col in pks]), 2233 ) 2234 for table, pks in self._pks_by_table.items() 2235 ) 2236 2237 @_memoized_configured_property 2238 def _server_default_cols(self): 2239 return dict( 2240 ( 2241 table, 2242 frozenset( 2243 [ 2244 col.key 2245 for col in columns 2246 if col.server_default is not None 2247 ] 2248 ), 2249 ) 2250 for table, columns in self._cols_by_table.items() 2251 ) 2252 2253 @_memoized_configured_property 2254 def _server_default_plus_onupdate_propkeys(self): 2255 result = set() 2256 2257 for table, columns in self._cols_by_table.items(): 2258 for col in columns: 2259 if ( 2260 col.server_default is not None 2261 or col.server_onupdate is not None 2262 ) and col in self._columntoproperty: 2263 result.add(self._columntoproperty[col].key) 2264 2265 return result 2266 2267 @_memoized_configured_property 2268 def _server_onupdate_default_cols(self): 2269 return dict( 2270 ( 2271 table, 2272 frozenset( 2273 [ 2274 col.key 2275 for col in columns 2276 if col.server_onupdate is not None 2277 ] 2278 ), 2279 ) 2280 for table, columns in self._cols_by_table.items() 2281 ) 2282 2283 @property 2284 def selectable(self): 2285 """The :func:`_expression.select` construct this :class:`_orm.Mapper` 2286 selects from 2287 by default. 2288 2289 Normally, this is equivalent to :attr:`.persist_selectable`, unless 2290 the ``with_polymorphic`` feature is in use, in which case the 2291 full "polymorphic" selectable is returned. 2292 2293 """ 2294 return self._with_polymorphic_selectable 2295 2296 def _with_polymorphic_args( 2297 self, spec=None, selectable=False, innerjoin=False 2298 ): 2299 if self.with_polymorphic: 2300 if not spec: 2301 spec = self.with_polymorphic[0] 2302 if selectable is False: 2303 selectable = self.with_polymorphic[1] 2304 elif selectable is False: 2305 selectable = None 2306 mappers = self._mappers_from_spec(spec, selectable) 2307 if selectable is not None: 2308 return mappers, selectable 2309 else: 2310 return mappers, self._selectable_from_mappers(mappers, innerjoin) 2311 2312 @_memoized_configured_property 2313 def _polymorphic_properties(self): 2314 return list( 2315 self._iterate_polymorphic_properties( 2316 self._with_polymorphic_mappers 2317 ) 2318 ) 2319 2320 def _iterate_polymorphic_properties(self, mappers=None): 2321 """Return an iterator of MapperProperty objects which will render into 2322 a SELECT.""" 2323 if mappers is None: 2324 mappers = self._with_polymorphic_mappers 2325 2326 if not mappers: 2327 for c in self.iterate_properties: 2328 yield c 2329 else: 2330 # in the polymorphic case, filter out discriminator columns 2331 # from other mappers, as these are sometimes dependent on that 2332 # mapper's polymorphic selectable (which we don't want rendered) 2333 for c in util.unique_list( 2334 chain( 2335 *[ 2336 list(mapper.iterate_properties) 2337 for mapper in [self] + mappers 2338 ] 2339 ) 2340 ): 2341 if getattr(c, "_is_polymorphic_discriminator", False) and ( 2342 self.polymorphic_on is None 2343 or c.columns[0] is not self.polymorphic_on 2344 ): 2345 continue 2346 yield c 2347 2348 @_memoized_configured_property 2349 def attrs(self): 2350 """A namespace of all :class:`.MapperProperty` objects 2351 associated this mapper. 2352 2353 This is an object that provides each property based on 2354 its key name. For instance, the mapper for a 2355 ``User`` class which has ``User.name`` attribute would 2356 provide ``mapper.attrs.name``, which would be the 2357 :class:`.ColumnProperty` representing the ``name`` 2358 column. The namespace object can also be iterated, 2359 which would yield each :class:`.MapperProperty`. 2360 2361 :class:`_orm.Mapper` has several pre-filtered views 2362 of this attribute which limit the types of properties 2363 returned, including :attr:`.synonyms`, :attr:`.column_attrs`, 2364 :attr:`.relationships`, and :attr:`.composites`. 2365 2366 .. warning:: 2367 2368 The :attr:`_orm.Mapper.attrs` accessor namespace is an 2369 instance of :class:`.OrderedProperties`. This is 2370 a dictionary-like object which includes a small number of 2371 named methods such as :meth:`.OrderedProperties.items` 2372 and :meth:`.OrderedProperties.values`. When 2373 accessing attributes dynamically, favor using the dict-access 2374 scheme, e.g. ``mapper.attrs[somename]`` over 2375 ``getattr(mapper.attrs, somename)`` to avoid name collisions. 2376 2377 .. seealso:: 2378 2379 :attr:`_orm.Mapper.all_orm_descriptors` 2380 2381 """ 2382 if Mapper._new_mappers: 2383 configure_mappers() 2384 return util.ImmutableProperties(self._props) 2385 2386 @_memoized_configured_property 2387 def all_orm_descriptors(self): 2388 """A namespace of all :class:`.InspectionAttr` attributes associated 2389 with the mapped class. 2390 2391 These attributes are in all cases Python :term:`descriptors` 2392 associated with the mapped class or its superclasses. 2393 2394 This namespace includes attributes that are mapped to the class 2395 as well as attributes declared by extension modules. 2396 It includes any Python descriptor type that inherits from 2397 :class:`.InspectionAttr`. This includes 2398 :class:`.QueryableAttribute`, as well as extension types such as 2399 :class:`.hybrid_property`, :class:`.hybrid_method` and 2400 :class:`.AssociationProxy`. 2401 2402 To distinguish between mapped attributes and extension attributes, 2403 the attribute :attr:`.InspectionAttr.extension_type` will refer 2404 to a constant that distinguishes between different extension types. 2405 2406 The sorting of the attributes is based on the following rules: 2407 2408 1. Iterate through the class and its superclasses in order from 2409 subclass to superclass (i.e. iterate through ``cls.__mro__``) 2410 2411 2. For each class, yield the attributes in the order in which they 2412 appear in ``__dict__``, with the exception of those in step 2413 3 below. In Python 3.6 and above this ordering will be the 2414 same as that of the class' construction, with the exception 2415 of attributes that were added after the fact by the application 2416 or the mapper. 2417 2418 3. If a certain attribute key is also in the superclass ``__dict__``, 2419 then it's included in the iteration for that class, and not the 2420 class in which it first appeared. 2421 2422 The above process produces an ordering that is deterministic in terms 2423 of the order in which attributes were assigned to the class. 2424 2425 .. versionchanged:: 1.3.19 ensured deterministic ordering for 2426 :meth:`_orm.Mapper.all_orm_descriptors`. 2427 2428 When dealing with a :class:`.QueryableAttribute`, the 2429 :attr:`.QueryableAttribute.property` attribute refers to the 2430 :class:`.MapperProperty` property, which is what you get when 2431 referring to the collection of mapped properties via 2432 :attr:`_orm.Mapper.attrs`. 2433 2434 .. warning:: 2435 2436 The :attr:`_orm.Mapper.all_orm_descriptors` 2437 accessor namespace is an 2438 instance of :class:`.OrderedProperties`. This is 2439 a dictionary-like object which includes a small number of 2440 named methods such as :meth:`.OrderedProperties.items` 2441 and :meth:`.OrderedProperties.values`. When 2442 accessing attributes dynamically, favor using the dict-access 2443 scheme, e.g. ``mapper.all_orm_descriptors[somename]`` over 2444 ``getattr(mapper.all_orm_descriptors, somename)`` to avoid name 2445 collisions. 2446 2447 .. seealso:: 2448 2449 :attr:`_orm.Mapper.attrs` 2450 2451 """ 2452 return util.ImmutableProperties( 2453 dict(self.class_manager._all_sqla_attributes()) 2454 ) 2455 2456 @_memoized_configured_property 2457 def synonyms(self): 2458 """Return a namespace of all :class:`.SynonymProperty` 2459 properties maintained by this :class:`_orm.Mapper`. 2460 2461 .. seealso:: 2462 2463 :attr:`_orm.Mapper.attrs` - namespace of all 2464 :class:`.MapperProperty` 2465 objects. 2466 2467 """ 2468 return self._filter_properties(properties.SynonymProperty) 2469 2470 @_memoized_configured_property 2471 def column_attrs(self): 2472 """Return a namespace of all :class:`.ColumnProperty` 2473 properties maintained by this :class:`_orm.Mapper`. 2474 2475 .. seealso:: 2476 2477 :attr:`_orm.Mapper.attrs` - namespace of all 2478 :class:`.MapperProperty` 2479 objects. 2480 2481 """ 2482 return self._filter_properties(properties.ColumnProperty) 2483 2484 @_memoized_configured_property 2485 def relationships(self): 2486 """A namespace of all :class:`.RelationshipProperty` properties 2487 maintained by this :class:`_orm.Mapper`. 2488 2489 .. warning:: 2490 2491 the :attr:`_orm.Mapper.relationships` accessor namespace is an 2492 instance of :class:`.OrderedProperties`. This is 2493 a dictionary-like object which includes a small number of 2494 named methods such as :meth:`.OrderedProperties.items` 2495 and :meth:`.OrderedProperties.values`. When 2496 accessing attributes dynamically, favor using the dict-access 2497 scheme, e.g. ``mapper.relationships[somename]`` over 2498 ``getattr(mapper.relationships, somename)`` to avoid name 2499 collisions. 2500 2501 .. seealso:: 2502 2503 :attr:`_orm.Mapper.attrs` - namespace of all 2504 :class:`.MapperProperty` 2505 objects. 2506 2507 """ 2508 return self._filter_properties(properties.RelationshipProperty) 2509 2510 @_memoized_configured_property 2511 def composites(self): 2512 """Return a namespace of all :class:`.CompositeProperty` 2513 properties maintained by this :class:`_orm.Mapper`. 2514 2515 .. seealso:: 2516 2517 :attr:`_orm.Mapper.attrs` - namespace of all 2518 :class:`.MapperProperty` 2519 objects. 2520 2521 """ 2522 return self._filter_properties(properties.CompositeProperty) 2523 2524 def _filter_properties(self, type_): 2525 if Mapper._new_mappers: 2526 configure_mappers() 2527 return util.ImmutableProperties( 2528 util.OrderedDict( 2529 (k, v) for k, v in self._props.items() if isinstance(v, type_) 2530 ) 2531 ) 2532 2533 @_memoized_configured_property 2534 def _get_clause(self): 2535 """create a "get clause" based on the primary key. this is used 2536 by query.get() and many-to-one lazyloads to load this item 2537 by primary key. 2538 2539 """ 2540 params = [ 2541 (primary_key, sql.bindparam(None, type_=primary_key.type)) 2542 for primary_key in self.primary_key 2543 ] 2544 return ( 2545 sql.and_(*[k == v for (k, v) in params]), 2546 util.column_dict(params), 2547 ) 2548 2549 @_memoized_configured_property 2550 def _equivalent_columns(self): 2551 """Create a map of all equivalent columns, based on 2552 the determination of column pairs that are equated to 2553 one another based on inherit condition. This is designed 2554 to work with the queries that util.polymorphic_union 2555 comes up with, which often don't include the columns from 2556 the base table directly (including the subclass table columns 2557 only). 2558 2559 The resulting structure is a dictionary of columns mapped 2560 to lists of equivalent columns, e.g.:: 2561 2562 { 2563 tablea.col1: 2564 {tableb.col1, tablec.col1}, 2565 tablea.col2: 2566 {tabled.col2} 2567 } 2568 2569 """ 2570 result = util.column_dict() 2571 2572 def visit_binary(binary): 2573 if binary.operator == operators.eq: 2574 if binary.left in result: 2575 result[binary.left].add(binary.right) 2576 else: 2577 result[binary.left] = util.column_set((binary.right,)) 2578 if binary.right in result: 2579 result[binary.right].add(binary.left) 2580 else: 2581 result[binary.right] = util.column_set((binary.left,)) 2582 2583 for mapper in self.base_mapper.self_and_descendants: 2584 if mapper.inherit_condition is not None: 2585 visitors.traverse( 2586 mapper.inherit_condition, {}, {"binary": visit_binary} 2587 ) 2588 2589 return result 2590 2591 def _is_userland_descriptor(self, obj): 2592 if isinstance( 2593 obj, 2594 ( 2595 _MappedAttribute, 2596 instrumentation.ClassManager, 2597 expression.ColumnElement, 2598 ), 2599 ): 2600 return False 2601 else: 2602 return True 2603 2604 def _should_exclude(self, name, assigned_name, local, column): 2605 """determine whether a particular property should be implicitly 2606 present on the class. 2607 2608 This occurs when properties are propagated from an inherited class, or 2609 are applied from the columns present in the mapped table. 2610 2611 """ 2612 2613 # check for class-bound attributes and/or descriptors, 2614 # either local or from an inherited class 2615 if local: 2616 if self.class_.__dict__.get( 2617 assigned_name, None 2618 ) is not None and self._is_userland_descriptor( 2619 self.class_.__dict__[assigned_name] 2620 ): 2621 return True 2622 else: 2623 attr = self.class_manager._get_class_attr_mro(assigned_name, None) 2624 if attr is not None and self._is_userland_descriptor(attr): 2625 return True 2626 2627 if ( 2628 self.include_properties is not None 2629 and name not in self.include_properties 2630 and (column is None or column not in self.include_properties) 2631 ): 2632 self._log("not including property %s" % (name)) 2633 return True 2634 2635 if self.exclude_properties is not None and ( 2636 name in self.exclude_properties 2637 or (column is not None and column in self.exclude_properties) 2638 ): 2639 self._log("excluding property %s" % (name)) 2640 return True 2641 2642 return False 2643 2644 def common_parent(self, other): 2645 """Return true if the given mapper shares a 2646 common inherited parent as this mapper.""" 2647 2648 return self.base_mapper is other.base_mapper 2649 2650 def _canload(self, state, allow_subtypes): 2651 s = self.primary_mapper() 2652 if self.polymorphic_on is not None or allow_subtypes: 2653 return _state_mapper(state).isa(s) 2654 else: 2655 return _state_mapper(state) is s 2656 2657 def isa(self, other): 2658 """Return True if the this mapper inherits from the given mapper.""" 2659 2660 m = self 2661 while m and m is not other: 2662 m = m.inherits 2663 return bool(m) 2664 2665 def iterate_to_root(self): 2666 m = self 2667 while m: 2668 yield m 2669 m = m.inherits 2670 2671 @_memoized_configured_property 2672 def self_and_descendants(self): 2673 """The collection including this mapper and all descendant mappers. 2674 2675 This includes not just the immediately inheriting mappers but 2676 all their inheriting mappers as well. 2677 2678 """ 2679 descendants = [] 2680 stack = deque([self]) 2681 while stack: 2682 item = stack.popleft() 2683 descendants.append(item) 2684 stack.extend(item._inheriting_mappers) 2685 return util.WeakSequence(descendants) 2686 2687 def polymorphic_iterator(self): 2688 """Iterate through the collection including this mapper and 2689 all descendant mappers. 2690 2691 This includes not just the immediately inheriting mappers but 2692 all their inheriting mappers as well. 2693 2694 To iterate through an entire hierarchy, use 2695 ``mapper.base_mapper.polymorphic_iterator()``. 2696 2697 """ 2698 return iter(self.self_and_descendants) 2699 2700 def primary_mapper(self): 2701 """Return the primary mapper corresponding to this mapper's class key 2702 (class).""" 2703 2704 return self.class_manager.mapper 2705 2706 @property 2707 def primary_base_mapper(self): 2708 return self.class_manager.mapper.base_mapper 2709 2710 def _result_has_identity_key(self, result, adapter=None): 2711 pk_cols = self.primary_key 2712 if adapter: 2713 pk_cols = [adapter.columns[c] for c in pk_cols] 2714 for col in pk_cols: 2715 if not result._has_key(col): 2716 return False 2717 else: 2718 return True 2719 2720 def identity_key_from_row(self, row, identity_token=None, adapter=None): 2721 """Return an identity-map key for use in storing/retrieving an 2722 item from the identity map. 2723 2724 :param row: A :class:`.RowProxy` instance. The columns which are 2725 mapped by this :class:`_orm.Mapper` should be locatable in the row, 2726 preferably via the :class:`_schema.Column` 2727 object directly (as is the case 2728 when a :func:`_expression.select` construct is executed), 2729 or via string names of 2730 the form ``<tablename>_<colname>``. 2731 2732 """ 2733 pk_cols = self.primary_key 2734 if adapter: 2735 pk_cols = [adapter.columns[c] for c in pk_cols] 2736 2737 return ( 2738 self._identity_class, 2739 tuple(row[column] for column in pk_cols), 2740 identity_token, 2741 ) 2742 2743 def identity_key_from_primary_key(self, primary_key, identity_token=None): 2744 """Return an identity-map key for use in storing/retrieving an 2745 item from an identity map. 2746 2747 :param primary_key: A list of values indicating the identifier. 2748 2749 """ 2750 return self._identity_class, tuple(primary_key), identity_token 2751 2752 def identity_key_from_instance(self, instance): 2753 """Return the identity key for the given instance, based on 2754 its primary key attributes. 2755 2756 If the instance's state is expired, calling this method 2757 will result in a database check to see if the object has been deleted. 2758 If the row no longer exists, 2759 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 2760 2761 This value is typically also found on the instance state under the 2762 attribute name `key`. 2763 2764 """ 2765 state = attributes.instance_state(instance) 2766 return self._identity_key_from_state(state, attributes.PASSIVE_OFF) 2767 2768 def _identity_key_from_state( 2769 self, state, passive=attributes.PASSIVE_RETURN_NEVER_SET 2770 ): 2771 dict_ = state.dict 2772 manager = state.manager 2773 return ( 2774 self._identity_class, 2775 tuple( 2776 [ 2777 manager[prop.key].impl.get(state, dict_, passive) 2778 for prop in self._identity_key_props 2779 ] 2780 ), 2781 state.identity_token, 2782 ) 2783 2784 def primary_key_from_instance(self, instance): 2785 """Return the list of primary key values for the given 2786 instance. 2787 2788 If the instance's state is expired, calling this method 2789 will result in a database check to see if the object has been deleted. 2790 If the row no longer exists, 2791 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 2792 2793 """ 2794 state = attributes.instance_state(instance) 2795 identity_key = self._identity_key_from_state( 2796 state, attributes.PASSIVE_OFF 2797 ) 2798 return identity_key[1] 2799 2800 @_memoized_configured_property 2801 def _persistent_sortkey_fn(self): 2802 key_fns = [col.type.sort_key_function for col in self.primary_key] 2803 2804 if set(key_fns).difference([None]): 2805 2806 def key(state): 2807 return tuple( 2808 key_fn(val) if key_fn is not None else val 2809 for key_fn, val in zip(key_fns, state.key[1]) 2810 ) 2811 2812 else: 2813 2814 def key(state): 2815 return state.key[1] 2816 2817 return key 2818 2819 @_memoized_configured_property 2820 def _identity_key_props(self): 2821 return [self._columntoproperty[col] for col in self.primary_key] 2822 2823 @_memoized_configured_property 2824 def _all_pk_props(self): 2825 collection = set() 2826 for table in self.tables: 2827 collection.update(self._pks_by_table[table]) 2828 return collection 2829 2830 @_memoized_configured_property 2831 def _should_undefer_in_wildcard(self): 2832 cols = set(self.primary_key) 2833 if self.polymorphic_on is not None: 2834 cols.add(self.polymorphic_on) 2835 return cols 2836 2837 @_memoized_configured_property 2838 def _primary_key_propkeys(self): 2839 return {prop.key for prop in self._all_pk_props} 2840 2841 def _get_state_attr_by_column( 2842 self, state, dict_, column, passive=attributes.PASSIVE_RETURN_NEVER_SET 2843 ): 2844 prop = self._columntoproperty[column] 2845 return state.manager[prop.key].impl.get(state, dict_, passive=passive) 2846 2847 def _set_committed_state_attr_by_column(self, state, dict_, column, value): 2848 prop = self._columntoproperty[column] 2849 state.manager[prop.key].impl.set_committed_value(state, dict_, value) 2850 2851 def _set_state_attr_by_column(self, state, dict_, column, value): 2852 prop = self._columntoproperty[column] 2853 state.manager[prop.key].impl.set(state, dict_, value, None) 2854 2855 def _get_committed_attr_by_column(self, obj, column): 2856 state = attributes.instance_state(obj) 2857 dict_ = attributes.instance_dict(obj) 2858 return self._get_committed_state_attr_by_column( 2859 state, dict_, column, passive=attributes.PASSIVE_OFF 2860 ) 2861 2862 def _get_committed_state_attr_by_column( 2863 self, state, dict_, column, passive=attributes.PASSIVE_RETURN_NEVER_SET 2864 ): 2865 2866 prop = self._columntoproperty[column] 2867 return state.manager[prop.key].impl.get_committed_value( 2868 state, dict_, passive=passive 2869 ) 2870 2871 def _optimized_get_statement(self, state, attribute_names): 2872 """assemble a WHERE clause which retrieves a given state by primary 2873 key, using a minimized set of tables. 2874 2875 Applies to a joined-table inheritance mapper where the 2876 requested attribute names are only present on joined tables, 2877 not the base table. The WHERE clause attempts to include 2878 only those tables to minimize joins. 2879 2880 """ 2881 props = self._props 2882 2883 tables = set( 2884 chain( 2885 *[ 2886 sql_util.find_tables(c, check_columns=True) 2887 for key in attribute_names 2888 for c in props[key].columns 2889 ] 2890 ) 2891 ) 2892 2893 if self.base_mapper.local_table in tables: 2894 return None 2895 2896 def visit_binary(binary): 2897 leftcol = binary.left 2898 rightcol = binary.right 2899 if leftcol is None or rightcol is None: 2900 return 2901 2902 if leftcol.table not in tables: 2903 leftval = self._get_committed_state_attr_by_column( 2904 state, 2905 state.dict, 2906 leftcol, 2907 passive=attributes.PASSIVE_NO_INITIALIZE, 2908 ) 2909 if leftval in orm_util._none_set: 2910 raise _OptGetColumnsNotAvailable() 2911 binary.left = sql.bindparam( 2912 None, leftval, type_=binary.right.type 2913 ) 2914 elif rightcol.table not in tables: 2915 rightval = self._get_committed_state_attr_by_column( 2916 state, 2917 state.dict, 2918 rightcol, 2919 passive=attributes.PASSIVE_NO_INITIALIZE, 2920 ) 2921 if rightval in orm_util._none_set: 2922 raise _OptGetColumnsNotAvailable() 2923 binary.right = sql.bindparam( 2924 None, rightval, type_=binary.right.type 2925 ) 2926 2927 allconds = [] 2928 2929 try: 2930 start = False 2931 for mapper in reversed(list(self.iterate_to_root())): 2932 if mapper.local_table in tables: 2933 start = True 2934 elif not isinstance( 2935 mapper.local_table, expression.TableClause 2936 ): 2937 return None 2938 if start and not mapper.single: 2939 allconds.append( 2940 visitors.cloned_traverse( 2941 mapper.inherit_condition, 2942 {}, 2943 {"binary": visit_binary}, 2944 ) 2945 ) 2946 except _OptGetColumnsNotAvailable: 2947 return None 2948 2949 cond = sql.and_(*allconds) 2950 2951 cols = [] 2952 for key in attribute_names: 2953 cols.extend(props[key].columns) 2954 return sql.select(cols, cond, use_labels=True) 2955 2956 def _iterate_to_target_viawpoly(self, mapper): 2957 if self.isa(mapper): 2958 prev = self 2959 for m in self.iterate_to_root(): 2960 yield m 2961 2962 if m is not prev and prev not in m._with_polymorphic_mappers: 2963 break 2964 2965 prev = m 2966 if m is mapper: 2967 break 2968 2969 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 2970 if not enabled_via_opt: 2971 # common case, takes place for all polymorphic loads 2972 mapper = polymorphic_from 2973 for m in self._iterate_to_target_viawpoly(mapper): 2974 if m.polymorphic_load == "selectin": 2975 return m 2976 else: 2977 # uncommon case, selectin load options were used 2978 enabled_via_opt = set(enabled_via_opt) 2979 enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 2980 for entity in enabled_via_opt.union([polymorphic_from]): 2981 mapper = entity.mapper 2982 for m in self._iterate_to_target_viawpoly(mapper): 2983 if ( 2984 m.polymorphic_load == "selectin" 2985 or m in enabled_via_opt_mappers 2986 ): 2987 return enabled_via_opt_mappers.get(m, m) 2988 2989 return None 2990 2991 @util.dependencies( 2992 "sqlalchemy.ext.baked", "sqlalchemy.orm.strategy_options" 2993 ) 2994 def _subclass_load_via_in(self, baked, strategy_options, entity): 2995 """Assemble a BakedQuery that can load the columns local to 2996 this subclass as a SELECT with IN. 2997 2998 """ 2999 assert self.inherits 3000 3001 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 3002 keep_props = set([polymorphic_prop] + self._identity_key_props) 3003 3004 disable_opt = strategy_options.Load(entity) 3005 enable_opt = strategy_options.Load(entity) 3006 3007 for prop in self.attrs: 3008 if prop.parent is self or prop in keep_props: 3009 # "enable" options, to turn on the properties that we want to 3010 # load by default (subject to options from the query) 3011 enable_opt.set_generic_strategy( 3012 (prop.key,), dict(prop.strategy_key) 3013 ) 3014 else: 3015 # "disable" options, to turn off the properties from the 3016 # superclass that we *don't* want to load, applied after 3017 # the options from the query to override them 3018 disable_opt.set_generic_strategy( 3019 (prop.key,), {"do_nothing": True} 3020 ) 3021 3022 if len(self.primary_key) > 1: 3023 in_expr = sql.tuple_(*self.primary_key) 3024 else: 3025 in_expr = self.primary_key[0] 3026 3027 if entity.is_aliased_class: 3028 assert entity.mapper is self 3029 q = baked.BakedQuery( 3030 self._compiled_cache, 3031 lambda session: session.query(entity) 3032 .select_entity_from(entity.selectable) 3033 ._adapt_all_clauses(), 3034 (self,), 3035 ) 3036 q.spoil() 3037 else: 3038 q = baked.BakedQuery( 3039 self._compiled_cache, 3040 lambda session: session.query(self), 3041 (self,), 3042 ) 3043 3044 q += lambda q: q.filter( 3045 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 3046 ).order_by(*self.primary_key) 3047 3048 return q, enable_opt, disable_opt 3049 3050 @_memoized_configured_property 3051 def _subclass_load_via_in_mapper(self): 3052 return self._subclass_load_via_in(self) 3053 3054 def cascade_iterator(self, type_, state, halt_on=None): 3055 r"""Iterate each element and its mapper in an object graph, 3056 for all relationships that meet the given cascade rule. 3057 3058 :param type\_: 3059 The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 3060 etc.). 3061 3062 .. note:: the ``"all"`` cascade is not accepted here. For a generic 3063 object traversal function, see :ref:`faq_walk_objects`. 3064 3065 :param state: 3066 The lead InstanceState. child items will be processed per 3067 the relationships defined for this object's mapper. 3068 3069 :return: the method yields individual object instances. 3070 3071 .. seealso:: 3072 3073 :ref:`unitofwork_cascades` 3074 3075 :ref:`faq_walk_objects` - illustrates a generic function to 3076 traverse all objects without relying on cascades. 3077 3078 """ 3079 visited_states = set() 3080 prp, mpp = object(), object() 3081 3082 assert state.mapper.isa(self) 3083 3084 visitables = deque( 3085 [(deque(state.mapper._props.values()), prp, state, state.dict)] 3086 ) 3087 3088 while visitables: 3089 iterator, item_type, parent_state, parent_dict = visitables[-1] 3090 if not iterator: 3091 visitables.pop() 3092 continue 3093 3094 if item_type is prp: 3095 prop = iterator.popleft() 3096 if type_ not in prop.cascade: 3097 continue 3098 queue = deque( 3099 prop.cascade_iterator( 3100 type_, 3101 parent_state, 3102 parent_dict, 3103 visited_states, 3104 halt_on, 3105 ) 3106 ) 3107 if queue: 3108 visitables.append((queue, mpp, None, None)) 3109 elif item_type is mpp: 3110 ( 3111 instance, 3112 instance_mapper, 3113 corresponding_state, 3114 corresponding_dict, 3115 ) = iterator.popleft() 3116 yield ( 3117 instance, 3118 instance_mapper, 3119 corresponding_state, 3120 corresponding_dict, 3121 ) 3122 visitables.append( 3123 ( 3124 deque(instance_mapper._props.values()), 3125 prp, 3126 corresponding_state, 3127 corresponding_dict, 3128 ) 3129 ) 3130 3131 @_memoized_configured_property 3132 def _compiled_cache(self): 3133 return util.LRUCache(self._compiled_cache_size) 3134 3135 @_memoized_configured_property 3136 def _sorted_tables(self): 3137 table_to_mapper = {} 3138 3139 for mapper in self.base_mapper.self_and_descendants: 3140 for t in mapper.tables: 3141 table_to_mapper.setdefault(t, mapper) 3142 3143 extra_dependencies = [] 3144 for table, mapper in table_to_mapper.items(): 3145 super_ = mapper.inherits 3146 if super_: 3147 extra_dependencies.extend( 3148 [(super_table, table) for super_table in super_.tables] 3149 ) 3150 3151 def skip(fk): 3152 # attempt to skip dependencies that are not 3153 # significant to the inheritance chain 3154 # for two tables that are related by inheritance. 3155 # while that dependency may be important, it's technically 3156 # not what we mean to sort on here. 3157 parent = table_to_mapper.get(fk.parent.table) 3158 dep = table_to_mapper.get(fk.column.table) 3159 if ( 3160 parent is not None 3161 and dep is not None 3162 and dep is not parent 3163 and dep.inherit_condition is not None 3164 ): 3165 cols = set(sql_util._find_columns(dep.inherit_condition)) 3166 if parent.inherit_condition is not None: 3167 cols = cols.union( 3168 sql_util._find_columns(parent.inherit_condition) 3169 ) 3170 return fk.parent not in cols and fk.column not in cols 3171 else: 3172 return fk.parent not in cols 3173 return False 3174 3175 sorted_ = sql_util.sort_tables( 3176 table_to_mapper, 3177 skip_fn=skip, 3178 extra_dependencies=extra_dependencies, 3179 ) 3180 3181 ret = util.OrderedDict() 3182 for t in sorted_: 3183 ret[t] = table_to_mapper[t] 3184 return ret 3185 3186 def _memo(self, key, callable_): 3187 if key in self._memoized_values: 3188 return self._memoized_values[key] 3189 else: 3190 self._memoized_values[key] = value = callable_() 3191 return value 3192 3193 @util.memoized_property 3194 def _table_to_equated(self): 3195 """memoized map of tables to collections of columns to be 3196 synchronized upwards to the base mapper.""" 3197 3198 result = util.defaultdict(list) 3199 3200 for table in self._sorted_tables: 3201 cols = set(table.c) 3202 for m in self.iterate_to_root(): 3203 if m._inherits_equated_pairs and cols.intersection( 3204 util.reduce( 3205 set.union, 3206 [l.proxy_set for l, r in m._inherits_equated_pairs], 3207 ) 3208 ): 3209 result[table].append((m, m._inherits_equated_pairs)) 3210 3211 return result 3212 3213 3214class _OptGetColumnsNotAvailable(Exception): 3215 pass 3216 3217 3218def configure_mappers(): 3219 """Initialize the inter-mapper relationships of all mappers that 3220 have been constructed thus far. 3221 3222 This function can be called any number of times, but in 3223 most cases is invoked automatically, the first time mappings are used, 3224 as well as whenever mappings are used and additional not-yet-configured 3225 mappers have been constructed. 3226 3227 Points at which this occur include when a mapped class is instantiated 3228 into an instance, as well as when the :meth:`.Session.query` method 3229 is used. 3230 3231 The :func:`.configure_mappers` function provides several event hooks 3232 that can be used to augment its functionality. These methods include: 3233 3234 * :meth:`.MapperEvents.before_configured` - called once before 3235 :func:`.configure_mappers` does any work; this can be used to establish 3236 additional options, properties, or related mappings before the operation 3237 proceeds. 3238 3239 * :meth:`.MapperEvents.mapper_configured` - called as each individual 3240 :class:`_orm.Mapper` is configured within the process; will include all 3241 mapper state except for backrefs set up by other mappers that are still 3242 to be configured. 3243 3244 * :meth:`.MapperEvents.after_configured` - called once after 3245 :func:`.configure_mappers` is complete; at this stage, all 3246 :class:`_orm.Mapper` objects that are known to SQLAlchemy will be fully 3247 configured. Note that the calling application may still have other 3248 mappings that haven't been produced yet, such as if they are in modules 3249 as yet unimported. 3250 3251 """ 3252 3253 if not Mapper._new_mappers: 3254 return 3255 3256 _CONFIGURE_MUTEX.acquire() 3257 try: 3258 global _already_compiling 3259 if _already_compiling: 3260 return 3261 _already_compiling = True 3262 try: 3263 3264 # double-check inside mutex 3265 if not Mapper._new_mappers: 3266 return 3267 3268 has_skip = False 3269 3270 Mapper.dispatch._for_class(Mapper).before_configured() 3271 # initialize properties on all mappers 3272 # note that _mapper_registry is unordered, which 3273 # may randomly conceal/reveal issues related to 3274 # the order of mapper compilation 3275 3276 for mapper in list(_mapper_registry): 3277 run_configure = None 3278 for fn in mapper.dispatch.before_mapper_configured: 3279 run_configure = fn(mapper, mapper.class_) 3280 if run_configure is EXT_SKIP: 3281 has_skip = True 3282 break 3283 if run_configure is EXT_SKIP: 3284 continue 3285 3286 if getattr(mapper, "_configure_failed", False): 3287 e = sa_exc.InvalidRequestError( 3288 "One or more mappers failed to initialize - " 3289 "can't proceed with initialization of other " 3290 "mappers. Triggering mapper: '%s'. " 3291 "Original exception was: %s" 3292 % (mapper, mapper._configure_failed) 3293 ) 3294 e._configure_failed = mapper._configure_failed 3295 raise e 3296 3297 if not mapper.configured: 3298 try: 3299 mapper._post_configure_properties() 3300 mapper._expire_memoizations() 3301 mapper.dispatch.mapper_configured( 3302 mapper, mapper.class_ 3303 ) 3304 except Exception: 3305 exc = sys.exc_info()[1] 3306 if not hasattr(exc, "_configure_failed"): 3307 mapper._configure_failed = exc 3308 raise 3309 3310 if not has_skip: 3311 Mapper._new_mappers = False 3312 finally: 3313 _already_compiling = False 3314 finally: 3315 _CONFIGURE_MUTEX.release() 3316 Mapper.dispatch._for_class(Mapper).after_configured() 3317 3318 3319def reconstructor(fn): 3320 """Decorate a method as the 'reconstructor' hook. 3321 3322 Designates a method as the "reconstructor", an ``__init__``-like 3323 method that will be called by the ORM after the instance has been 3324 loaded from the database or otherwise reconstituted. 3325 3326 The reconstructor will be invoked with no arguments. Scalar 3327 (non-collection) database-mapped attributes of the instance will 3328 be available for use within the function. Eagerly-loaded 3329 collections are generally not yet available and will usually only 3330 contain the first element. ORM state changes made to objects at 3331 this stage will not be recorded for the next flush() operation, so 3332 the activity within a reconstructor should be conservative. 3333 3334 .. seealso:: 3335 3336 :ref:`mapping_constructors` 3337 3338 :meth:`.InstanceEvents.load` 3339 3340 """ 3341 fn.__sa_reconstructor__ = True 3342 return fn 3343 3344 3345def validates(*names, **kw): 3346 r"""Decorate a method as a 'validator' for one or more named properties. 3347 3348 Designates a method as a validator, a method which receives the 3349 name of the attribute as well as a value to be assigned, or in the 3350 case of a collection, the value to be added to the collection. 3351 The function can then raise validation exceptions to halt the 3352 process from continuing (where Python's built-in ``ValueError`` 3353 and ``AssertionError`` exceptions are reasonable choices), or can 3354 modify or replace the value before proceeding. The function should 3355 otherwise return the given value. 3356 3357 Note that a validator for a collection **cannot** issue a load of that 3358 collection within the validation routine - this usage raises 3359 an assertion to avoid recursion overflows. This is a reentrant 3360 condition which is not supported. 3361 3362 :param \*names: list of attribute names to be validated. 3363 :param include_removes: if True, "remove" events will be 3364 sent as well - the validation function must accept an additional 3365 argument "is_remove" which will be a boolean. 3366 3367 :param include_backrefs: defaults to ``True``; if ``False``, the 3368 validation function will not emit if the originator is an attribute 3369 event related via a backref. This can be used for bi-directional 3370 :func:`.validates` usage where only one validator should emit per 3371 attribute operation. 3372 3373 .. versionadded:: 0.9.0 3374 3375 .. seealso:: 3376 3377 :ref:`simple_validators` - usage examples for :func:`.validates` 3378 3379 """ 3380 include_removes = kw.pop("include_removes", False) 3381 include_backrefs = kw.pop("include_backrefs", True) 3382 3383 def wrap(fn): 3384 fn.__sa_validators__ = names 3385 fn.__sa_validation_opts__ = { 3386 "include_removes": include_removes, 3387 "include_backrefs": include_backrefs, 3388 } 3389 return fn 3390 3391 return wrap 3392 3393 3394def _event_on_load(state, ctx): 3395 instrumenting_mapper = state.manager.info[_INSTRUMENTOR] 3396 if instrumenting_mapper._reconstructor: 3397 instrumenting_mapper._reconstructor(state.obj()) 3398 3399 3400def _event_on_first_init(manager, cls): 3401 """Initial mapper compilation trigger. 3402 3403 instrumentation calls this one when InstanceState 3404 is first generated, and is needed for legacy mutable 3405 attributes to work. 3406 """ 3407 3408 instrumenting_mapper = manager.info.get(_INSTRUMENTOR) 3409 if instrumenting_mapper: 3410 if Mapper._new_mappers: 3411 configure_mappers() 3412 3413 3414def _event_on_init(state, args, kwargs): 3415 """Run init_instance hooks. 3416 3417 This also includes mapper compilation, normally not needed 3418 here but helps with some piecemeal configuration 3419 scenarios (such as in the ORM tutorial). 3420 3421 """ 3422 3423 instrumenting_mapper = state.manager.info.get(_INSTRUMENTOR) 3424 if instrumenting_mapper: 3425 if Mapper._new_mappers: 3426 configure_mappers() 3427 if instrumenting_mapper._set_polymorphic_identity: 3428 instrumenting_mapper._set_polymorphic_identity(state) 3429 3430 3431class _ColumnMapping(dict): 3432 """Error reporting helper for mapper._columntoproperty.""" 3433 3434 __slots__ = ("mapper",) 3435 3436 def __init__(self, mapper): 3437 self.mapper = mapper 3438 3439 def __missing__(self, column): 3440 prop = self.mapper._props.get(column) 3441 if prop: 3442 raise orm_exc.UnmappedColumnError( 3443 "Column '%s.%s' is not available, due to " 3444 "conflicting property '%s':%r" 3445 % (column.table.name, column.name, column.key, prop) 3446 ) 3447 raise orm_exc.UnmappedColumnError( 3448 "No column %s is configured on mapper %s..." 3449 % (column, self.mapper) 3450 ) 3451