1# orm/mapper.py 2# Copyright (C) 2005-2019 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 InspectionAttr 38from .interfaces import MapperProperty 39from .path_registry import PathRegistry 40from .. import event 41from .. import exc as sa_exc 42from .. import inspection 43from .. import log 44from .. import schema 45from .. import sql 46from .. import util 47from ..sql import expression 48from ..sql import operators 49from ..sql import util as sql_util 50from ..sql import visitors 51 52 53_mapper_registry = weakref.WeakKeyDictionary() 54_already_compiling = False 55 56_memoized_configured_property = util.group_expirable_memoized_property() 57 58 59# a constant returned by _get_attr_by_column to indicate 60# this mapper is not handling an attribute for a particular 61# column 62NO_ATTRIBUTE = util.symbol("NO_ATTRIBUTE") 63 64# lock used to synchronize the "mapper configure" step 65_CONFIGURE_MUTEX = util.threading.RLock() 66 67 68@inspection._self_inspects 69@log.class_logger 70class Mapper(InspectionAttr): 71 """Define the correlation of class attributes to database table 72 columns. 73 74 The :class:`.Mapper` object is instantiated using the 75 :func:`~sqlalchemy.orm.mapper` function. For information 76 about instantiating new :class:`.Mapper` objects, see 77 that function's documentation. 78 79 80 When :func:`.mapper` is used 81 explicitly to link a user defined class with table 82 metadata, this is referred to as *classical mapping*. 83 Modern SQLAlchemy usage tends to favor the 84 :mod:`sqlalchemy.ext.declarative` extension for class 85 configuration, which 86 makes usage of :func:`.mapper` behind the scenes. 87 88 Given a particular class known to be mapped by the ORM, 89 the :class:`.Mapper` which maintains it can be acquired 90 using the :func:`.inspect` function:: 91 92 from sqlalchemy import inspect 93 94 mapper = inspect(MyClass) 95 96 A class which was mapped by the :mod:`sqlalchemy.ext.declarative` 97 extension will also have its mapper available via the ``__mapper__`` 98 attribute. 99 100 101 """ 102 103 _new_mappers = False 104 105 def __init__( 106 self, 107 class_, 108 local_table=None, 109 properties=None, 110 primary_key=None, 111 non_primary=False, 112 inherits=None, 113 inherit_condition=None, 114 inherit_foreign_keys=None, 115 extension=None, 116 order_by=False, 117 always_refresh=False, 118 version_id_col=None, 119 version_id_generator=None, 120 polymorphic_on=None, 121 _polymorphic_map=None, 122 polymorphic_identity=None, 123 concrete=False, 124 with_polymorphic=None, 125 polymorphic_load=None, 126 allow_partial_pks=True, 127 batch=True, 128 column_prefix=None, 129 include_properties=None, 130 exclude_properties=None, 131 passive_updates=True, 132 passive_deletes=False, 133 confirm_deleted_rows=True, 134 eager_defaults=False, 135 legacy_is_orphan=False, 136 _compiled_cache_size=100, 137 ): 138 r"""Return a new :class:`~.Mapper` object. 139 140 This function is typically used behind the scenes 141 via the Declarative extension. When using Declarative, 142 many of the usual :func:`.mapper` arguments are handled 143 by the Declarative extension itself, including ``class_``, 144 ``local_table``, ``properties``, and ``inherits``. 145 Other options are passed to :func:`.mapper` using 146 the ``__mapper_args__`` class variable:: 147 148 class MyClass(Base): 149 __tablename__ = 'my_table' 150 id = Column(Integer, primary_key=True) 151 type = Column(String(50)) 152 alt = Column("some_alt", Integer) 153 154 __mapper_args__ = { 155 'polymorphic_on' : type 156 } 157 158 159 Explicit use of :func:`.mapper` 160 is often referred to as *classical mapping*. The above 161 declarative example is equivalent in classical form to:: 162 163 my_table = Table("my_table", metadata, 164 Column('id', Integer, primary_key=True), 165 Column('type', String(50)), 166 Column("some_alt", Integer) 167 ) 168 169 class MyClass(object): 170 pass 171 172 mapper(MyClass, my_table, 173 polymorphic_on=my_table.c.type, 174 properties={ 175 'alt':my_table.c.some_alt 176 }) 177 178 .. seealso:: 179 180 :ref:`classical_mapping` - discussion of direct usage of 181 :func:`.mapper` 182 183 :param class\_: The class to be mapped. When using Declarative, 184 this argument is automatically passed as the declared class 185 itself. 186 187 :param local_table: The :class:`.Table` or other selectable 188 to which the class is mapped. May be ``None`` if 189 this mapper inherits from another mapper using single-table 190 inheritance. When using Declarative, this argument is 191 automatically passed by the extension, based on what 192 is configured via the ``__table__`` argument or via the 193 :class:`.Table` produced as a result of the ``__tablename__`` 194 and :class:`.Column` arguments present. 195 196 :param always_refresh: If True, all query operations for this mapped 197 class will overwrite all data within object instances that already 198 exist within the session, erasing any in-memory changes with 199 whatever information was loaded from the database. Usage of this 200 flag is highly discouraged; as an alternative, see the method 201 :meth:`.Query.populate_existing`. 202 203 :param allow_partial_pks: Defaults to True. Indicates that a 204 composite primary key with some NULL values should be considered as 205 possibly existing within the database. This affects whether a 206 mapper will assign an incoming row to an existing identity, as well 207 as if :meth:`.Session.merge` will check the database first for a 208 particular primary key value. A "partial primary key" can occur if 209 one has mapped to an OUTER JOIN, for example. 210 211 :param batch: Defaults to ``True``, indicating that save operations 212 of multiple entities can be batched together for efficiency. 213 Setting to False indicates 214 that an instance will be fully saved before saving the next 215 instance. This is used in the extremely rare case that a 216 :class:`.MapperEvents` listener requires being called 217 in between individual row persistence operations. 218 219 :param column_prefix: A string which will be prepended 220 to the mapped attribute name when :class:`.Column` 221 objects are automatically assigned as attributes to the 222 mapped class. Does not affect explicitly specified 223 column-based properties. 224 225 See the section :ref:`column_prefix` for an example. 226 227 :param concrete: If True, indicates this mapper should use concrete 228 table inheritance with its parent mapper. 229 230 See the section :ref:`concrete_inheritance` for an example. 231 232 :param confirm_deleted_rows: defaults to True; when a DELETE occurs 233 of one more rows based on specific primary keys, a warning is 234 emitted when the number of rows matched does not equal the number 235 of rows expected. This parameter may be set to False to handle the 236 case where database ON DELETE CASCADE rules may be deleting some of 237 those rows automatically. The warning may be changed to an 238 exception in a future release. 239 240 .. versionadded:: 0.9.4 - added 241 :paramref:`.mapper.confirm_deleted_rows` as well as conditional 242 matched row checking on delete. 243 244 :param eager_defaults: if True, the ORM will immediately fetch the 245 value of server-generated default values after an INSERT or UPDATE, 246 rather than leaving them as expired to be fetched on next access. 247 This can be used for event schemes where the server-generated values 248 are needed immediately before the flush completes. By default, 249 this scheme will emit an individual ``SELECT`` statement per row 250 inserted or updated, which note can add significant performance 251 overhead. However, if the 252 target database supports :term:`RETURNING`, the default values will 253 be returned inline with the INSERT or UPDATE statement, which can 254 greatly enhance performance for an application that needs frequent 255 access to just-generated server defaults. 256 257 .. seealso:: 258 259 :ref:`orm_server_defaults` 260 261 .. versionchanged:: 0.9.0 The ``eager_defaults`` option can now 262 make use of :term:`RETURNING` for backends which support it. 263 264 :param exclude_properties: A list or set of string column names to 265 be excluded from mapping. 266 267 See :ref:`include_exclude_cols` for an example. 268 269 :param extension: A :class:`.MapperExtension` instance or 270 list of :class:`.MapperExtension` instances which will be applied 271 to all operations by this :class:`.Mapper`. 272 273 .. deprecated:: 0.7 274 275 :class:`.MapperExtension` is deprecated in favor of the 276 :class:`.MapperEvents` listener interface. The 277 :paramref:`.mapper.extension` parameter will be 278 removed in a future release. 279 280 :param include_properties: An inclusive list or set of string column 281 names to map. 282 283 See :ref:`include_exclude_cols` for an example. 284 285 :param inherits: A mapped class or the corresponding :class:`.Mapper` 286 of one indicating a superclass to which this :class:`.Mapper` 287 should *inherit* from. The mapped class here must be a subclass 288 of the other mapper's class. When using Declarative, this argument 289 is passed automatically as a result of the natural class 290 hierarchy of the declared classes. 291 292 .. seealso:: 293 294 :ref:`inheritance_toplevel` 295 296 :param inherit_condition: For joined table inheritance, a SQL 297 expression which will 298 define how the two tables are joined; defaults to a natural join 299 between the two tables. 300 301 :param inherit_foreign_keys: When ``inherit_condition`` is used and 302 the columns present are missing a :class:`.ForeignKey` 303 configuration, this parameter can be used to specify which columns 304 are "foreign". In most cases can be left as ``None``. 305 306 :param legacy_is_orphan: Boolean, defaults to ``False``. 307 When ``True``, specifies that "legacy" orphan consideration 308 is to be applied to objects mapped by this mapper, which means 309 that a pending (that is, not persistent) object is auto-expunged 310 from an owning :class:`.Session` only when it is de-associated 311 from *all* parents that specify a ``delete-orphan`` cascade towards 312 this mapper. The new default behavior is that the object is 313 auto-expunged when it is de-associated with *any* of its parents 314 that specify ``delete-orphan`` cascade. This behavior is more 315 consistent with that of a persistent object, and allows behavior to 316 be consistent in more scenarios independently of whether or not an 317 orphanable object has been flushed yet or not. 318 319 See the change note and example at :ref:`legacy_is_orphan_addition` 320 for more detail on this change. 321 322 :param non_primary: Specify that this :class:`.Mapper` is in addition 323 to the "primary" mapper, that is, the one used for persistence. 324 The :class:`.Mapper` created here may be used for ad-hoc 325 mapping of the class to an alternate selectable, for loading 326 only. 327 328 :paramref:`.Mapper.non_primary` is not an often used option, but 329 is useful in some specific :func:`.relationship` cases. 330 331 .. seealso:: 332 333 :ref:`relationship_non_primary_mapper` 334 335 :param order_by: A single :class:`.Column` or list of :class:`.Column` 336 objects for which selection operations should use as the default 337 ordering for entities. By default mappers have no pre-defined 338 ordering. 339 340 .. deprecated:: 1.1 The :paramref:`.Mapper.order_by` parameter 341 is deprecated, and will be removed in a future release. 342 Use :meth:`.Query.order_by` to determine the ordering of a 343 result set. 344 345 :param passive_deletes: Indicates DELETE behavior of foreign key 346 columns when a joined-table inheritance entity is being deleted. 347 Defaults to ``False`` for a base mapper; for an inheriting mapper, 348 defaults to ``False`` unless the value is set to ``True`` 349 on the superclass mapper. 350 351 When ``True``, it is assumed that ON DELETE CASCADE is configured 352 on the foreign key relationships that link this mapper's table 353 to its superclass table, so that when the unit of work attempts 354 to delete the entity, it need only emit a DELETE statement for the 355 superclass table, and not this table. 356 357 When ``False``, a DELETE statement is emitted for this mapper's 358 table individually. If the primary key attributes local to this 359 table are unloaded, then a SELECT must be emitted in order to 360 validate these attributes; note that the primary key columns 361 of a joined-table subclass are not part of the "primary key" of 362 the object as a whole. 363 364 Note that a value of ``True`` is **always** forced onto the 365 subclass mappers; that is, it's not possible for a superclass 366 to specify passive_deletes without this taking effect for 367 all subclass mappers. 368 369 .. versionadded:: 1.1 370 371 .. seealso:: 372 373 :ref:`passive_deletes` - description of similar feature as 374 used with :func:`.relationship` 375 376 :paramref:`.mapper.passive_updates` - supporting ON UPDATE 377 CASCADE for joined-table inheritance mappers 378 379 :param passive_updates: Indicates UPDATE behavior of foreign key 380 columns when a primary key column changes on a joined-table 381 inheritance mapping. Defaults to ``True``. 382 383 When True, it is assumed that ON UPDATE CASCADE is configured on 384 the foreign key in the database, and that the database will handle 385 propagation of an UPDATE from a source column to dependent columns 386 on joined-table rows. 387 388 When False, it is assumed that the database does not enforce 389 referential integrity and will not be issuing its own CASCADE 390 operation for an update. The unit of work process will 391 emit an UPDATE statement for the dependent columns during a 392 primary key change. 393 394 .. seealso:: 395 396 :ref:`passive_updates` - description of a similar feature as 397 used with :func:`.relationship` 398 399 :paramref:`.mapper.passive_deletes` - supporting ON DELETE 400 CASCADE for joined-table inheritance mappers 401 402 :param polymorphic_load: Specifies "polymorphic loading" behavior 403 for a subclass in an inheritance hierarchy (joined and single 404 table inheritance only). Valid values are: 405 406 * "'inline'" - specifies this class should be part of the 407 "with_polymorphic" mappers, e.g. its columns will be included 408 in a SELECT query against the base. 409 410 * "'selectin'" - specifies that when instances of this class 411 are loaded, an additional SELECT will be emitted to retrieve 412 the columns specific to this subclass. The SELECT uses 413 IN to fetch multiple subclasses at once. 414 415 .. versionadded:: 1.2 416 417 .. seealso:: 418 419 :ref:`with_polymorphic_mapper_config` 420 421 :ref:`polymorphic_selectin` 422 423 :param polymorphic_on: Specifies the column, attribute, or 424 SQL expression used to determine the target class for an 425 incoming row, when inheriting classes are present. 426 427 This value is commonly a :class:`.Column` object that's 428 present in the mapped :class:`.Table`:: 429 430 class Employee(Base): 431 __tablename__ = 'employee' 432 433 id = Column(Integer, primary_key=True) 434 discriminator = Column(String(50)) 435 436 __mapper_args__ = { 437 "polymorphic_on":discriminator, 438 "polymorphic_identity":"employee" 439 } 440 441 It may also be specified 442 as a SQL expression, as in this example where we 443 use the :func:`.case` construct to provide a conditional 444 approach:: 445 446 class Employee(Base): 447 __tablename__ = 'employee' 448 449 id = Column(Integer, primary_key=True) 450 discriminator = Column(String(50)) 451 452 __mapper_args__ = { 453 "polymorphic_on":case([ 454 (discriminator == "EN", "engineer"), 455 (discriminator == "MA", "manager"), 456 ], else_="employee"), 457 "polymorphic_identity":"employee" 458 } 459 460 It may also refer to any attribute 461 configured with :func:`.column_property`, or to the 462 string name of one:: 463 464 class Employee(Base): 465 __tablename__ = 'employee' 466 467 id = Column(Integer, primary_key=True) 468 discriminator = Column(String(50)) 469 employee_type = column_property( 470 case([ 471 (discriminator == "EN", "engineer"), 472 (discriminator == "MA", "manager"), 473 ], else_="employee") 474 ) 475 476 __mapper_args__ = { 477 "polymorphic_on":employee_type, 478 "polymorphic_identity":"employee" 479 } 480 481 When setting ``polymorphic_on`` to reference an 482 attribute or expression that's not present in the 483 locally mapped :class:`.Table`, yet the value 484 of the discriminator should be persisted to the database, 485 the value of the 486 discriminator is not automatically set on new 487 instances; this must be handled by the user, 488 either through manual means or via event listeners. 489 A typical approach to establishing such a listener 490 looks like:: 491 492 from sqlalchemy import event 493 from sqlalchemy.orm import object_mapper 494 495 @event.listens_for(Employee, "init", propagate=True) 496 def set_identity(instance, *arg, **kw): 497 mapper = object_mapper(instance) 498 instance.discriminator = mapper.polymorphic_identity 499 500 Where above, we assign the value of ``polymorphic_identity`` 501 for the mapped class to the ``discriminator`` attribute, 502 thus persisting the value to the ``discriminator`` column 503 in the database. 504 505 .. warning:: 506 507 Currently, **only one discriminator column may be set**, typically 508 on the base-most class in the hierarchy. "Cascading" polymorphic 509 columns are not yet supported. 510 511 .. seealso:: 512 513 :ref:`inheritance_toplevel` 514 515 :param polymorphic_identity: Specifies the value which 516 identifies this particular class as returned by the 517 column expression referred to by the ``polymorphic_on`` 518 setting. As rows are received, the value corresponding 519 to the ``polymorphic_on`` column expression is compared 520 to this value, indicating which subclass should 521 be used for the newly reconstructed object. 522 523 :param properties: A dictionary mapping the string names of object 524 attributes to :class:`.MapperProperty` instances, which define the 525 persistence behavior of that attribute. Note that :class:`.Column` 526 objects present in 527 the mapped :class:`.Table` are automatically placed into 528 ``ColumnProperty`` instances upon mapping, unless overridden. 529 When using Declarative, this argument is passed automatically, 530 based on all those :class:`.MapperProperty` instances declared 531 in the declared class body. 532 533 :param primary_key: A list of :class:`.Column` objects which define 534 the primary key to be used against this mapper's selectable unit. 535 This is normally simply the primary key of the ``local_table``, but 536 can be overridden here. 537 538 :param version_id_col: A :class:`.Column` 539 that will be used to keep a running version id of rows 540 in the table. This is used to detect concurrent updates or 541 the presence of stale data in a flush. The methodology is to 542 detect if an UPDATE statement does not match the last known 543 version id, a 544 :class:`~sqlalchemy.orm.exc.StaleDataError` exception is 545 thrown. 546 By default, the column must be of :class:`.Integer` type, 547 unless ``version_id_generator`` specifies an alternative version 548 generator. 549 550 .. seealso:: 551 552 :ref:`mapper_version_counter` - discussion of version counting 553 and rationale. 554 555 :param version_id_generator: Define how new version ids should 556 be generated. Defaults to ``None``, which indicates that 557 a simple integer counting scheme be employed. To provide a custom 558 versioning scheme, provide a callable function of the form:: 559 560 def generate_version(version): 561 return next_version 562 563 Alternatively, server-side versioning functions such as triggers, 564 or programmatic versioning schemes outside of the version id 565 generator may be used, by specifying the value ``False``. 566 Please see :ref:`server_side_version_counter` for a discussion 567 of important points when using this option. 568 569 .. versionadded:: 0.9.0 ``version_id_generator`` supports 570 server-side version number generation. 571 572 .. seealso:: 573 574 :ref:`custom_version_counter` 575 576 :ref:`server_side_version_counter` 577 578 579 :param with_polymorphic: A tuple in the form ``(<classes>, 580 <selectable>)`` indicating the default style of "polymorphic" 581 loading, that is, which tables are queried at once. <classes> is 582 any single or list of mappers and/or classes indicating the 583 inherited classes that should be loaded at once. The special value 584 ``'*'`` may be used to indicate all descending classes should be 585 loaded immediately. The second tuple argument <selectable> 586 indicates a selectable that will be used to query for multiple 587 classes. 588 589 .. seealso:: 590 591 :ref:`with_polymorphic` - discussion of polymorphic querying 592 techniques. 593 594 """ 595 596 self.class_ = util.assert_arg_type(class_, type, "class_") 597 598 self.class_manager = None 599 600 self._primary_key_argument = util.to_list(primary_key) 601 self.non_primary = non_primary 602 603 if order_by is not False: 604 self.order_by = util.to_list(order_by) 605 util.warn_deprecated( 606 "Mapper.order_by is deprecated." 607 "Use Query.order_by() in order to affect the ordering of ORM " 608 "result sets." 609 ) 610 611 else: 612 self.order_by = order_by 613 614 self.always_refresh = always_refresh 615 616 if isinstance(version_id_col, MapperProperty): 617 self.version_id_prop = version_id_col 618 self.version_id_col = None 619 else: 620 self.version_id_col = version_id_col 621 if version_id_generator is False: 622 self.version_id_generator = False 623 elif version_id_generator is None: 624 self.version_id_generator = lambda x: (x or 0) + 1 625 else: 626 self.version_id_generator = version_id_generator 627 628 self.concrete = concrete 629 self.single = False 630 self.inherits = inherits 631 self.local_table = local_table 632 self.inherit_condition = inherit_condition 633 self.inherit_foreign_keys = inherit_foreign_keys 634 self._init_properties = properties or {} 635 self._delete_orphans = [] 636 self.batch = batch 637 self.eager_defaults = eager_defaults 638 self.column_prefix = column_prefix 639 self.polymorphic_on = expression._clause_element_as_expr( 640 polymorphic_on 641 ) 642 self._dependency_processors = [] 643 self.validators = util.immutabledict() 644 self.passive_updates = passive_updates 645 self.passive_deletes = passive_deletes 646 self.legacy_is_orphan = legacy_is_orphan 647 self._clause_adapter = None 648 self._requires_row_aliasing = False 649 self._inherits_equated_pairs = None 650 self._memoized_values = {} 651 self._compiled_cache_size = _compiled_cache_size 652 self._reconstructor = None 653 self._deprecated_extensions = util.to_list(extension or []) 654 self.allow_partial_pks = allow_partial_pks 655 656 if self.inherits and not self.concrete: 657 self.confirm_deleted_rows = False 658 else: 659 self.confirm_deleted_rows = confirm_deleted_rows 660 661 if isinstance(self.local_table, expression.SelectBase): 662 raise sa_exc.InvalidRequestError( 663 "When mapping against a select() construct, map against " 664 "an alias() of the construct instead." 665 "This because several databases don't allow a " 666 "SELECT from a subquery that does not have an alias." 667 ) 668 669 self._set_with_polymorphic(with_polymorphic) 670 self.polymorphic_load = polymorphic_load 671 672 # our 'polymorphic identity', a string name that when located in a 673 # result set row indicates this Mapper should be used to construct 674 # the object instance for that row. 675 self.polymorphic_identity = polymorphic_identity 676 677 # a dictionary of 'polymorphic identity' names, associating those 678 # names with Mappers that will be used to construct object instances 679 # upon a select operation. 680 if _polymorphic_map is None: 681 self.polymorphic_map = {} 682 else: 683 self.polymorphic_map = _polymorphic_map 684 685 if include_properties is not None: 686 self.include_properties = util.to_set(include_properties) 687 else: 688 self.include_properties = None 689 if exclude_properties: 690 self.exclude_properties = util.to_set(exclude_properties) 691 else: 692 self.exclude_properties = None 693 694 self.configured = False 695 696 # prevent this mapper from being constructed 697 # while a configure_mappers() is occurring (and defer a 698 # configure_mappers() until construction succeeds) 699 _CONFIGURE_MUTEX.acquire() 700 try: 701 self.dispatch._events._new_mapper_instance(class_, self) 702 self._configure_inheritance() 703 self._configure_legacy_instrument_class() 704 self._configure_class_instrumentation() 705 self._configure_listeners() 706 self._configure_properties() 707 self._configure_polymorphic_setter() 708 self._configure_pks() 709 Mapper._new_mappers = True 710 self._log("constructed") 711 self._expire_memoizations() 712 finally: 713 _CONFIGURE_MUTEX.release() 714 715 # major attributes initialized at the classlevel so that 716 # they can be Sphinx-documented. 717 718 is_mapper = True 719 """Part of the inspection API.""" 720 721 represents_outer_join = False 722 723 @property 724 def mapper(self): 725 """Part of the inspection API. 726 727 Returns self. 728 729 """ 730 return self 731 732 @property 733 def entity(self): 734 r"""Part of the inspection API. 735 736 Returns self.class\_. 737 738 """ 739 return self.class_ 740 741 local_table = None 742 """The :class:`.Selectable` which this :class:`.Mapper` manages. 743 744 Typically is an instance of :class:`.Table` or :class:`.Alias`. 745 May also be ``None``. 746 747 The "local" table is the 748 selectable that the :class:`.Mapper` is directly responsible for 749 managing from an attribute access and flush perspective. For 750 non-inheriting mappers, the local table is the same as the 751 "mapped" table. For joined-table inheritance mappers, local_table 752 will be the particular sub-table of the overall "join" which 753 this :class:`.Mapper` represents. If this mapper is a 754 single-table inheriting mapper, local_table will be ``None``. 755 756 .. seealso:: 757 758 :attr:`~.Mapper.mapped_table`. 759 760 """ 761 762 mapped_table = None 763 """The :class:`.Selectable` to which this :class:`.Mapper` is mapped. 764 765 Typically an instance of :class:`.Table`, :class:`.Join`, or 766 :class:`.Alias`. 767 768 The "mapped" table is the selectable that 769 the mapper selects from during queries. For non-inheriting 770 mappers, the mapped table is the same as the "local" table. 771 For joined-table inheritance mappers, mapped_table references the 772 full :class:`.Join` representing full rows for this particular 773 subclass. For single-table inheritance mappers, mapped_table 774 references the base table. 775 776 .. seealso:: 777 778 :attr:`~.Mapper.local_table`. 779 780 """ 781 782 inherits = None 783 """References the :class:`.Mapper` which this :class:`.Mapper` 784 inherits from, if any. 785 786 This is a *read only* attribute determined during mapper construction. 787 Behavior is undefined if directly modified. 788 789 """ 790 791 configured = None 792 """Represent ``True`` if this :class:`.Mapper` has been configured. 793 794 This is a *read only* attribute determined during mapper construction. 795 Behavior is undefined if directly modified. 796 797 .. seealso:: 798 799 :func:`.configure_mappers`. 800 801 """ 802 803 concrete = None 804 """Represent ``True`` if this :class:`.Mapper` is a concrete 805 inheritance mapper. 806 807 This is a *read only* attribute determined during mapper construction. 808 Behavior is undefined if directly modified. 809 810 """ 811 812 tables = None 813 """An iterable containing the collection of :class:`.Table` objects 814 which this :class:`.Mapper` is aware of. 815 816 If the mapper is mapped to a :class:`.Join`, or an :class:`.Alias` 817 representing a :class:`.Select`, the individual :class:`.Table` 818 objects that comprise the full construct will be represented here. 819 820 This is a *read only* attribute determined during mapper construction. 821 Behavior is undefined if directly modified. 822 823 """ 824 825 primary_key = None 826 """An iterable containing the collection of :class:`.Column` objects 827 which comprise the 'primary key' of the mapped table, from the 828 perspective of this :class:`.Mapper`. 829 830 This list is against the selectable in :attr:`~.Mapper.mapped_table`. In 831 the case of inheriting mappers, some columns may be managed by a 832 superclass mapper. For example, in the case of a :class:`.Join`, the 833 primary key is determined by all of the primary key columns across all 834 tables referenced by the :class:`.Join`. 835 836 The list is also not necessarily the same as the primary key column 837 collection associated with the underlying tables; the :class:`.Mapper` 838 features a ``primary_key`` argument that can override what the 839 :class:`.Mapper` considers as primary key columns. 840 841 This is a *read only* attribute determined during mapper construction. 842 Behavior is undefined if directly modified. 843 844 """ 845 846 class_ = None 847 """The Python class which this :class:`.Mapper` maps. 848 849 This is a *read only* attribute determined during mapper construction. 850 Behavior is undefined if directly modified. 851 852 """ 853 854 class_manager = None 855 """The :class:`.ClassManager` which maintains event listeners 856 and class-bound descriptors for this :class:`.Mapper`. 857 858 This is a *read only* attribute determined during mapper construction. 859 Behavior is undefined if directly modified. 860 861 """ 862 863 single = None 864 """Represent ``True`` if this :class:`.Mapper` is a single table 865 inheritance mapper. 866 867 :attr:`~.Mapper.local_table` will be ``None`` if this flag is set. 868 869 This is a *read only* attribute determined during mapper construction. 870 Behavior is undefined if directly modified. 871 872 """ 873 874 non_primary = None 875 """Represent ``True`` if this :class:`.Mapper` is a "non-primary" 876 mapper, e.g. a mapper that is used only to select rows but not for 877 persistence management. 878 879 This is a *read only* attribute determined during mapper construction. 880 Behavior is undefined if directly modified. 881 882 """ 883 884 polymorphic_on = None 885 """The :class:`.Column` or SQL expression specified as the 886 ``polymorphic_on`` argument 887 for this :class:`.Mapper`, within an inheritance scenario. 888 889 This attribute is normally a :class:`.Column` instance but 890 may also be an expression, such as one derived from 891 :func:`.cast`. 892 893 This is a *read only* attribute determined during mapper construction. 894 Behavior is undefined if directly modified. 895 896 """ 897 898 polymorphic_map = None 899 """A mapping of "polymorphic identity" identifiers mapped to 900 :class:`.Mapper` instances, within an inheritance scenario. 901 902 The identifiers can be of any type which is comparable to the 903 type of column represented by :attr:`~.Mapper.polymorphic_on`. 904 905 An inheritance chain of mappers will all reference the same 906 polymorphic map object. The object is used to correlate incoming 907 result rows to target mappers. 908 909 This is a *read only* attribute determined during mapper construction. 910 Behavior is undefined if directly modified. 911 912 """ 913 914 polymorphic_identity = None 915 """Represent an identifier which is matched against the 916 :attr:`~.Mapper.polymorphic_on` column during result row loading. 917 918 Used only with inheritance, this object can be of any type which is 919 comparable to the type of column represented by 920 :attr:`~.Mapper.polymorphic_on`. 921 922 This is a *read only* attribute determined during mapper construction. 923 Behavior is undefined if directly modified. 924 925 """ 926 927 base_mapper = None 928 """The base-most :class:`.Mapper` in an inheritance chain. 929 930 In a non-inheriting scenario, this attribute will always be this 931 :class:`.Mapper`. In an inheritance scenario, it references 932 the :class:`.Mapper` which is parent to all other :class:`.Mapper` 933 objects in the inheritance chain. 934 935 This is a *read only* attribute determined during mapper construction. 936 Behavior is undefined if directly modified. 937 938 """ 939 940 columns = None 941 """A collection of :class:`.Column` or other scalar expression 942 objects maintained by this :class:`.Mapper`. 943 944 The collection behaves the same as that of the ``c`` attribute on 945 any :class:`.Table` object, except that only those columns included in 946 this mapping are present, and are keyed based on the attribute name 947 defined in the mapping, not necessarily the ``key`` attribute of the 948 :class:`.Column` itself. Additionally, scalar expressions mapped 949 by :func:`.column_property` are also present here. 950 951 This is a *read only* attribute determined during mapper construction. 952 Behavior is undefined if directly modified. 953 954 """ 955 956 validators = None 957 """An immutable dictionary of attributes which have been decorated 958 using the :func:`~.orm.validates` decorator. 959 960 The dictionary contains string attribute names as keys 961 mapped to the actual validation method. 962 963 """ 964 965 c = None 966 """A synonym for :attr:`~.Mapper.columns`.""" 967 968 @util.memoized_property 969 def _path_registry(self): 970 return PathRegistry.per_mapper(self) 971 972 def _configure_inheritance(self): 973 """Configure settings related to inheriting and/or inherited mappers 974 being present.""" 975 976 # a set of all mappers which inherit from this one. 977 self._inheriting_mappers = util.WeakSequence() 978 979 if self.inherits: 980 if isinstance(self.inherits, type): 981 self.inherits = class_mapper(self.inherits, configure=False) 982 if not issubclass(self.class_, self.inherits.class_): 983 raise sa_exc.ArgumentError( 984 "Class '%s' does not inherit from '%s'" 985 % (self.class_.__name__, self.inherits.class_.__name__) 986 ) 987 if self.non_primary != self.inherits.non_primary: 988 np = not self.non_primary and "primary" or "non-primary" 989 raise sa_exc.ArgumentError( 990 "Inheritance of %s mapper for class '%s' is " 991 "only allowed from a %s mapper" 992 % (np, self.class_.__name__, np) 993 ) 994 # inherit_condition is optional. 995 if self.local_table is None: 996 self.local_table = self.inherits.local_table 997 self.mapped_table = self.inherits.mapped_table 998 self.single = True 999 elif self.local_table is not self.inherits.local_table: 1000 if self.concrete: 1001 self.mapped_table = self.local_table 1002 for mapper in self.iterate_to_root(): 1003 if mapper.polymorphic_on is not None: 1004 mapper._requires_row_aliasing = True 1005 else: 1006 if self.inherit_condition is None: 1007 # figure out inherit condition from our table to the 1008 # immediate table of the inherited mapper, not its 1009 # full table which could pull in other stuff we don't 1010 # want (allows test/inheritance.InheritTest4 to pass) 1011 self.inherit_condition = sql_util.join_condition( 1012 self.inherits.local_table, self.local_table 1013 ) 1014 self.mapped_table = sql.join( 1015 self.inherits.mapped_table, 1016 self.local_table, 1017 self.inherit_condition, 1018 ) 1019 1020 fks = util.to_set(self.inherit_foreign_keys) 1021 self._inherits_equated_pairs = sql_util.criterion_as_pairs( 1022 self.mapped_table.onclause, 1023 consider_as_foreign_keys=fks, 1024 ) 1025 else: 1026 self.mapped_table = self.local_table 1027 1028 if self.polymorphic_identity is not None and not self.concrete: 1029 self._identity_class = self.inherits._identity_class 1030 else: 1031 self._identity_class = self.class_ 1032 1033 if self.version_id_col is None: 1034 self.version_id_col = self.inherits.version_id_col 1035 self.version_id_generator = self.inherits.version_id_generator 1036 elif ( 1037 self.inherits.version_id_col is not None 1038 and self.version_id_col is not self.inherits.version_id_col 1039 ): 1040 util.warn( 1041 "Inheriting version_id_col '%s' does not match inherited " 1042 "version_id_col '%s' and will not automatically populate " 1043 "the inherited versioning column. " 1044 "version_id_col should only be specified on " 1045 "the base-most mapper that includes versioning." 1046 % ( 1047 self.version_id_col.description, 1048 self.inherits.version_id_col.description, 1049 ) 1050 ) 1051 1052 if ( 1053 self.order_by is False 1054 and not self.concrete 1055 and self.inherits.order_by is not False 1056 ): 1057 self.order_by = self.inherits.order_by 1058 1059 self.polymorphic_map = self.inherits.polymorphic_map 1060 self.batch = self.inherits.batch 1061 self.inherits._inheriting_mappers.append(self) 1062 self.base_mapper = self.inherits.base_mapper 1063 self.passive_updates = self.inherits.passive_updates 1064 self.passive_deletes = ( 1065 self.inherits.passive_deletes or self.passive_deletes 1066 ) 1067 self._all_tables = self.inherits._all_tables 1068 1069 if self.polymorphic_identity is not None: 1070 if self.polymorphic_identity in self.polymorphic_map: 1071 util.warn( 1072 "Reassigning polymorphic association for identity %r " 1073 "from %r to %r: Check for duplicate use of %r as " 1074 "value for polymorphic_identity." 1075 % ( 1076 self.polymorphic_identity, 1077 self.polymorphic_map[self.polymorphic_identity], 1078 self, 1079 self.polymorphic_identity, 1080 ) 1081 ) 1082 self.polymorphic_map[self.polymorphic_identity] = self 1083 1084 if self.polymorphic_load and self.concrete: 1085 raise sa_exc.ArgumentError( 1086 "polymorphic_load is not currently supported " 1087 "with concrete table inheritance" 1088 ) 1089 if self.polymorphic_load == "inline": 1090 self.inherits._add_with_polymorphic_subclass(self) 1091 elif self.polymorphic_load == "selectin": 1092 pass 1093 elif self.polymorphic_load is not None: 1094 raise sa_exc.ArgumentError( 1095 "unknown argument for polymorphic_load: %r" 1096 % self.polymorphic_load 1097 ) 1098 1099 else: 1100 self._all_tables = set() 1101 self.base_mapper = self 1102 self.mapped_table = self.local_table 1103 if self.polymorphic_identity is not None: 1104 self.polymorphic_map[self.polymorphic_identity] = self 1105 self._identity_class = self.class_ 1106 1107 if self.mapped_table is None: 1108 raise sa_exc.ArgumentError( 1109 "Mapper '%s' does not have a mapped_table specified." % self 1110 ) 1111 1112 def _set_with_polymorphic(self, with_polymorphic): 1113 if with_polymorphic == "*": 1114 self.with_polymorphic = ("*", None) 1115 elif isinstance(with_polymorphic, (tuple, list)): 1116 if isinstance( 1117 with_polymorphic[0], util.string_types + (tuple, list) 1118 ): 1119 self.with_polymorphic = with_polymorphic 1120 else: 1121 self.with_polymorphic = (with_polymorphic, None) 1122 elif with_polymorphic is not None: 1123 raise sa_exc.ArgumentError("Invalid setting for with_polymorphic") 1124 else: 1125 self.with_polymorphic = None 1126 1127 if isinstance(self.local_table, expression.SelectBase): 1128 raise sa_exc.InvalidRequestError( 1129 "When mapping against a select() construct, map against " 1130 "an alias() of the construct instead." 1131 "This because several databases don't allow a " 1132 "SELECT from a subquery that does not have an alias." 1133 ) 1134 1135 if self.with_polymorphic and isinstance( 1136 self.with_polymorphic[1], expression.SelectBase 1137 ): 1138 self.with_polymorphic = ( 1139 self.with_polymorphic[0], 1140 self.with_polymorphic[1].alias(), 1141 ) 1142 1143 if self.configured: 1144 self._expire_memoizations() 1145 1146 def _add_with_polymorphic_subclass(self, mapper): 1147 subcl = mapper.class_ 1148 if self.with_polymorphic is None: 1149 self._set_with_polymorphic((subcl,)) 1150 elif self.with_polymorphic[0] != "*": 1151 self._set_with_polymorphic( 1152 (self.with_polymorphic[0] + (subcl,), self.with_polymorphic[1]) 1153 ) 1154 1155 def _set_concrete_base(self, mapper): 1156 """Set the given :class:`.Mapper` as the 'inherits' for this 1157 :class:`.Mapper`, assuming this :class:`.Mapper` is concrete 1158 and does not already have an inherits.""" 1159 1160 assert self.concrete 1161 assert not self.inherits 1162 assert isinstance(mapper, Mapper) 1163 self.inherits = mapper 1164 self.inherits.polymorphic_map.update(self.polymorphic_map) 1165 self.polymorphic_map = self.inherits.polymorphic_map 1166 for mapper in self.iterate_to_root(): 1167 if mapper.polymorphic_on is not None: 1168 mapper._requires_row_aliasing = True 1169 self.batch = self.inherits.batch 1170 for mp in self.self_and_descendants: 1171 mp.base_mapper = self.inherits.base_mapper 1172 self.inherits._inheriting_mappers.append(self) 1173 self.passive_updates = self.inherits.passive_updates 1174 self._all_tables = self.inherits._all_tables 1175 1176 for key, prop in mapper._props.items(): 1177 if key not in self._props and not self._should_exclude( 1178 key, key, local=False, column=None 1179 ): 1180 self._adapt_inherited_property(key, prop, False) 1181 1182 def _set_polymorphic_on(self, polymorphic_on): 1183 self.polymorphic_on = polymorphic_on 1184 self._configure_polymorphic_setter(True) 1185 1186 def _configure_legacy_instrument_class(self): 1187 1188 if self.inherits: 1189 self.dispatch._update(self.inherits.dispatch) 1190 super_extensions = set( 1191 chain( 1192 *[ 1193 m._deprecated_extensions 1194 for m in self.inherits.iterate_to_root() 1195 ] 1196 ) 1197 ) 1198 else: 1199 super_extensions = set() 1200 1201 for ext in self._deprecated_extensions: 1202 if ext not in super_extensions: 1203 ext._adapt_instrument_class(self, ext) 1204 1205 def _configure_listeners(self): 1206 if self.inherits: 1207 super_extensions = set( 1208 chain( 1209 *[ 1210 m._deprecated_extensions 1211 for m in self.inherits.iterate_to_root() 1212 ] 1213 ) 1214 ) 1215 else: 1216 super_extensions = set() 1217 1218 for ext in self._deprecated_extensions: 1219 if ext not in super_extensions: 1220 ext._adapt_listener(self, ext) 1221 1222 def _configure_class_instrumentation(self): 1223 """If this mapper is to be a primary mapper (i.e. the 1224 non_primary flag is not set), associate this Mapper with the 1225 given class_ and entity name. 1226 1227 Subsequent calls to ``class_mapper()`` for the class_/entity 1228 name combination will return this mapper. Also decorate the 1229 `__init__` method on the mapped class to include optional 1230 auto-session attachment logic. 1231 1232 """ 1233 1234 manager = attributes.manager_of_class(self.class_) 1235 1236 if self.non_primary: 1237 if not manager or not manager.is_mapped: 1238 raise sa_exc.InvalidRequestError( 1239 "Class %s has no primary mapper configured. Configure " 1240 "a primary mapper first before setting up a non primary " 1241 "Mapper." % self.class_ 1242 ) 1243 self.class_manager = manager 1244 self._identity_class = manager.mapper._identity_class 1245 _mapper_registry[self] = True 1246 return 1247 1248 if manager is not None: 1249 assert manager.class_ is self.class_ 1250 if manager.is_mapped: 1251 raise sa_exc.ArgumentError( 1252 "Class '%s' already has a primary mapper defined. " 1253 "Use non_primary=True to " 1254 "create a non primary Mapper. clear_mappers() will " 1255 "remove *all* current mappers from all classes." 1256 % self.class_ 1257 ) 1258 # else: 1259 # a ClassManager may already exist as 1260 # ClassManager.instrument_attribute() creates 1261 # new managers for each subclass if they don't yet exist. 1262 1263 _mapper_registry[self] = True 1264 1265 # note: this *must be called before instrumentation.register_class* 1266 # to maintain the documented behavior of instrument_class 1267 self.dispatch.instrument_class(self, self.class_) 1268 1269 if manager is None: 1270 manager = instrumentation.register_class(self.class_) 1271 1272 self.class_manager = manager 1273 1274 manager.mapper = self 1275 manager.deferred_scalar_loader = util.partial( 1276 loading.load_scalar_attributes, self 1277 ) 1278 1279 # The remaining members can be added by any mapper, 1280 # e_name None or not. 1281 if manager.info.get(_INSTRUMENTOR, False): 1282 return 1283 1284 event.listen(manager, "first_init", _event_on_first_init, raw=True) 1285 event.listen(manager, "init", _event_on_init, raw=True) 1286 1287 for key, method in util.iterate_attributes(self.class_): 1288 if key == "__init__" and hasattr(method, "_sa_original_init"): 1289 method = method._sa_original_init 1290 if isinstance(method, types.MethodType): 1291 method = method.im_func 1292 if isinstance(method, types.FunctionType): 1293 if hasattr(method, "__sa_reconstructor__"): 1294 self._reconstructor = method 1295 event.listen(manager, "load", _event_on_load, raw=True) 1296 elif hasattr(method, "__sa_validators__"): 1297 validation_opts = method.__sa_validation_opts__ 1298 for name in method.__sa_validators__: 1299 if name in self.validators: 1300 raise sa_exc.InvalidRequestError( 1301 "A validation function for mapped " 1302 "attribute %r on mapper %s already exists." 1303 % (name, self) 1304 ) 1305 self.validators = self.validators.union( 1306 {name: (method, validation_opts)} 1307 ) 1308 1309 manager.info[_INSTRUMENTOR] = self 1310 1311 @classmethod 1312 def _configure_all(cls): 1313 """Class-level path to the :func:`.configure_mappers` call. 1314 """ 1315 configure_mappers() 1316 1317 def dispose(self): 1318 # Disable any attribute-based compilation. 1319 self.configured = True 1320 1321 if hasattr(self, "_configure_failed"): 1322 del self._configure_failed 1323 1324 if ( 1325 not self.non_primary 1326 and self.class_manager is not None 1327 and self.class_manager.is_mapped 1328 and self.class_manager.mapper is self 1329 ): 1330 instrumentation.unregister_class(self.class_) 1331 1332 def _configure_pks(self): 1333 self.tables = sql_util.find_tables(self.mapped_table) 1334 1335 self._pks_by_table = {} 1336 self._cols_by_table = {} 1337 1338 all_cols = util.column_set( 1339 chain(*[col.proxy_set for col in self._columntoproperty]) 1340 ) 1341 1342 pk_cols = util.column_set(c for c in all_cols if c.primary_key) 1343 1344 # identify primary key columns which are also mapped by this mapper. 1345 tables = set(self.tables + [self.mapped_table]) 1346 self._all_tables.update(tables) 1347 for t in tables: 1348 if t.primary_key and pk_cols.issuperset(t.primary_key): 1349 # ordering is important since it determines the ordering of 1350 # mapper.primary_key (and therefore query.get()) 1351 self._pks_by_table[t] = util.ordered_column_set( 1352 t.primary_key 1353 ).intersection(pk_cols) 1354 self._cols_by_table[t] = util.ordered_column_set(t.c).intersection( 1355 all_cols 1356 ) 1357 1358 # if explicit PK argument sent, add those columns to the 1359 # primary key mappings 1360 if self._primary_key_argument: 1361 for k in self._primary_key_argument: 1362 if k.table not in self._pks_by_table: 1363 self._pks_by_table[k.table] = util.OrderedSet() 1364 self._pks_by_table[k.table].add(k) 1365 1366 # otherwise, see that we got a full PK for the mapped table 1367 elif ( 1368 self.mapped_table not in self._pks_by_table 1369 or len(self._pks_by_table[self.mapped_table]) == 0 1370 ): 1371 raise sa_exc.ArgumentError( 1372 "Mapper %s could not assemble any primary " 1373 "key columns for mapped table '%s'" 1374 % (self, self.mapped_table.description) 1375 ) 1376 elif self.local_table not in self._pks_by_table and isinstance( 1377 self.local_table, schema.Table 1378 ): 1379 util.warn( 1380 "Could not assemble any primary " 1381 "keys for locally mapped table '%s' - " 1382 "no rows will be persisted in this Table." 1383 % self.local_table.description 1384 ) 1385 1386 if ( 1387 self.inherits 1388 and not self.concrete 1389 and not self._primary_key_argument 1390 ): 1391 # if inheriting, the "primary key" for this mapper is 1392 # that of the inheriting (unless concrete or explicit) 1393 self.primary_key = self.inherits.primary_key 1394 else: 1395 # determine primary key from argument or mapped_table pks - 1396 # reduce to the minimal set of columns 1397 if self._primary_key_argument: 1398 primary_key = sql_util.reduce_columns( 1399 [ 1400 self.mapped_table.corresponding_column(c) 1401 for c in self._primary_key_argument 1402 ], 1403 ignore_nonexistent_tables=True, 1404 ) 1405 else: 1406 primary_key = sql_util.reduce_columns( 1407 self._pks_by_table[self.mapped_table], 1408 ignore_nonexistent_tables=True, 1409 ) 1410 1411 if len(primary_key) == 0: 1412 raise sa_exc.ArgumentError( 1413 "Mapper %s could not assemble any primary " 1414 "key columns for mapped table '%s'" 1415 % (self, self.mapped_table.description) 1416 ) 1417 1418 self.primary_key = tuple(primary_key) 1419 self._log("Identified primary key columns: %s", primary_key) 1420 1421 # determine cols that aren't expressed within our tables; mark these 1422 # as "read only" properties which are refreshed upon INSERT/UPDATE 1423 self._readonly_props = set( 1424 self._columntoproperty[col] 1425 for col in self._columntoproperty 1426 if self._columntoproperty[col] not in self._identity_key_props 1427 and ( 1428 not hasattr(col, "table") 1429 or col.table not in self._cols_by_table 1430 ) 1431 ) 1432 1433 def _configure_properties(self): 1434 # Column and other ClauseElement objects which are mapped 1435 self.columns = self.c = util.OrderedProperties() 1436 1437 # object attribute names mapped to MapperProperty objects 1438 self._props = util.OrderedDict() 1439 1440 # table columns mapped to lists of MapperProperty objects 1441 # using a list allows a single column to be defined as 1442 # populating multiple object attributes 1443 self._columntoproperty = _ColumnMapping(self) 1444 1445 # load custom properties 1446 if self._init_properties: 1447 for key, prop in self._init_properties.items(): 1448 self._configure_property(key, prop, False) 1449 1450 # pull properties from the inherited mapper if any. 1451 if self.inherits: 1452 for key, prop in self.inherits._props.items(): 1453 if key not in self._props and not self._should_exclude( 1454 key, key, local=False, column=None 1455 ): 1456 self._adapt_inherited_property(key, prop, False) 1457 1458 # create properties for each column in the mapped table, 1459 # for those columns which don't already map to a property 1460 for column in self.mapped_table.columns: 1461 if column in self._columntoproperty: 1462 continue 1463 1464 column_key = (self.column_prefix or "") + column.key 1465 1466 if self._should_exclude( 1467 column.key, 1468 column_key, 1469 local=self.local_table.c.contains_column(column), 1470 column=column, 1471 ): 1472 continue 1473 1474 # adjust the "key" used for this column to that 1475 # of the inheriting mapper 1476 for mapper in self.iterate_to_root(): 1477 if column in mapper._columntoproperty: 1478 column_key = mapper._columntoproperty[column].key 1479 1480 self._configure_property( 1481 column_key, column, init=False, setparent=True 1482 ) 1483 1484 def _configure_polymorphic_setter(self, init=False): 1485 """Configure an attribute on the mapper representing the 1486 'polymorphic_on' column, if applicable, and not 1487 already generated by _configure_properties (which is typical). 1488 1489 Also create a setter function which will assign this 1490 attribute to the value of the 'polymorphic_identity' 1491 upon instance construction, also if applicable. This 1492 routine will run when an instance is created. 1493 1494 """ 1495 setter = False 1496 1497 if self.polymorphic_on is not None: 1498 setter = True 1499 1500 if isinstance(self.polymorphic_on, util.string_types): 1501 # polymorphic_on specified as a string - link 1502 # it to mapped ColumnProperty 1503 try: 1504 self.polymorphic_on = self._props[self.polymorphic_on] 1505 except KeyError: 1506 raise sa_exc.ArgumentError( 1507 "Can't determine polymorphic_on " 1508 "value '%s' - no attribute is " 1509 "mapped to this name." % self.polymorphic_on 1510 ) 1511 1512 if self.polymorphic_on in self._columntoproperty: 1513 # polymorphic_on is a column that is already mapped 1514 # to a ColumnProperty 1515 prop = self._columntoproperty[self.polymorphic_on] 1516 elif isinstance(self.polymorphic_on, MapperProperty): 1517 # polymorphic_on is directly a MapperProperty, 1518 # ensure it's a ColumnProperty 1519 if not isinstance( 1520 self.polymorphic_on, properties.ColumnProperty 1521 ): 1522 raise sa_exc.ArgumentError( 1523 "Only direct column-mapped " 1524 "property or SQL expression " 1525 "can be passed for polymorphic_on" 1526 ) 1527 prop = self.polymorphic_on 1528 elif not expression._is_column(self.polymorphic_on): 1529 # polymorphic_on is not a Column and not a ColumnProperty; 1530 # not supported right now. 1531 raise sa_exc.ArgumentError( 1532 "Only direct column-mapped " 1533 "property or SQL expression " 1534 "can be passed for polymorphic_on" 1535 ) 1536 else: 1537 # polymorphic_on is a Column or SQL expression and 1538 # doesn't appear to be mapped. this means it can be 1. 1539 # only present in the with_polymorphic selectable or 1540 # 2. a totally standalone SQL expression which we'd 1541 # hope is compatible with this mapper's mapped_table 1542 col = self.mapped_table.corresponding_column( 1543 self.polymorphic_on 1544 ) 1545 if col is None: 1546 # polymorphic_on doesn't derive from any 1547 # column/expression isn't present in the mapped 1548 # table. we will make a "hidden" ColumnProperty 1549 # for it. Just check that if it's directly a 1550 # schema.Column and we have with_polymorphic, it's 1551 # likely a user error if the schema.Column isn't 1552 # represented somehow in either mapped_table or 1553 # with_polymorphic. Otherwise as of 0.7.4 we 1554 # just go with it and assume the user wants it 1555 # that way (i.e. a CASE statement) 1556 setter = False 1557 instrument = False 1558 col = self.polymorphic_on 1559 if isinstance(col, schema.Column) and ( 1560 self.with_polymorphic is None 1561 or self.with_polymorphic[1].corresponding_column(col) 1562 is None 1563 ): 1564 raise sa_exc.InvalidRequestError( 1565 "Could not map polymorphic_on column " 1566 "'%s' to the mapped table - polymorphic " 1567 "loads will not function properly" 1568 % col.description 1569 ) 1570 else: 1571 # column/expression that polymorphic_on derives from 1572 # is present in our mapped table 1573 # and is probably mapped, but polymorphic_on itself 1574 # is not. This happens when 1575 # the polymorphic_on is only directly present in the 1576 # with_polymorphic selectable, as when use 1577 # polymorphic_union. 1578 # we'll make a separate ColumnProperty for it. 1579 instrument = True 1580 key = getattr(col, "key", None) 1581 if key: 1582 if self._should_exclude(col.key, col.key, False, col): 1583 raise sa_exc.InvalidRequestError( 1584 "Cannot exclude or override the " 1585 "discriminator column %r" % col.key 1586 ) 1587 else: 1588 self.polymorphic_on = col = col.label("_sa_polymorphic_on") 1589 key = col.key 1590 1591 prop = properties.ColumnProperty(col, _instrument=instrument) 1592 self._configure_property(key, prop, init=init, setparent=True) 1593 1594 # the actual polymorphic_on should be the first public-facing 1595 # column in the property 1596 self.polymorphic_on = prop.columns[0] 1597 polymorphic_key = prop.key 1598 1599 else: 1600 # no polymorphic_on was set. 1601 # check inheriting mappers for one. 1602 for mapper in self.iterate_to_root(): 1603 # determine if polymorphic_on of the parent 1604 # should be propagated here. If the col 1605 # is present in our mapped table, or if our mapped 1606 # table is the same as the parent (i.e. single table 1607 # inheritance), we can use it 1608 if mapper.polymorphic_on is not None: 1609 if self.mapped_table is mapper.mapped_table: 1610 self.polymorphic_on = mapper.polymorphic_on 1611 else: 1612 self.polymorphic_on = ( 1613 self.mapped_table.corresponding_column 1614 )(mapper.polymorphic_on) 1615 # we can use the parent mapper's _set_polymorphic_identity 1616 # directly; it ensures the polymorphic_identity of the 1617 # instance's mapper is used so is portable to subclasses. 1618 if self.polymorphic_on is not None: 1619 self._set_polymorphic_identity = ( 1620 mapper._set_polymorphic_identity 1621 ) 1622 self._validate_polymorphic_identity = ( 1623 mapper._validate_polymorphic_identity 1624 ) 1625 else: 1626 self._set_polymorphic_identity = None 1627 return 1628 1629 if setter: 1630 1631 def _set_polymorphic_identity(state): 1632 dict_ = state.dict 1633 state.get_impl(polymorphic_key).set( 1634 state, 1635 dict_, 1636 state.manager.mapper.polymorphic_identity, 1637 None, 1638 ) 1639 1640 def _validate_polymorphic_identity(mapper, state, dict_): 1641 if ( 1642 polymorphic_key in dict_ 1643 and dict_[polymorphic_key] 1644 not in mapper._acceptable_polymorphic_identities 1645 ): 1646 util.warn_limited( 1647 "Flushing object %s with " 1648 "incompatible polymorphic identity %r; the " 1649 "object may not refresh and/or load correctly", 1650 (state_str(state), dict_[polymorphic_key]), 1651 ) 1652 1653 self._set_polymorphic_identity = _set_polymorphic_identity 1654 self._validate_polymorphic_identity = ( 1655 _validate_polymorphic_identity 1656 ) 1657 else: 1658 self._set_polymorphic_identity = None 1659 1660 _validate_polymorphic_identity = None 1661 1662 @_memoized_configured_property 1663 def _version_id_prop(self): 1664 if self.version_id_col is not None: 1665 return self._columntoproperty[self.version_id_col] 1666 else: 1667 return None 1668 1669 @_memoized_configured_property 1670 def _acceptable_polymorphic_identities(self): 1671 identities = set() 1672 1673 stack = deque([self]) 1674 while stack: 1675 item = stack.popleft() 1676 if item.mapped_table is self.mapped_table: 1677 identities.add(item.polymorphic_identity) 1678 stack.extend(item._inheriting_mappers) 1679 1680 return identities 1681 1682 @_memoized_configured_property 1683 def _prop_set(self): 1684 return frozenset(self._props.values()) 1685 1686 def _adapt_inherited_property(self, key, prop, init): 1687 if not self.concrete: 1688 self._configure_property(key, prop, init=False, setparent=False) 1689 elif key not in self._props: 1690 # determine if the class implements this attribute; if not, 1691 # or if it is implemented by the attribute that is handling the 1692 # given superclass-mapped property, then we need to report that we 1693 # can't use this at the instance level since we are a concrete 1694 # mapper and we don't map this. don't trip user-defined 1695 # descriptors that might have side effects when invoked. 1696 implementing_attribute = self.class_manager._get_class_attr_mro( 1697 key, prop 1698 ) 1699 if implementing_attribute is prop or ( 1700 isinstance( 1701 implementing_attribute, attributes.InstrumentedAttribute 1702 ) 1703 and implementing_attribute._parententity is prop.parent 1704 ): 1705 self._configure_property( 1706 key, 1707 properties.ConcreteInheritedProperty(), 1708 init=init, 1709 setparent=True, 1710 ) 1711 1712 def _configure_property(self, key, prop, init=True, setparent=True): 1713 self._log("_configure_property(%s, %s)", key, prop.__class__.__name__) 1714 1715 if not isinstance(prop, MapperProperty): 1716 prop = self._property_from_column(key, prop) 1717 1718 if isinstance(prop, properties.ColumnProperty): 1719 col = self.mapped_table.corresponding_column(prop.columns[0]) 1720 1721 # if the column is not present in the mapped table, 1722 # test if a column has been added after the fact to the 1723 # parent table (or their parent, etc.) [ticket:1570] 1724 if col is None and self.inherits: 1725 path = [self] 1726 for m in self.inherits.iterate_to_root(): 1727 col = m.local_table.corresponding_column(prop.columns[0]) 1728 if col is not None: 1729 for m2 in path: 1730 m2.mapped_table._reset_exported() 1731 col = self.mapped_table.corresponding_column( 1732 prop.columns[0] 1733 ) 1734 break 1735 path.append(m) 1736 1737 # subquery expression, column not present in the mapped 1738 # selectable. 1739 if col is None: 1740 col = prop.columns[0] 1741 1742 # column is coming in after _readonly_props was 1743 # initialized; check for 'readonly' 1744 if hasattr(self, "_readonly_props") and ( 1745 not hasattr(col, "table") 1746 or col.table not in self._cols_by_table 1747 ): 1748 self._readonly_props.add(prop) 1749 1750 else: 1751 # if column is coming in after _cols_by_table was 1752 # initialized, ensure the col is in the right set 1753 if ( 1754 hasattr(self, "_cols_by_table") 1755 and col.table in self._cols_by_table 1756 and col not in self._cols_by_table[col.table] 1757 ): 1758 self._cols_by_table[col.table].add(col) 1759 1760 # if this properties.ColumnProperty represents the "polymorphic 1761 # discriminator" column, mark it. We'll need this when rendering 1762 # columns in SELECT statements. 1763 if not hasattr(prop, "_is_polymorphic_discriminator"): 1764 prop._is_polymorphic_discriminator = ( 1765 col is self.polymorphic_on 1766 or prop.columns[0] is self.polymorphic_on 1767 ) 1768 1769 self.columns[key] = col 1770 for col in prop.columns + prop._orig_columns: 1771 for col in col.proxy_set: 1772 self._columntoproperty[col] = prop 1773 1774 prop.key = key 1775 1776 if setparent: 1777 prop.set_parent(self, init) 1778 1779 if key in self._props and getattr( 1780 self._props[key], "_mapped_by_synonym", False 1781 ): 1782 syn = self._props[key]._mapped_by_synonym 1783 raise sa_exc.ArgumentError( 1784 "Can't call map_column=True for synonym %r=%r, " 1785 "a ColumnProperty already exists keyed to the name " 1786 "%r for column %r" % (syn, key, key, syn) 1787 ) 1788 1789 if ( 1790 key in self._props 1791 and not isinstance(prop, properties.ColumnProperty) 1792 and not isinstance( 1793 self._props[key], 1794 ( 1795 properties.ColumnProperty, 1796 properties.ConcreteInheritedProperty, 1797 ), 1798 ) 1799 ): 1800 util.warn( 1801 "Property %s on %s being replaced with new " 1802 "property %s; the old property will be discarded" 1803 % (self._props[key], self, prop) 1804 ) 1805 oldprop = self._props[key] 1806 self._path_registry.pop(oldprop, None) 1807 1808 self._props[key] = prop 1809 1810 if not self.non_primary: 1811 prop.instrument_class(self) 1812 1813 for mapper in self._inheriting_mappers: 1814 mapper._adapt_inherited_property(key, prop, init) 1815 1816 if init: 1817 prop.init() 1818 prop.post_instrument_class(self) 1819 1820 if self.configured: 1821 self._expire_memoizations() 1822 1823 def _property_from_column(self, key, prop): 1824 """generate/update a :class:`.ColumnProprerty` given a 1825 :class:`.Column` object. """ 1826 1827 # we were passed a Column or a list of Columns; 1828 # generate a properties.ColumnProperty 1829 columns = util.to_list(prop) 1830 column = columns[0] 1831 if not expression._is_column(column): 1832 raise sa_exc.ArgumentError( 1833 "%s=%r is not an instance of MapperProperty or Column" 1834 % (key, prop) 1835 ) 1836 1837 prop = self._props.get(key, None) 1838 1839 if isinstance(prop, properties.ColumnProperty): 1840 if ( 1841 ( 1842 not self._inherits_equated_pairs 1843 or (prop.columns[0], column) 1844 not in self._inherits_equated_pairs 1845 ) 1846 and not prop.columns[0].shares_lineage(column) 1847 and prop.columns[0] is not self.version_id_col 1848 and column is not self.version_id_col 1849 ): 1850 warn_only = prop.parent is not self 1851 msg = ( 1852 "Implicitly combining column %s with column " 1853 "%s under attribute '%s'. Please configure one " 1854 "or more attributes for these same-named columns " 1855 "explicitly." % (prop.columns[-1], column, key) 1856 ) 1857 if warn_only: 1858 util.warn(msg) 1859 else: 1860 raise sa_exc.InvalidRequestError(msg) 1861 1862 # existing properties.ColumnProperty from an inheriting 1863 # mapper. make a copy and append our column to it 1864 prop = prop.copy() 1865 prop.columns.insert(0, column) 1866 self._log( 1867 "inserting column to existing list " 1868 "in properties.ColumnProperty %s" % (key) 1869 ) 1870 return prop 1871 elif prop is None or isinstance( 1872 prop, properties.ConcreteInheritedProperty 1873 ): 1874 mapped_column = [] 1875 for c in columns: 1876 mc = self.mapped_table.corresponding_column(c) 1877 if mc is None: 1878 mc = self.local_table.corresponding_column(c) 1879 if mc is not None: 1880 # if the column is in the local table but not the 1881 # mapped table, this corresponds to adding a 1882 # column after the fact to the local table. 1883 # [ticket:1523] 1884 self.mapped_table._reset_exported() 1885 mc = self.mapped_table.corresponding_column(c) 1886 if mc is None: 1887 raise sa_exc.ArgumentError( 1888 "When configuring property '%s' on %s, " 1889 "column '%s' is not represented in the mapper's " 1890 "table. Use the `column_property()` function to " 1891 "force this column to be mapped as a read-only " 1892 "attribute." % (key, self, c) 1893 ) 1894 mapped_column.append(mc) 1895 return properties.ColumnProperty(*mapped_column) 1896 else: 1897 raise sa_exc.ArgumentError( 1898 "WARNING: when configuring property '%s' on %s, " 1899 "column '%s' conflicts with property '%r'. " 1900 "To resolve this, map the column to the class under a " 1901 "different name in the 'properties' dictionary. Or, " 1902 "to remove all awareness of the column entirely " 1903 "(including its availability as a foreign key), " 1904 "use the 'include_properties' or 'exclude_properties' " 1905 "mapper arguments to control specifically which table " 1906 "columns get mapped." % (key, self, column.key, prop) 1907 ) 1908 1909 def _post_configure_properties(self): 1910 """Call the ``init()`` method on all ``MapperProperties`` 1911 attached to this mapper. 1912 1913 This is a deferred configuration step which is intended 1914 to execute once all mappers have been constructed. 1915 1916 """ 1917 1918 self._log("_post_configure_properties() started") 1919 l = [(key, prop) for key, prop in self._props.items()] 1920 for key, prop in l: 1921 self._log("initialize prop %s", key) 1922 1923 if prop.parent is self and not prop._configure_started: 1924 prop.init() 1925 1926 if prop._configure_finished: 1927 prop.post_instrument_class(self) 1928 1929 self._log("_post_configure_properties() complete") 1930 self.configured = True 1931 1932 def add_properties(self, dict_of_properties): 1933 """Add the given dictionary of properties to this mapper, 1934 using `add_property`. 1935 1936 """ 1937 for key, value in dict_of_properties.items(): 1938 self.add_property(key, value) 1939 1940 def add_property(self, key, prop): 1941 """Add an individual MapperProperty to this mapper. 1942 1943 If the mapper has not been configured yet, just adds the 1944 property to the initial properties dictionary sent to the 1945 constructor. If this Mapper has already been configured, then 1946 the given MapperProperty is configured immediately. 1947 1948 """ 1949 self._init_properties[key] = prop 1950 self._configure_property(key, prop, init=self.configured) 1951 1952 def _expire_memoizations(self): 1953 for mapper in self.iterate_to_root(): 1954 _memoized_configured_property.expire_instance(mapper) 1955 1956 @property 1957 def _log_desc(self): 1958 return ( 1959 "(" 1960 + self.class_.__name__ 1961 + "|" 1962 + ( 1963 self.local_table is not None 1964 and self.local_table.description 1965 or str(self.local_table) 1966 ) 1967 + (self.non_primary and "|non-primary" or "") 1968 + ")" 1969 ) 1970 1971 def _log(self, msg, *args): 1972 self.logger.info("%s " + msg, *((self._log_desc,) + args)) 1973 1974 def _log_debug(self, msg, *args): 1975 self.logger.debug("%s " + msg, *((self._log_desc,) + args)) 1976 1977 def __repr__(self): 1978 return "<Mapper at 0x%x; %s>" % (id(self), self.class_.__name__) 1979 1980 def __str__(self): 1981 return "Mapper|%s|%s%s" % ( 1982 self.class_.__name__, 1983 self.local_table is not None 1984 and self.local_table.description 1985 or None, 1986 self.non_primary and "|non-primary" or "", 1987 ) 1988 1989 def _is_orphan(self, state): 1990 orphan_possible = False 1991 for mapper in self.iterate_to_root(): 1992 for (key, cls) in mapper._delete_orphans: 1993 orphan_possible = True 1994 1995 has_parent = attributes.manager_of_class(cls).has_parent( 1996 state, key, optimistic=state.has_identity 1997 ) 1998 1999 if self.legacy_is_orphan and has_parent: 2000 return False 2001 elif not self.legacy_is_orphan and not has_parent: 2002 return True 2003 2004 if self.legacy_is_orphan: 2005 return orphan_possible 2006 else: 2007 return False 2008 2009 def has_property(self, key): 2010 return key in self._props 2011 2012 def get_property(self, key, _configure_mappers=True): 2013 """return a MapperProperty associated with the given key. 2014 """ 2015 2016 if _configure_mappers and Mapper._new_mappers: 2017 configure_mappers() 2018 2019 try: 2020 return self._props[key] 2021 except KeyError: 2022 raise sa_exc.InvalidRequestError( 2023 "Mapper '%s' has no property '%s'" % (self, key) 2024 ) 2025 2026 def get_property_by_column(self, column): 2027 """Given a :class:`.Column` object, return the 2028 :class:`.MapperProperty` which maps this column.""" 2029 2030 return self._columntoproperty[column] 2031 2032 @property 2033 def iterate_properties(self): 2034 """return an iterator of all MapperProperty objects.""" 2035 if Mapper._new_mappers: 2036 configure_mappers() 2037 return iter(self._props.values()) 2038 2039 def _mappers_from_spec(self, spec, selectable): 2040 """given a with_polymorphic() argument, return the set of mappers it 2041 represents. 2042 2043 Trims the list of mappers to just those represented within the given 2044 selectable, if present. This helps some more legacy-ish mappings. 2045 2046 """ 2047 if spec == "*": 2048 mappers = list(self.self_and_descendants) 2049 elif spec: 2050 mappers = set() 2051 for m in util.to_list(spec): 2052 m = _class_to_mapper(m) 2053 if not m.isa(self): 2054 raise sa_exc.InvalidRequestError( 2055 "%r does not inherit from %r" % (m, self) 2056 ) 2057 2058 if selectable is None: 2059 mappers.update(m.iterate_to_root()) 2060 else: 2061 mappers.add(m) 2062 mappers = [m for m in self.self_and_descendants if m in mappers] 2063 else: 2064 mappers = [] 2065 2066 if selectable is not None: 2067 tables = set( 2068 sql_util.find_tables(selectable, include_aliases=True) 2069 ) 2070 mappers = [m for m in mappers if m.local_table in tables] 2071 return mappers 2072 2073 def _selectable_from_mappers(self, mappers, innerjoin): 2074 """given a list of mappers (assumed to be within this mapper's 2075 inheritance hierarchy), construct an outerjoin amongst those mapper's 2076 mapped tables. 2077 2078 """ 2079 from_obj = self.mapped_table 2080 for m in mappers: 2081 if m is self: 2082 continue 2083 if m.concrete: 2084 raise sa_exc.InvalidRequestError( 2085 "'with_polymorphic()' requires 'selectable' argument " 2086 "when concrete-inheriting mappers are used." 2087 ) 2088 elif not m.single: 2089 if innerjoin: 2090 from_obj = from_obj.join( 2091 m.local_table, m.inherit_condition 2092 ) 2093 else: 2094 from_obj = from_obj.outerjoin( 2095 m.local_table, m.inherit_condition 2096 ) 2097 2098 return from_obj 2099 2100 @_memoized_configured_property 2101 def _single_table_criterion(self): 2102 if self.single and self.inherits and self.polymorphic_on is not None: 2103 return self.polymorphic_on._annotate({"parentmapper": self}).in_( 2104 m.polymorphic_identity for m in self.self_and_descendants 2105 ) 2106 else: 2107 return None 2108 2109 @_memoized_configured_property 2110 def _with_polymorphic_mappers(self): 2111 if Mapper._new_mappers: 2112 configure_mappers() 2113 if not self.with_polymorphic: 2114 return [] 2115 return self._mappers_from_spec(*self.with_polymorphic) 2116 2117 @_memoized_configured_property 2118 def _with_polymorphic_selectable(self): 2119 if not self.with_polymorphic: 2120 return self.mapped_table 2121 2122 spec, selectable = self.with_polymorphic 2123 if selectable is not None: 2124 return selectable 2125 else: 2126 return self._selectable_from_mappers( 2127 self._mappers_from_spec(spec, selectable), False 2128 ) 2129 2130 with_polymorphic_mappers = _with_polymorphic_mappers 2131 """The list of :class:`.Mapper` objects included in the 2132 default "polymorphic" query. 2133 2134 """ 2135 2136 @_memoized_configured_property 2137 def _insert_cols_evaluating_none(self): 2138 return dict( 2139 ( 2140 table, 2141 frozenset( 2142 col for col in columns if col.type.should_evaluate_none 2143 ), 2144 ) 2145 for table, columns in self._cols_by_table.items() 2146 ) 2147 2148 @_memoized_configured_property 2149 def _insert_cols_as_none(self): 2150 return dict( 2151 ( 2152 table, 2153 frozenset( 2154 col.key 2155 for col in columns 2156 if not col.primary_key 2157 and not col.server_default 2158 and not col.default 2159 and not col.type.should_evaluate_none 2160 ), 2161 ) 2162 for table, columns in self._cols_by_table.items() 2163 ) 2164 2165 @_memoized_configured_property 2166 def _propkey_to_col(self): 2167 return dict( 2168 ( 2169 table, 2170 dict( 2171 (self._columntoproperty[col].key, col) for col in columns 2172 ), 2173 ) 2174 for table, columns in self._cols_by_table.items() 2175 ) 2176 2177 @_memoized_configured_property 2178 def _pk_keys_by_table(self): 2179 return dict( 2180 (table, frozenset([col.key for col in pks])) 2181 for table, pks in self._pks_by_table.items() 2182 ) 2183 2184 @_memoized_configured_property 2185 def _pk_attr_keys_by_table(self): 2186 return dict( 2187 ( 2188 table, 2189 frozenset([self._columntoproperty[col].key for col in pks]), 2190 ) 2191 for table, pks in self._pks_by_table.items() 2192 ) 2193 2194 @_memoized_configured_property 2195 def _server_default_cols(self): 2196 return dict( 2197 ( 2198 table, 2199 frozenset( 2200 [ 2201 col.key 2202 for col in columns 2203 if col.server_default is not None 2204 ] 2205 ), 2206 ) 2207 for table, columns in self._cols_by_table.items() 2208 ) 2209 2210 @_memoized_configured_property 2211 def _server_default_plus_onupdate_propkeys(self): 2212 result = set() 2213 2214 for table, columns in self._cols_by_table.items(): 2215 for col in columns: 2216 if ( 2217 col.server_default is not None 2218 or col.server_onupdate is not None 2219 ) and col in self._columntoproperty: 2220 result.add(self._columntoproperty[col].key) 2221 2222 return result 2223 2224 @_memoized_configured_property 2225 def _server_onupdate_default_cols(self): 2226 return dict( 2227 ( 2228 table, 2229 frozenset( 2230 [ 2231 col.key 2232 for col in columns 2233 if col.server_onupdate is not None 2234 ] 2235 ), 2236 ) 2237 for table, columns in self._cols_by_table.items() 2238 ) 2239 2240 @property 2241 def selectable(self): 2242 """The :func:`.select` construct this :class:`.Mapper` selects from 2243 by default. 2244 2245 Normally, this is equivalent to :attr:`.mapped_table`, unless 2246 the ``with_polymorphic`` feature is in use, in which case the 2247 full "polymorphic" selectable is returned. 2248 2249 """ 2250 return self._with_polymorphic_selectable 2251 2252 def _with_polymorphic_args( 2253 self, spec=None, selectable=False, innerjoin=False 2254 ): 2255 if self.with_polymorphic: 2256 if not spec: 2257 spec = self.with_polymorphic[0] 2258 if selectable is False: 2259 selectable = self.with_polymorphic[1] 2260 elif selectable is False: 2261 selectable = None 2262 mappers = self._mappers_from_spec(spec, selectable) 2263 if selectable is not None: 2264 return mappers, selectable 2265 else: 2266 return mappers, self._selectable_from_mappers(mappers, innerjoin) 2267 2268 @_memoized_configured_property 2269 def _polymorphic_properties(self): 2270 return list( 2271 self._iterate_polymorphic_properties( 2272 self._with_polymorphic_mappers 2273 ) 2274 ) 2275 2276 def _iterate_polymorphic_properties(self, mappers=None): 2277 """Return an iterator of MapperProperty objects which will render into 2278 a SELECT.""" 2279 if mappers is None: 2280 mappers = self._with_polymorphic_mappers 2281 2282 if not mappers: 2283 for c in self.iterate_properties: 2284 yield c 2285 else: 2286 # in the polymorphic case, filter out discriminator columns 2287 # from other mappers, as these are sometimes dependent on that 2288 # mapper's polymorphic selectable (which we don't want rendered) 2289 for c in util.unique_list( 2290 chain( 2291 *[ 2292 list(mapper.iterate_properties) 2293 for mapper in [self] + mappers 2294 ] 2295 ) 2296 ): 2297 if getattr(c, "_is_polymorphic_discriminator", False) and ( 2298 self.polymorphic_on is None 2299 or c.columns[0] is not self.polymorphic_on 2300 ): 2301 continue 2302 yield c 2303 2304 @_memoized_configured_property 2305 def attrs(self): 2306 """A namespace of all :class:`.MapperProperty` objects 2307 associated this mapper. 2308 2309 This is an object that provides each property based on 2310 its key name. For instance, the mapper for a 2311 ``User`` class which has ``User.name`` attribute would 2312 provide ``mapper.attrs.name``, which would be the 2313 :class:`.ColumnProperty` representing the ``name`` 2314 column. The namespace object can also be iterated, 2315 which would yield each :class:`.MapperProperty`. 2316 2317 :class:`.Mapper` has several pre-filtered views 2318 of this attribute which limit the types of properties 2319 returned, including :attr:`.synonyms`, :attr:`.column_attrs`, 2320 :attr:`.relationships`, and :attr:`.composites`. 2321 2322 .. warning:: 2323 2324 The :attr:`.Mapper.attrs` accessor namespace is an 2325 instance of :class:`.OrderedProperties`. This is 2326 a dictionary-like object which includes a small number of 2327 named methods such as :meth:`.OrderedProperties.items` 2328 and :meth:`.OrderedProperties.values`. When 2329 accessing attributes dynamically, favor using the dict-access 2330 scheme, e.g. ``mapper.attrs[somename]`` over 2331 ``getattr(mapper.attrs, somename)`` to avoid name collisions. 2332 2333 .. seealso:: 2334 2335 :attr:`.Mapper.all_orm_descriptors` 2336 2337 """ 2338 if Mapper._new_mappers: 2339 configure_mappers() 2340 return util.ImmutableProperties(self._props) 2341 2342 @_memoized_configured_property 2343 def all_orm_descriptors(self): 2344 """A namespace of all :class:`.InspectionAttr` attributes associated 2345 with the mapped class. 2346 2347 These attributes are in all cases Python :term:`descriptors` 2348 associated with the mapped class or its superclasses. 2349 2350 This namespace includes attributes that are mapped to the class 2351 as well as attributes declared by extension modules. 2352 It includes any Python descriptor type that inherits from 2353 :class:`.InspectionAttr`. This includes 2354 :class:`.QueryableAttribute`, as well as extension types such as 2355 :class:`.hybrid_property`, :class:`.hybrid_method` and 2356 :class:`.AssociationProxy`. 2357 2358 To distinguish between mapped attributes and extension attributes, 2359 the attribute :attr:`.InspectionAttr.extension_type` will refer 2360 to a constant that distinguishes between different extension types. 2361 2362 When dealing with a :class:`.QueryableAttribute`, the 2363 :attr:`.QueryableAttribute.property` attribute refers to the 2364 :class:`.MapperProperty` property, which is what you get when 2365 referring to the collection of mapped properties via 2366 :attr:`.Mapper.attrs`. 2367 2368 .. warning:: 2369 2370 The :attr:`.Mapper.all_orm_descriptors` accessor namespace is an 2371 instance of :class:`.OrderedProperties`. This is 2372 a dictionary-like object which includes a small number of 2373 named methods such as :meth:`.OrderedProperties.items` 2374 and :meth:`.OrderedProperties.values`. When 2375 accessing attributes dynamically, favor using the dict-access 2376 scheme, e.g. ``mapper.all_orm_descriptors[somename]`` over 2377 ``getattr(mapper.all_orm_descriptors, somename)`` to avoid name 2378 collisions. 2379 2380 .. seealso:: 2381 2382 :attr:`.Mapper.attrs` 2383 2384 """ 2385 return util.ImmutableProperties( 2386 dict(self.class_manager._all_sqla_attributes()) 2387 ) 2388 2389 @_memoized_configured_property 2390 def synonyms(self): 2391 """Return a namespace of all :class:`.SynonymProperty` 2392 properties maintained by this :class:`.Mapper`. 2393 2394 .. seealso:: 2395 2396 :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty` 2397 objects. 2398 2399 """ 2400 return self._filter_properties(properties.SynonymProperty) 2401 2402 @_memoized_configured_property 2403 def column_attrs(self): 2404 """Return a namespace of all :class:`.ColumnProperty` 2405 properties maintained by this :class:`.Mapper`. 2406 2407 .. seealso:: 2408 2409 :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty` 2410 objects. 2411 2412 """ 2413 return self._filter_properties(properties.ColumnProperty) 2414 2415 @_memoized_configured_property 2416 def relationships(self): 2417 """A namespace of all :class:`.RelationshipProperty` properties 2418 maintained by this :class:`.Mapper`. 2419 2420 .. warning:: 2421 2422 the :attr:`.Mapper.relationships` accessor namespace is an 2423 instance of :class:`.OrderedProperties`. This is 2424 a dictionary-like object which includes a small number of 2425 named methods such as :meth:`.OrderedProperties.items` 2426 and :meth:`.OrderedProperties.values`. When 2427 accessing attributes dynamically, favor using the dict-access 2428 scheme, e.g. ``mapper.relationships[somename]`` over 2429 ``getattr(mapper.relationships, somename)`` to avoid name 2430 collisions. 2431 2432 .. seealso:: 2433 2434 :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty` 2435 objects. 2436 2437 """ 2438 return self._filter_properties(properties.RelationshipProperty) 2439 2440 @_memoized_configured_property 2441 def composites(self): 2442 """Return a namespace of all :class:`.CompositeProperty` 2443 properties maintained by this :class:`.Mapper`. 2444 2445 .. seealso:: 2446 2447 :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty` 2448 objects. 2449 2450 """ 2451 return self._filter_properties(properties.CompositeProperty) 2452 2453 def _filter_properties(self, type_): 2454 if Mapper._new_mappers: 2455 configure_mappers() 2456 return util.ImmutableProperties( 2457 util.OrderedDict( 2458 (k, v) for k, v in self._props.items() if isinstance(v, type_) 2459 ) 2460 ) 2461 2462 @_memoized_configured_property 2463 def _get_clause(self): 2464 """create a "get clause" based on the primary key. this is used 2465 by query.get() and many-to-one lazyloads to load this item 2466 by primary key. 2467 2468 """ 2469 params = [ 2470 (primary_key, sql.bindparam(None, type_=primary_key.type)) 2471 for primary_key in self.primary_key 2472 ] 2473 return ( 2474 sql.and_(*[k == v for (k, v) in params]), 2475 util.column_dict(params), 2476 ) 2477 2478 @_memoized_configured_property 2479 def _equivalent_columns(self): 2480 """Create a map of all equivalent columns, based on 2481 the determination of column pairs that are equated to 2482 one another based on inherit condition. This is designed 2483 to work with the queries that util.polymorphic_union 2484 comes up with, which often don't include the columns from 2485 the base table directly (including the subclass table columns 2486 only). 2487 2488 The resulting structure is a dictionary of columns mapped 2489 to lists of equivalent columns, e.g.:: 2490 2491 { 2492 tablea.col1: 2493 {tableb.col1, tablec.col1}, 2494 tablea.col2: 2495 {tabled.col2} 2496 } 2497 2498 """ 2499 result = util.column_dict() 2500 2501 def visit_binary(binary): 2502 if binary.operator == operators.eq: 2503 if binary.left in result: 2504 result[binary.left].add(binary.right) 2505 else: 2506 result[binary.left] = util.column_set((binary.right,)) 2507 if binary.right in result: 2508 result[binary.right].add(binary.left) 2509 else: 2510 result[binary.right] = util.column_set((binary.left,)) 2511 2512 for mapper in self.base_mapper.self_and_descendants: 2513 if mapper.inherit_condition is not None: 2514 visitors.traverse( 2515 mapper.inherit_condition, {}, {"binary": visit_binary} 2516 ) 2517 2518 return result 2519 2520 def _is_userland_descriptor(self, obj): 2521 if isinstance( 2522 obj, 2523 ( 2524 _MappedAttribute, 2525 instrumentation.ClassManager, 2526 expression.ColumnElement, 2527 ), 2528 ): 2529 return False 2530 else: 2531 return True 2532 2533 def _should_exclude(self, name, assigned_name, local, column): 2534 """determine whether a particular property should be implicitly 2535 present on the class. 2536 2537 This occurs when properties are propagated from an inherited class, or 2538 are applied from the columns present in the mapped table. 2539 2540 """ 2541 2542 # check for class-bound attributes and/or descriptors, 2543 # either local or from an inherited class 2544 if local: 2545 if self.class_.__dict__.get( 2546 assigned_name, None 2547 ) is not None and self._is_userland_descriptor( 2548 self.class_.__dict__[assigned_name] 2549 ): 2550 return True 2551 else: 2552 attr = self.class_manager._get_class_attr_mro(assigned_name, None) 2553 if attr is not None and self._is_userland_descriptor(attr): 2554 return True 2555 2556 if ( 2557 self.include_properties is not None 2558 and name not in self.include_properties 2559 and (column is None or column not in self.include_properties) 2560 ): 2561 self._log("not including property %s" % (name)) 2562 return True 2563 2564 if self.exclude_properties is not None and ( 2565 name in self.exclude_properties 2566 or (column is not None and column in self.exclude_properties) 2567 ): 2568 self._log("excluding property %s" % (name)) 2569 return True 2570 2571 return False 2572 2573 def common_parent(self, other): 2574 """Return true if the given mapper shares a 2575 common inherited parent as this mapper.""" 2576 2577 return self.base_mapper is other.base_mapper 2578 2579 def _canload(self, state, allow_subtypes): 2580 s = self.primary_mapper() 2581 if self.polymorphic_on is not None or allow_subtypes: 2582 return _state_mapper(state).isa(s) 2583 else: 2584 return _state_mapper(state) is s 2585 2586 def isa(self, other): 2587 """Return True if the this mapper inherits from the given mapper.""" 2588 2589 m = self 2590 while m and m is not other: 2591 m = m.inherits 2592 return bool(m) 2593 2594 def iterate_to_root(self): 2595 m = self 2596 while m: 2597 yield m 2598 m = m.inherits 2599 2600 @_memoized_configured_property 2601 def self_and_descendants(self): 2602 """The collection including this mapper and all descendant mappers. 2603 2604 This includes not just the immediately inheriting mappers but 2605 all their inheriting mappers as well. 2606 2607 """ 2608 descendants = [] 2609 stack = deque([self]) 2610 while stack: 2611 item = stack.popleft() 2612 descendants.append(item) 2613 stack.extend(item._inheriting_mappers) 2614 return util.WeakSequence(descendants) 2615 2616 def polymorphic_iterator(self): 2617 """Iterate through the collection including this mapper and 2618 all descendant mappers. 2619 2620 This includes not just the immediately inheriting mappers but 2621 all their inheriting mappers as well. 2622 2623 To iterate through an entire hierarchy, use 2624 ``mapper.base_mapper.polymorphic_iterator()``. 2625 2626 """ 2627 return iter(self.self_and_descendants) 2628 2629 def primary_mapper(self): 2630 """Return the primary mapper corresponding to this mapper's class key 2631 (class).""" 2632 2633 return self.class_manager.mapper 2634 2635 @property 2636 def primary_base_mapper(self): 2637 return self.class_manager.mapper.base_mapper 2638 2639 def _result_has_identity_key(self, result, adapter=None): 2640 pk_cols = self.primary_key 2641 if adapter: 2642 pk_cols = [adapter.columns[c] for c in pk_cols] 2643 for col in pk_cols: 2644 if not result._has_key(col): 2645 return False 2646 else: 2647 return True 2648 2649 def identity_key_from_row(self, row, identity_token=None, adapter=None): 2650 """Return an identity-map key for use in storing/retrieving an 2651 item from the identity map. 2652 2653 :param row: A :class:`.RowProxy` instance. The columns which are 2654 mapped by this :class:`.Mapper` should be locatable in the row, 2655 preferably via the :class:`.Column` object directly (as is the case 2656 when a :func:`.select` construct is executed), or via string names of 2657 the form ``<tablename>_<colname>``. 2658 2659 """ 2660 pk_cols = self.primary_key 2661 if adapter: 2662 pk_cols = [adapter.columns[c] for c in pk_cols] 2663 2664 return ( 2665 self._identity_class, 2666 tuple(row[column] for column in pk_cols), 2667 identity_token, 2668 ) 2669 2670 def identity_key_from_primary_key(self, primary_key, identity_token=None): 2671 """Return an identity-map key for use in storing/retrieving an 2672 item from an identity map. 2673 2674 :param primary_key: A list of values indicating the identifier. 2675 2676 """ 2677 return self._identity_class, tuple(primary_key), identity_token 2678 2679 def identity_key_from_instance(self, instance): 2680 """Return the identity key for the given instance, based on 2681 its primary key attributes. 2682 2683 If the instance's state is expired, calling this method 2684 will result in a database check to see if the object has been deleted. 2685 If the row no longer exists, 2686 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 2687 2688 This value is typically also found on the instance state under the 2689 attribute name `key`. 2690 2691 """ 2692 state = attributes.instance_state(instance) 2693 return self._identity_key_from_state(state, attributes.PASSIVE_OFF) 2694 2695 def _identity_key_from_state( 2696 self, state, passive=attributes.PASSIVE_RETURN_NEVER_SET 2697 ): 2698 dict_ = state.dict 2699 manager = state.manager 2700 return ( 2701 self._identity_class, 2702 tuple( 2703 [ 2704 manager[prop.key].impl.get(state, dict_, passive) 2705 for prop in self._identity_key_props 2706 ] 2707 ), 2708 state.identity_token, 2709 ) 2710 2711 def primary_key_from_instance(self, instance): 2712 """Return the list of primary key values for the given 2713 instance. 2714 2715 If the instance's state is expired, calling this method 2716 will result in a database check to see if the object has been deleted. 2717 If the row no longer exists, 2718 :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised. 2719 2720 """ 2721 state = attributes.instance_state(instance) 2722 identity_key = self._identity_key_from_state( 2723 state, attributes.PASSIVE_OFF 2724 ) 2725 return identity_key[1] 2726 2727 @_memoized_configured_property 2728 def _identity_key_props(self): 2729 return [self._columntoproperty[col] for col in self.primary_key] 2730 2731 @_memoized_configured_property 2732 def _all_pk_props(self): 2733 collection = set() 2734 for table in self.tables: 2735 collection.update(self._pks_by_table[table]) 2736 return collection 2737 2738 @_memoized_configured_property 2739 def _should_undefer_in_wildcard(self): 2740 cols = set(self.primary_key) 2741 if self.polymorphic_on is not None: 2742 cols.add(self.polymorphic_on) 2743 return cols 2744 2745 @_memoized_configured_property 2746 def _primary_key_propkeys(self): 2747 return {prop.key for prop in self._all_pk_props} 2748 2749 def _get_state_attr_by_column( 2750 self, state, dict_, column, passive=attributes.PASSIVE_RETURN_NEVER_SET 2751 ): 2752 prop = self._columntoproperty[column] 2753 return state.manager[prop.key].impl.get(state, dict_, passive=passive) 2754 2755 def _set_committed_state_attr_by_column(self, state, dict_, column, value): 2756 prop = self._columntoproperty[column] 2757 state.manager[prop.key].impl.set_committed_value(state, dict_, value) 2758 2759 def _set_state_attr_by_column(self, state, dict_, column, value): 2760 prop = self._columntoproperty[column] 2761 state.manager[prop.key].impl.set(state, dict_, value, None) 2762 2763 def _get_committed_attr_by_column(self, obj, column): 2764 state = attributes.instance_state(obj) 2765 dict_ = attributes.instance_dict(obj) 2766 return self._get_committed_state_attr_by_column( 2767 state, dict_, column, passive=attributes.PASSIVE_OFF 2768 ) 2769 2770 def _get_committed_state_attr_by_column( 2771 self, state, dict_, column, passive=attributes.PASSIVE_RETURN_NEVER_SET 2772 ): 2773 2774 prop = self._columntoproperty[column] 2775 return state.manager[prop.key].impl.get_committed_value( 2776 state, dict_, passive=passive 2777 ) 2778 2779 def _optimized_get_statement(self, state, attribute_names): 2780 """assemble a WHERE clause which retrieves a given state by primary 2781 key, using a minimized set of tables. 2782 2783 Applies to a joined-table inheritance mapper where the 2784 requested attribute names are only present on joined tables, 2785 not the base table. The WHERE clause attempts to include 2786 only those tables to minimize joins. 2787 2788 """ 2789 props = self._props 2790 2791 tables = set( 2792 chain( 2793 *[ 2794 sql_util.find_tables(c, check_columns=True) 2795 for key in attribute_names 2796 for c in props[key].columns 2797 ] 2798 ) 2799 ) 2800 2801 if self.base_mapper.local_table in tables: 2802 return None 2803 2804 class ColumnsNotAvailable(Exception): 2805 pass 2806 2807 def visit_binary(binary): 2808 leftcol = binary.left 2809 rightcol = binary.right 2810 if leftcol is None or rightcol is None: 2811 return 2812 2813 if leftcol.table not in tables: 2814 leftval = self._get_committed_state_attr_by_column( 2815 state, 2816 state.dict, 2817 leftcol, 2818 passive=attributes.PASSIVE_NO_INITIALIZE, 2819 ) 2820 if leftval in orm_util._none_set: 2821 raise ColumnsNotAvailable() 2822 binary.left = sql.bindparam( 2823 None, leftval, type_=binary.right.type 2824 ) 2825 elif rightcol.table not in tables: 2826 rightval = self._get_committed_state_attr_by_column( 2827 state, 2828 state.dict, 2829 rightcol, 2830 passive=attributes.PASSIVE_NO_INITIALIZE, 2831 ) 2832 if rightval in orm_util._none_set: 2833 raise ColumnsNotAvailable() 2834 binary.right = sql.bindparam( 2835 None, rightval, type_=binary.right.type 2836 ) 2837 2838 allconds = [] 2839 2840 try: 2841 start = False 2842 for mapper in reversed(list(self.iterate_to_root())): 2843 if mapper.local_table in tables: 2844 start = True 2845 elif not isinstance( 2846 mapper.local_table, expression.TableClause 2847 ): 2848 return None 2849 if start and not mapper.single: 2850 allconds.append( 2851 visitors.cloned_traverse( 2852 mapper.inherit_condition, 2853 {}, 2854 {"binary": visit_binary}, 2855 ) 2856 ) 2857 except ColumnsNotAvailable: 2858 return None 2859 2860 cond = sql.and_(*allconds) 2861 2862 cols = [] 2863 for key in attribute_names: 2864 cols.extend(props[key].columns) 2865 return sql.select(cols, cond, use_labels=True) 2866 2867 def _iterate_to_target_viawpoly(self, mapper): 2868 if self.isa(mapper): 2869 prev = self 2870 for m in self.iterate_to_root(): 2871 yield m 2872 2873 if m is not prev and prev not in m._with_polymorphic_mappers: 2874 break 2875 2876 prev = m 2877 if m is mapper: 2878 break 2879 2880 def _should_selectin_load(self, enabled_via_opt, polymorphic_from): 2881 if not enabled_via_opt: 2882 # common case, takes place for all polymorphic loads 2883 mapper = polymorphic_from 2884 for m in self._iterate_to_target_viawpoly(mapper): 2885 if m.polymorphic_load == "selectin": 2886 return m 2887 else: 2888 # uncommon case, selectin load options were used 2889 enabled_via_opt = set(enabled_via_opt) 2890 enabled_via_opt_mappers = {e.mapper: e for e in enabled_via_opt} 2891 for entity in enabled_via_opt.union([polymorphic_from]): 2892 mapper = entity.mapper 2893 for m in self._iterate_to_target_viawpoly(mapper): 2894 if ( 2895 m.polymorphic_load == "selectin" 2896 or m in enabled_via_opt_mappers 2897 ): 2898 return enabled_via_opt_mappers.get(m, m) 2899 2900 return None 2901 2902 @util.dependencies( 2903 "sqlalchemy.ext.baked", "sqlalchemy.orm.strategy_options" 2904 ) 2905 def _subclass_load_via_in(self, baked, strategy_options, entity): 2906 """Assemble a BakedQuery that can load the columns local to 2907 this subclass as a SELECT with IN. 2908 2909 """ 2910 assert self.inherits 2911 2912 polymorphic_prop = self._columntoproperty[self.polymorphic_on] 2913 keep_props = set([polymorphic_prop] + self._identity_key_props) 2914 2915 disable_opt = strategy_options.Load(entity) 2916 enable_opt = strategy_options.Load(entity) 2917 2918 for prop in self.attrs: 2919 if prop.parent is self or prop in keep_props: 2920 # "enable" options, to turn on the properties that we want to 2921 # load by default (subject to options from the query) 2922 enable_opt.set_generic_strategy( 2923 (prop.key,), dict(prop.strategy_key) 2924 ) 2925 else: 2926 # "disable" options, to turn off the properties from the 2927 # superclass that we *don't* want to load, applied after 2928 # the options from the query to override them 2929 disable_opt.set_generic_strategy( 2930 (prop.key,), {"do_nothing": True} 2931 ) 2932 2933 if len(self.primary_key) > 1: 2934 in_expr = sql.tuple_(*self.primary_key) 2935 else: 2936 in_expr = self.primary_key[0] 2937 2938 if entity.is_aliased_class: 2939 assert entity.mapper is self 2940 q = baked.BakedQuery( 2941 self._compiled_cache, 2942 lambda session: session.query(entity) 2943 .select_entity_from(entity.selectable) 2944 ._adapt_all_clauses(), 2945 (self,), 2946 ) 2947 q.spoil() 2948 else: 2949 q = baked.BakedQuery( 2950 self._compiled_cache, 2951 lambda session: session.query(self), 2952 (self,), 2953 ) 2954 2955 q += lambda q: q.filter( 2956 in_expr.in_(sql.bindparam("primary_keys", expanding=True)) 2957 ).order_by(*self.primary_key) 2958 2959 return q, enable_opt, disable_opt 2960 2961 @_memoized_configured_property 2962 def _subclass_load_via_in_mapper(self): 2963 return self._subclass_load_via_in(self) 2964 2965 def cascade_iterator(self, type_, state, halt_on=None): 2966 """Iterate each element and its mapper in an object graph, 2967 for all relationships that meet the given cascade rule. 2968 2969 :param type_: 2970 The name of the cascade rule (i.e. ``"save-update"``, ``"delete"``, 2971 etc.). 2972 2973 .. note:: the ``"all"`` cascade is not accepted here. For a generic 2974 object traversal function, see :ref:`faq_walk_objects`. 2975 2976 :param state: 2977 The lead InstanceState. child items will be processed per 2978 the relationships defined for this object's mapper. 2979 2980 :return: the method yields individual object instances. 2981 2982 .. seealso:: 2983 2984 :ref:`unitofwork_cascades` 2985 2986 :ref:`faq_walk_objects` - illustrates a generic function to 2987 traverse all objects without relying on cascades. 2988 2989 """ 2990 visited_states = set() 2991 prp, mpp = object(), object() 2992 2993 assert state.mapper.isa(self) 2994 2995 visitables = deque( 2996 [(deque(state.mapper._props.values()), prp, state, state.dict)] 2997 ) 2998 2999 while visitables: 3000 iterator, item_type, parent_state, parent_dict = visitables[-1] 3001 if not iterator: 3002 visitables.pop() 3003 continue 3004 3005 if item_type is prp: 3006 prop = iterator.popleft() 3007 if type_ not in prop.cascade: 3008 continue 3009 queue = deque( 3010 prop.cascade_iterator( 3011 type_, 3012 parent_state, 3013 parent_dict, 3014 visited_states, 3015 halt_on, 3016 ) 3017 ) 3018 if queue: 3019 visitables.append((queue, mpp, None, None)) 3020 elif item_type is mpp: 3021 ( 3022 instance, 3023 instance_mapper, 3024 corresponding_state, 3025 corresponding_dict, 3026 ) = iterator.popleft() 3027 yield ( 3028 instance, 3029 instance_mapper, 3030 corresponding_state, 3031 corresponding_dict, 3032 ) 3033 visitables.append( 3034 ( 3035 deque(instance_mapper._props.values()), 3036 prp, 3037 corresponding_state, 3038 corresponding_dict, 3039 ) 3040 ) 3041 3042 @_memoized_configured_property 3043 def _compiled_cache(self): 3044 return util.LRUCache(self._compiled_cache_size) 3045 3046 @_memoized_configured_property 3047 def _sorted_tables(self): 3048 table_to_mapper = {} 3049 3050 for mapper in self.base_mapper.self_and_descendants: 3051 for t in mapper.tables: 3052 table_to_mapper.setdefault(t, mapper) 3053 3054 extra_dependencies = [] 3055 for table, mapper in table_to_mapper.items(): 3056 super_ = mapper.inherits 3057 if super_: 3058 extra_dependencies.extend( 3059 [(super_table, table) for super_table in super_.tables] 3060 ) 3061 3062 def skip(fk): 3063 # attempt to skip dependencies that are not 3064 # significant to the inheritance chain 3065 # for two tables that are related by inheritance. 3066 # while that dependency may be important, it's technically 3067 # not what we mean to sort on here. 3068 parent = table_to_mapper.get(fk.parent.table) 3069 dep = table_to_mapper.get(fk.column.table) 3070 if ( 3071 parent is not None 3072 and dep is not None 3073 and dep is not parent 3074 and dep.inherit_condition is not None 3075 ): 3076 cols = set(sql_util._find_columns(dep.inherit_condition)) 3077 if parent.inherit_condition is not None: 3078 cols = cols.union( 3079 sql_util._find_columns(parent.inherit_condition) 3080 ) 3081 return fk.parent not in cols and fk.column not in cols 3082 else: 3083 return fk.parent not in cols 3084 return False 3085 3086 sorted_ = sql_util.sort_tables( 3087 table_to_mapper, 3088 skip_fn=skip, 3089 extra_dependencies=extra_dependencies, 3090 ) 3091 3092 ret = util.OrderedDict() 3093 for t in sorted_: 3094 ret[t] = table_to_mapper[t] 3095 return ret 3096 3097 def _memo(self, key, callable_): 3098 if key in self._memoized_values: 3099 return self._memoized_values[key] 3100 else: 3101 self._memoized_values[key] = value = callable_() 3102 return value 3103 3104 @util.memoized_property 3105 def _table_to_equated(self): 3106 """memoized map of tables to collections of columns to be 3107 synchronized upwards to the base mapper.""" 3108 3109 result = util.defaultdict(list) 3110 3111 for table in self._sorted_tables: 3112 cols = set(table.c) 3113 for m in self.iterate_to_root(): 3114 if m._inherits_equated_pairs and cols.intersection( 3115 util.reduce( 3116 set.union, 3117 [l.proxy_set for l, r in m._inherits_equated_pairs], 3118 ) 3119 ): 3120 result[table].append((m, m._inherits_equated_pairs)) 3121 3122 return result 3123 3124 3125def configure_mappers(): 3126 """Initialize the inter-mapper relationships of all mappers that 3127 have been constructed thus far. 3128 3129 This function can be called any number of times, but in 3130 most cases is invoked automatically, the first time mappings are used, 3131 as well as whenever mappings are used and additional not-yet-configured 3132 mappers have been constructed. 3133 3134 Points at which this occur include when a mapped class is instantiated 3135 into an instance, as well as when the :meth:`.Session.query` method 3136 is used. 3137 3138 The :func:`.configure_mappers` function provides several event hooks 3139 that can be used to augment its functionality. These methods include: 3140 3141 * :meth:`.MapperEvents.before_configured` - called once before 3142 :func:`.configure_mappers` does any work; this can be used to establish 3143 additional options, properties, or related mappings before the operation 3144 proceeds. 3145 3146 * :meth:`.MapperEvents.mapper_configured` - called as each individual 3147 :class:`.Mapper` is configured within the process; will include all 3148 mapper state except for backrefs set up by other mappers that are still 3149 to be configured. 3150 3151 * :meth:`.MapperEvents.after_configured` - called once after 3152 :func:`.configure_mappers` is complete; at this stage, all 3153 :class:`.Mapper` objects that are known to SQLAlchemy will be fully 3154 configured. Note that the calling application may still have other 3155 mappings that haven't been produced yet, such as if they are in modules 3156 as yet unimported. 3157 3158 """ 3159 3160 if not Mapper._new_mappers: 3161 return 3162 3163 _CONFIGURE_MUTEX.acquire() 3164 try: 3165 global _already_compiling 3166 if _already_compiling: 3167 return 3168 _already_compiling = True 3169 try: 3170 3171 # double-check inside mutex 3172 if not Mapper._new_mappers: 3173 return 3174 3175 Mapper.dispatch._for_class(Mapper).before_configured() 3176 # initialize properties on all mappers 3177 # note that _mapper_registry is unordered, which 3178 # may randomly conceal/reveal issues related to 3179 # the order of mapper compilation 3180 3181 for mapper in list(_mapper_registry): 3182 if getattr(mapper, "_configure_failed", False): 3183 e = sa_exc.InvalidRequestError( 3184 "One or more mappers failed to initialize - " 3185 "can't proceed with initialization of other " 3186 "mappers. Triggering mapper: '%s'. " 3187 "Original exception was: %s" 3188 % (mapper, mapper._configure_failed) 3189 ) 3190 e._configure_failed = mapper._configure_failed 3191 raise e 3192 if not mapper.configured: 3193 try: 3194 mapper._post_configure_properties() 3195 mapper._expire_memoizations() 3196 mapper.dispatch.mapper_configured( 3197 mapper, mapper.class_ 3198 ) 3199 except Exception: 3200 exc = sys.exc_info()[1] 3201 if not hasattr(exc, "_configure_failed"): 3202 mapper._configure_failed = exc 3203 raise 3204 3205 Mapper._new_mappers = False 3206 finally: 3207 _already_compiling = False 3208 finally: 3209 _CONFIGURE_MUTEX.release() 3210 Mapper.dispatch._for_class(Mapper).after_configured() 3211 3212 3213def reconstructor(fn): 3214 """Decorate a method as the 'reconstructor' hook. 3215 3216 Designates a method as the "reconstructor", an ``__init__``-like 3217 method that will be called by the ORM after the instance has been 3218 loaded from the database or otherwise reconstituted. 3219 3220 The reconstructor will be invoked with no arguments. Scalar 3221 (non-collection) database-mapped attributes of the instance will 3222 be available for use within the function. Eagerly-loaded 3223 collections are generally not yet available and will usually only 3224 contain the first element. ORM state changes made to objects at 3225 this stage will not be recorded for the next flush() operation, so 3226 the activity within a reconstructor should be conservative. 3227 3228 .. seealso:: 3229 3230 :ref:`mapping_constructors` 3231 3232 :meth:`.InstanceEvents.load` 3233 3234 """ 3235 fn.__sa_reconstructor__ = True 3236 return fn 3237 3238 3239def validates(*names, **kw): 3240 r"""Decorate a method as a 'validator' for one or more named properties. 3241 3242 Designates a method as a validator, a method which receives the 3243 name of the attribute as well as a value to be assigned, or in the 3244 case of a collection, the value to be added to the collection. 3245 The function can then raise validation exceptions to halt the 3246 process from continuing (where Python's built-in ``ValueError`` 3247 and ``AssertionError`` exceptions are reasonable choices), or can 3248 modify or replace the value before proceeding. The function should 3249 otherwise return the given value. 3250 3251 Note that a validator for a collection **cannot** issue a load of that 3252 collection within the validation routine - this usage raises 3253 an assertion to avoid recursion overflows. This is a reentrant 3254 condition which is not supported. 3255 3256 :param \*names: list of attribute names to be validated. 3257 :param include_removes: if True, "remove" events will be 3258 sent as well - the validation function must accept an additional 3259 argument "is_remove" which will be a boolean. 3260 3261 :param include_backrefs: defaults to ``True``; if ``False``, the 3262 validation function will not emit if the originator is an attribute 3263 event related via a backref. This can be used for bi-directional 3264 :func:`.validates` usage where only one validator should emit per 3265 attribute operation. 3266 3267 .. versionadded:: 0.9.0 3268 3269 .. seealso:: 3270 3271 :ref:`simple_validators` - usage examples for :func:`.validates` 3272 3273 """ 3274 include_removes = kw.pop("include_removes", False) 3275 include_backrefs = kw.pop("include_backrefs", True) 3276 3277 def wrap(fn): 3278 fn.__sa_validators__ = names 3279 fn.__sa_validation_opts__ = { 3280 "include_removes": include_removes, 3281 "include_backrefs": include_backrefs, 3282 } 3283 return fn 3284 3285 return wrap 3286 3287 3288def _event_on_load(state, ctx): 3289 instrumenting_mapper = state.manager.info[_INSTRUMENTOR] 3290 if instrumenting_mapper._reconstructor: 3291 instrumenting_mapper._reconstructor(state.obj()) 3292 3293 3294def _event_on_first_init(manager, cls): 3295 """Initial mapper compilation trigger. 3296 3297 instrumentation calls this one when InstanceState 3298 is first generated, and is needed for legacy mutable 3299 attributes to work. 3300 """ 3301 3302 instrumenting_mapper = manager.info.get(_INSTRUMENTOR) 3303 if instrumenting_mapper: 3304 if Mapper._new_mappers: 3305 configure_mappers() 3306 3307 3308def _event_on_init(state, args, kwargs): 3309 """Run init_instance hooks. 3310 3311 This also includes mapper compilation, normally not needed 3312 here but helps with some piecemeal configuration 3313 scenarios (such as in the ORM tutorial). 3314 3315 """ 3316 3317 instrumenting_mapper = state.manager.info.get(_INSTRUMENTOR) 3318 if instrumenting_mapper: 3319 if Mapper._new_mappers: 3320 configure_mappers() 3321 if instrumenting_mapper._set_polymorphic_identity: 3322 instrumenting_mapper._set_polymorphic_identity(state) 3323 3324 3325class _ColumnMapping(dict): 3326 """Error reporting helper for mapper._columntoproperty.""" 3327 3328 __slots__ = ("mapper",) 3329 3330 def __init__(self, mapper): 3331 self.mapper = mapper 3332 3333 def __missing__(self, column): 3334 prop = self.mapper._props.get(column) 3335 if prop: 3336 raise orm_exc.UnmappedColumnError( 3337 "Column '%s.%s' is not available, due to " 3338 "conflicting property '%s':%r" 3339 % (column.table.name, column.name, column.key, prop) 3340 ) 3341 raise orm_exc.UnmappedColumnError( 3342 "No column %s is configured on mapper %s..." 3343 % (column, self.mapper) 3344 ) 3345