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