summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/mapper.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2014-01-21 20:10:23 -0500
committerMike Bayer <mike_mp@zzzcomputing.com>2014-01-21 20:10:23 -0500
commit07fb90c6cc14de6d02cf4be592c57d56831f59f7 (patch)
tree050ef65db988559c60f7aa40f2d0bfe24947e548 /lib/sqlalchemy/orm/mapper.py
parent560fd1d5ed643a1b0f95296f3b840c1963bbe67f (diff)
parentee1f4d21037690ad996c5eacf7e1200e92f2fbaa (diff)
downloadsqlalchemy-ticket_2501.tar.gz
Merge branch 'master' into ticket_2501ticket_2501
Conflicts: lib/sqlalchemy/orm/mapper.py
Diffstat (limited to 'lib/sqlalchemy/orm/mapper.py')
-rw-r--r--lib/sqlalchemy/orm/mapper.py582
1 files changed, 491 insertions, 91 deletions
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 5929aea6c..fc75a0cb5 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -1,5 +1,5 @@
# orm/mapper.py
-# Copyright (C) 2005-2013 the SQLAlchemy authors and contributors <see AUTHORS file>
+# Copyright (C) 2005-2014 the SQLAlchemy authors and contributors <see AUTHORS file>
#
# This module is part of SQLAlchemy and is released under
# the MIT License: http://www.opensource.org/licenses/mit-license.php
@@ -22,26 +22,18 @@ from collections import deque
from .. import sql, util, log, exc as sa_exc, event, schema, inspection
from ..sql import expression, visitors, operators, util as sql_util
-from . import instrumentation, attributes, \
- exc as orm_exc, events, loading, dependency
+from . import instrumentation, attributes, exc as orm_exc, loading, dependency
+from . import properties
from .interfaces import MapperProperty, _InspectionAttr, _MappedAttribute
-from .util import _INSTRUMENTOR, _class_to_mapper, \
- _state_mapper, class_mapper, \
- PathRegistry, state_str
+from .base import _class_to_mapper, _state_mapper, class_mapper, \
+ state_str, _INSTRUMENTOR
+from .path_registry import PathRegistry
+
import sys
-properties = util.importlater("sqlalchemy.orm", "properties")
-descriptor_props = util.importlater("sqlalchemy.orm", "descriptor_props")
-__all__ = (
- 'Mapper',
- '_mapper_registry',
- 'class_mapper',
- 'object_mapper',
- )
_mapper_registry = weakref.WeakKeyDictionary()
-_new_mappers = False
_already_compiling = False
_memoized_configured_property = util.group_expirable_memoized_property()
@@ -56,6 +48,8 @@ NO_ATTRIBUTE = util.symbol('NO_ATTRIBUTE')
_CONFIGURE_MUTEX = util.threading.RLock()
+@inspection._self_inspects
+@log.class_logger
class Mapper(_InspectionAttr):
"""Define the correlation of class attributes to database table
columns.
@@ -88,9 +82,12 @@ class Mapper(_InspectionAttr):
"""
+
+ _new_mappers = False
+
def __init__(self,
class_,
- local_table,
+ local_table=None,
properties=None,
primary_key=None,
non_primary=False,
@@ -118,10 +115,380 @@ class Mapper(_InspectionAttr):
legacy_is_orphan=False,
_compiled_cache_size=100,
):
- """Construct a new mapper.
+ """Return a new :class:`~.Mapper` object.
+
+ This function is typically used behind the scenes
+ via the Declarative extension. When using Declarative,
+ many of the usual :func:`.mapper` arguments are handled
+ by the Declarative extension itself, including ``class_``,
+ ``local_table``, ``properties``, and ``inherits``.
+ Other options are passed to :func:`.mapper` using
+ the ``__mapper_args__`` class variable::
+
+ class MyClass(Base):
+ __tablename__ = 'my_table'
+ id = Column(Integer, primary_key=True)
+ type = Column(String(50))
+ alt = Column("some_alt", Integer)
+
+ __mapper_args__ = {
+ 'polymorphic_on' : type
+ }
+
+
+ Explicit use of :func:`.mapper`
+ is often referred to as *classical mapping*. The above
+ declarative example is equivalent in classical form to::
+
+ my_table = Table("my_table", metadata,
+ Column('id', Integer, primary_key=True),
+ Column('type', String(50)),
+ Column("some_alt", Integer)
+ )
+
+ class MyClass(object):
+ pass
+
+ mapper(MyClass, my_table,
+ polymorphic_on=my_table.c.type,
+ properties={
+ 'alt':my_table.c.some_alt
+ })
+
+ .. seealso::
+
+ :ref:`classical_mapping` - discussion of direct usage of
+ :func:`.mapper`
+
+ :param class\_: The class to be mapped. When using Declarative,
+ this argument is automatically passed as the declared class
+ itself.
+
+ :param local_table: The :class:`.Table` or other selectable
+ to which the class is mapped. May be ``None`` if
+ this mapper inherits from another mapper using single-table
+ inheritance. When using Declarative, this argument is
+ automatically passed by the extension, based on what
+ is configured via the ``__table__`` argument or via the
+ :class:`.Table` produced as a result of the ``__tablename__``
+ and :class:`.Column` arguments present.
+
+ :param always_refresh: If True, all query operations for this mapped
+ class will overwrite all data within object instances that already
+ exist within the session, erasing any in-memory changes with
+ whatever information was loaded from the database. Usage of this
+ flag is highly discouraged; as an alternative, see the method
+ :meth:`.Query.populate_existing`.
+
+ :param allow_partial_pks: Defaults to True. Indicates that a
+ composite primary key with some NULL values should be considered as
+ possibly existing within the database. This affects whether a
+ mapper will assign an incoming row to an existing identity, as well
+ as if :meth:`.Session.merge` will check the database first for a
+ particular primary key value. A "partial primary key" can occur if
+ one has mapped to an OUTER JOIN, for example.
+
+ :param batch: Defaults to ``True``, indicating that save operations
+ of multiple entities can be batched together for efficiency.
+ Setting to False indicates
+ that an instance will be fully saved before saving the next
+ instance. This is used in the extremely rare case that a
+ :class:`.MapperEvents` listener requires being called
+ in between individual row persistence operations.
+
+ :param column_prefix: A string which will be prepended
+ to the mapped attribute name when :class:`.Column`
+ objects are automatically assigned as attributes to the
+ mapped class. Does not affect explicitly specified
+ column-based properties.
+
+ See the section :ref:`column_prefix` for an example.
+
+ :param concrete: If True, indicates this mapper should use concrete
+ table inheritance with its parent mapper.
+
+ See the section :ref:`concrete_inheritance` for an example.
+
+ :param eager_defaults: if True, the ORM will immediately fetch the
+ value of server-generated default values after an INSERT or UPDATE,
+ rather than leaving them as expired to be fetched on next access.
+ This can be used for event schemes where the server-generated values
+ are needed immediately before the flush completes. By default,
+ this scheme will emit an individual ``SELECT`` statement per row
+ inserted or updated, which note can add significant performance
+ overhead. However, if the
+ target database supports :term:`RETURNING`, the default values will be
+ returned inline with the INSERT or UPDATE statement, which can
+ greatly enhance performance for an application that needs frequent
+ access to just-generated server defaults.
+
+ .. versionchanged:: 0.9.0 The ``eager_defaults`` option can now
+ make use of :term:`RETURNING` for backends which support it.
+
+ :param exclude_properties: A list or set of string column names to
+ be excluded from mapping.
+
+ See :ref:`include_exclude_cols` for an example.
+
+ :param extension: A :class:`.MapperExtension` instance or
+ list of :class:`.MapperExtension` instances which will be applied
+ to all operations by this :class:`.Mapper`. **Deprecated.**
+ Please see :class:`.MapperEvents`.
+
+ :param include_properties: An inclusive list or set of string column
+ names to map.
+
+ See :ref:`include_exclude_cols` for an example.
+
+ :param inherits: A mapped class or the corresponding :class:`.Mapper`
+ of one indicating a superclass to which this :class:`.Mapper`
+ should *inherit* from. The mapped class here must be a subclass
+ of the other mapper's class. When using Declarative, this argument
+ is passed automatically as a result of the natural class
+ hierarchy of the declared classes.
+
+ .. seealso::
+
+ :ref:`inheritance_toplevel`
+
+ :param inherit_condition: For joined table inheritance, a SQL
+ expression which will
+ define how the two tables are joined; defaults to a natural join
+ between the two tables.
+
+ :param inherit_foreign_keys: When ``inherit_condition`` is used and the
+ columns present are missing a :class:`.ForeignKey` configuration,
+ this parameter can be used to specify which columns are "foreign".
+ In most cases can be left as ``None``.
+
+ :param legacy_is_orphan: Boolean, defaults to ``False``.
+ When ``True``, specifies that "legacy" orphan consideration
+ is to be applied to objects mapped by this mapper, which means
+ that a pending (that is, not persistent) object is auto-expunged
+ from an owning :class:`.Session` only when it is de-associated
+ from *all* parents that specify a ``delete-orphan`` cascade towards
+ this mapper. The new default behavior is that the object is auto-expunged
+ when it is de-associated with *any* of its parents that specify
+ ``delete-orphan`` cascade. This behavior is more consistent with
+ that of a persistent object, and allows behavior to be consistent
+ in more scenarios independently of whether or not an orphanable
+ object has been flushed yet or not.
+
+ See the change note and example at :ref:`legacy_is_orphan_addition`
+ for more detail on this change.
+
+ .. versionadded:: 0.8 - the consideration of a pending object as
+ an "orphan" has been modified to more closely match the
+ behavior as that of persistent objects, which is that the object
+ is expunged from the :class:`.Session` as soon as it is
+ de-associated from any of its orphan-enabled parents. Previously,
+ the pending object would be expunged only if de-associated
+ from all of its orphan-enabled parents. The new flag ``legacy_is_orphan``
+ is added to :func:`.orm.mapper` which re-establishes the
+ legacy behavior.
+
+ :param non_primary: Specify that this :class:`.Mapper` is in addition
+ to the "primary" mapper, that is, the one used for persistence.
+ The :class:`.Mapper` created here may be used for ad-hoc
+ mapping of the class to an alternate selectable, for loading
+ only.
+
+ The ``non_primary`` feature is rarely needed with modern
+ usage.
+
+ :param order_by: A single :class:`.Column` or list of :class:`.Column`
+ objects for which selection operations should use as the default
+ ordering for entities. By default mappers have no pre-defined
+ ordering.
+
+ :param passive_updates: Indicates UPDATE behavior of foreign key
+ columns when a primary key column changes on a joined-table
+ inheritance mapping. Defaults to ``True``.
+
+ When True, it is assumed that ON UPDATE CASCADE is configured on
+ the foreign key in the database, and that the database will handle
+ propagation of an UPDATE from a source column to dependent columns
+ on joined-table rows.
+
+ When False, it is assumed that the database does not enforce
+ referential integrity and will not be issuing its own CASCADE
+ operation for an update. The :class:`.Mapper` here will
+ emit an UPDATE statement for the dependent columns during a
+ primary key change.
+
+ ..seealso::
+
+ :ref:`passive_updates` - description of a similar feature as
+ used with :func:`.relationship`
+
+ :param polymorphic_on: Specifies the column, attribute, or
+ SQL expression used to determine the target class for an
+ incoming row, when inheriting classes are present.
+
+ This value is commonly a :class:`.Column` object that's
+ present in the mapped :class:`.Table`::
+
+ class Employee(Base):
+ __tablename__ = 'employee'
+
+ id = Column(Integer, primary_key=True)
+ discriminator = Column(String(50))
+
+ __mapper_args__ = {
+ "polymorphic_on":discriminator,
+ "polymorphic_identity":"employee"
+ }
+
+ It may also be specified
+ as a SQL expression, as in this example where we
+ use the :func:`.case` construct to provide a conditional
+ approach::
+
+ class Employee(Base):
+ __tablename__ = 'employee'
+
+ id = Column(Integer, primary_key=True)
+ discriminator = Column(String(50))
+
+ __mapper_args__ = {
+ "polymorphic_on":case([
+ (discriminator == "EN", "engineer"),
+ (discriminator == "MA", "manager"),
+ ], else_="employee"),
+ "polymorphic_identity":"employee"
+ }
+
+ It may also refer to any attribute
+ configured with :func:`.column_property`, or to the
+ string name of one::
+
+ class Employee(Base):
+ __tablename__ = 'employee'
+
+ id = Column(Integer, primary_key=True)
+ discriminator = Column(String(50))
+ employee_type = column_property(
+ case([
+ (discriminator == "EN", "engineer"),
+ (discriminator == "MA", "manager"),
+ ], else_="employee")
+ )
- Mappers are normally constructed via the
- :func:`~sqlalchemy.orm.mapper` function. See for details.
+ __mapper_args__ = {
+ "polymorphic_on":employee_type,
+ "polymorphic_identity":"employee"
+ }
+
+ .. versionchanged:: 0.7.4
+ ``polymorphic_on`` may be specified as a SQL expression,
+ or refer to any attribute configured with
+ :func:`.column_property`, or to the string name of one.
+
+ When setting ``polymorphic_on`` to reference an
+ attribute or expression that's not present in the
+ locally mapped :class:`.Table`, yet the value
+ of the discriminator should be persisted to the database,
+ the value of the
+ discriminator is not automatically set on new
+ instances; this must be handled by the user,
+ either through manual means or via event listeners.
+ A typical approach to establishing such a listener
+ looks like::
+
+ from sqlalchemy import event
+ from sqlalchemy.orm import object_mapper
+
+ @event.listens_for(Employee, "init", propagate=True)
+ def set_identity(instance, *arg, **kw):
+ mapper = object_mapper(instance)
+ instance.discriminator = mapper.polymorphic_identity
+
+ Where above, we assign the value of ``polymorphic_identity``
+ for the mapped class to the ``discriminator`` attribute,
+ thus persisting the value to the ``discriminator`` column
+ in the database.
+
+ .. seealso::
+
+ :ref:`inheritance_toplevel`
+
+ :param polymorphic_identity: Specifies the value which
+ identifies this particular class as returned by the
+ column expression referred to by the ``polymorphic_on``
+ setting. As rows are received, the value corresponding
+ to the ``polymorphic_on`` column expression is compared
+ to this value, indicating which subclass should
+ be used for the newly reconstructed object.
+
+ :param properties: A dictionary mapping the string names of object
+ attributes to :class:`.MapperProperty` instances, which define the
+ persistence behavior of that attribute. Note that :class:`.Column`
+ objects present in
+ the mapped :class:`.Table` are automatically placed into
+ ``ColumnProperty`` instances upon mapping, unless overridden.
+ When using Declarative, this argument is passed automatically,
+ based on all those :class:`.MapperProperty` instances declared
+ in the declared class body.
+
+ :param primary_key: A list of :class:`.Column` objects which define the
+ primary key to be used against this mapper's selectable unit.
+ This is normally simply the primary key of the ``local_table``, but
+ can be overridden here.
+
+ :param version_id_col: A :class:`.Column`
+ that will be used to keep a running version id of rows
+ in the table. This is used to detect concurrent updates or
+ the presence of stale data in a flush. The methodology is to
+ detect if an UPDATE statement does not match the last known
+ version id, a
+ :class:`~sqlalchemy.orm.exc.StaleDataError` exception is
+ thrown.
+ By default, the column must be of :class:`.Integer` type,
+ unless ``version_id_generator`` specifies an alternative version
+ generator.
+
+ .. seealso::
+
+ :ref:`mapper_version_counter` - discussion of version counting
+ and rationale.
+
+ :param version_id_generator: Define how new version ids should
+ be generated. Defaults to ``None``, which indicates that
+ a simple integer counting scheme be employed. To provide a custom
+ versioning scheme, provide a callable function of the form::
+
+ def generate_version(version):
+ return next_version
+
+ Alternatively, server-side versioning functions such as triggers,
+ or programmatic versioning schemes outside of the version id generator
+ may be used, by specifying the value ``False``.
+ Please see :ref:`server_side_version_counter` for a discussion
+ of important points when using this option.
+
+ .. versionadded:: 0.9.0 ``version_id_generator`` supports server-side
+ version number generation.
+
+ .. seealso::
+
+ :ref:`custom_version_counter`
+
+ :ref:`server_side_version_counter`
+
+
+ :param with_polymorphic: A tuple in the form ``(<classes>,
+ <selectable>)`` indicating the default style of "polymorphic"
+ loading, that is, which tables are queried at once. <classes> is
+ any single or list of mappers and/or classes indicating the
+ inherited classes that should be loaded at once. The special value
+ ``'*'`` may be used to indicate all descending classes should be
+ loaded immediately. The second tuple argument <selectable>
+ indicates a selectable that will be used to query for multiple
+ classes.
+
+ .. seealso::
+
+ :ref:`with_polymorphic` - discussion of polymorphic querying techniques.
"""
@@ -138,9 +505,19 @@ class Mapper(_InspectionAttr):
self.order_by = order_by
self.always_refresh = always_refresh
- self.version_id_col = version_id_col
- self.version_id_generator = version_id_generator or \
- (lambda x: (x or 0) + 1)
+
+ if isinstance(version_id_col, MapperProperty):
+ self.version_id_prop = version_id_col
+ self.version_id_col = None
+ else:
+ self.version_id_col = version_id_col
+ if version_id_generator is False:
+ self.version_id_generator = False
+ elif version_id_generator is None:
+ self.version_id_generator = lambda x: (x or 0) + 1
+ else:
+ self.version_id_generator = version_id_generator
+
self.concrete = concrete
self.single = False
self.inherits = inherits
@@ -218,7 +595,7 @@ class Mapper(_InspectionAttr):
# configure_mappers() until construction succeeds)
_CONFIGURE_MUTEX.acquire()
try:
- events._MapperEventsHold.populate(class_, self)
+ self.dispatch._events._new_mapper_instance(class_, self)
self._configure_inheritance()
self._configure_legacy_instrument_class()
self._configure_class_instrumentation()
@@ -226,8 +603,7 @@ class Mapper(_InspectionAttr):
self._configure_properties()
self._configure_polymorphic_setter()
self._configure_pks()
- global _new_mappers
- _new_mappers = True
+ Mapper._new_mappers = True
self._log("constructed")
self._expire_memoizations()
finally:
@@ -252,7 +628,7 @@ class Mapper(_InspectionAttr):
def entity(self):
"""Part of the inspection API.
- Returns self.class_.
+ Returns self.class\_.
"""
return self.class_
@@ -272,7 +648,9 @@ class Mapper(_InspectionAttr):
this :class:`.Mapper` represents. If this mapper is a
single-table inheriting mapper, local_table will be ``None``.
- See also :attr:`~.Mapper.mapped_table`.
+ .. seealso::
+
+ :attr:`~.Mapper.mapped_table`.
"""
@@ -290,7 +668,9 @@ class Mapper(_InspectionAttr):
subclass. For single-table inheritance mappers, mapped_table
references the base table.
- See also :attr:`~.Mapper.local_table`.
+ .. seealso::
+
+ :attr:`~.Mapper.local_table`.
"""
@@ -309,7 +689,9 @@ class Mapper(_InspectionAttr):
This is a *read only* attribute determined during mapper construction.
Behavior is undefined if directly modified.
- See also :func:`.configure_mappers`.
+ .. seealso::
+
+ :func:`.configure_mappers`.
"""
@@ -478,8 +860,6 @@ class Mapper(_InspectionAttr):
c = None
"""A synonym for :attr:`~.Mapper.columns`."""
- dispatch = event.dispatcher(events.MapperEvents)
-
@util.memoized_property
def _path_registry(self):
return PathRegistry.per_mapper(self)
@@ -489,7 +869,7 @@ class Mapper(_InspectionAttr):
being present."""
# a set of all mappers which inherit from this one.
- self._inheriting_mappers = util.WeakSet()
+ self._inheriting_mappers = util.WeakSequence()
if self.inherits:
if isinstance(self.inherits, type):
@@ -563,7 +943,7 @@ class Mapper(_InspectionAttr):
self.polymorphic_map = self.inherits.polymorphic_map
self.batch = self.inherits.batch
- self.inherits._inheriting_mappers.add(self)
+ self.inherits._inheriting_mappers.append(self)
self.base_mapper = self.inherits.base_mapper
self.passive_updates = self.inherits.passive_updates
self._all_tables = self.inherits._all_tables
@@ -630,7 +1010,7 @@ class Mapper(_InspectionAttr):
self.batch = self.inherits.batch
for mp in self.self_and_descendants:
mp.base_mapper = self.inherits.base_mapper
- self.inherits._inheriting_mappers.add(self)
+ self.inherits._inheriting_mappers.append(self)
self.passive_updates = self.inherits.passive_updates
self._all_tables = self.inherits._all_tables
for key, prop in mapper._props.items():
@@ -735,30 +1115,20 @@ class Mapper(_InspectionAttr):
self._reconstructor = method
event.listen(manager, 'load', _event_on_load, raw=True)
elif hasattr(method, '__sa_validators__'):
- include_removes = getattr(method,
- "__sa_include_removes__", False)
+ validation_opts = method.__sa_validation_opts__
for name in method.__sa_validators__:
self.validators = self.validators.union(
- {name: (method, include_removes)}
+ {name: (method, validation_opts)}
)
manager.info[_INSTRUMENTOR] = self
- @util.deprecated("0.7", message=":meth:`.Mapper.compile` "
- "is replaced by :func:`.configure_mappers`")
- def compile(self):
- """Initialize the inter-mapper relationships of all mappers that
- have been constructed thus far.
+ @classmethod
+ def _configure_all(cls):
+ """Class-level path to the :func:`.configure_mappers` call.
"""
configure_mappers()
- return self
-
- @property
- @util.deprecated("0.7", message=":attr:`.Mapper.compiled` "
- "is replaced by :attr:`.Mapper.configured`")
- def compiled(self):
- return self.configured
def dispose(self):
# Disable any attribute-based compilation.
@@ -956,7 +1326,7 @@ class Mapper(_InspectionAttr):
prop = self.polymorphic_on
self.polymorphic_on = prop.columns[0]
polymorphic_key = prop.key
- elif not expression.is_column(self.polymorphic_on):
+ elif not expression._is_column(self.polymorphic_on):
# polymorphic_on is not a Column and not a ColumnProperty;
# not supported right now.
raise sa_exc.ArgumentError(
@@ -1080,6 +1450,13 @@ class Mapper(_InspectionAttr):
_validate_polymorphic_identity = None
@_memoized_configured_property
+ def _version_id_prop(self):
+ if self.version_id_col is not None:
+ return self._columntoproperty[self.version_id_col]
+ else:
+ return None
+
+ @_memoized_configured_property
def _acceptable_polymorphic_identities(self):
identities = set()
@@ -1205,7 +1582,7 @@ class Mapper(_InspectionAttr):
# generate a properties.ColumnProperty
columns = util.to_list(prop)
column = columns[0]
- if not expression.is_column(column):
+ if not expression._is_column(column):
raise sa_exc.ArgumentError(
"%s=%r is not an instance of MapperProperty or Column"
% (key, prop))
@@ -1369,7 +1746,7 @@ class Mapper(_InspectionAttr):
"""return a MapperProperty associated with the given key.
"""
- if _configure_mappers and _new_mappers:
+ if _configure_mappers and Mapper._new_mappers:
configure_mappers()
try:
@@ -1387,7 +1764,7 @@ class Mapper(_InspectionAttr):
@property
def iterate_properties(self):
"""return an iterator of all MapperProperty objects."""
- if _new_mappers:
+ if Mapper._new_mappers:
configure_mappers()
return iter(self._props.values())
@@ -1461,7 +1838,7 @@ class Mapper(_InspectionAttr):
@_memoized_configured_property
def _with_polymorphic_mappers(self):
- if _new_mappers:
+ if Mapper._new_mappers:
configure_mappers()
if not self.with_polymorphic:
return []
@@ -1493,7 +1870,7 @@ class Mapper(_InspectionAttr):
Normally, this is equivalent to :attr:`.mapped_table`, unless
the ``with_polymorphic`` feature is in use, in which case the
- full "polymoprhic" selectable is returned.
+ full "polymorphic" selectable is returned.
"""
return self._with_polymorphic_selectable
@@ -1568,7 +1945,7 @@ class Mapper(_InspectionAttr):
:attr:`.Mapper.all_orm_descriptors`
"""
- if _new_mappers:
+ if Mapper._new_mappers:
configure_mappers()
return util.ImmutableProperties(self._props)
@@ -1611,23 +1988,23 @@ class Mapper(_InspectionAttr):
"""Return a namespace of all :class:`.SynonymProperty`
properties maintained by this :class:`.Mapper`.
- See also:
+ .. seealso::
- :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty`
- objects.
+ :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty`
+ objects.
"""
- return self._filter_properties(descriptor_props.SynonymProperty)
+ return self._filter_properties(properties.SynonymProperty)
@_memoized_configured_property
def column_attrs(self):
"""Return a namespace of all :class:`.ColumnProperty`
properties maintained by this :class:`.Mapper`.
- See also:
+ .. seealso::
- :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty`
- objects.
+ :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty`
+ objects.
"""
return self._filter_properties(properties.ColumnProperty)
@@ -1637,10 +2014,10 @@ class Mapper(_InspectionAttr):
"""Return a namespace of all :class:`.RelationshipProperty`
properties maintained by this :class:`.Mapper`.
- See also:
+ .. seealso::
- :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty`
- objects.
+ :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty`
+ objects.
"""
return self._filter_properties(properties.RelationshipProperty)
@@ -1650,16 +2027,16 @@ class Mapper(_InspectionAttr):
"""Return a namespace of all :class:`.CompositeProperty`
properties maintained by this :class:`.Mapper`.
- See also:
+ .. seealso::
- :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty`
- objects.
+ :attr:`.Mapper.attrs` - namespace of all :class:`.MapperProperty`
+ objects.
"""
- return self._filter_properties(descriptor_props.CompositeProperty)
+ return self._filter_properties(properties.CompositeProperty)
def _filter_properties(self, type_):
- if _new_mappers:
+ if Mapper._new_mappers:
configure_mappers()
return util.ImmutableProperties(util.OrderedDict(
(k, v) for k, v in self._props.items()
@@ -1805,7 +2182,7 @@ class Mapper(_InspectionAttr):
while stack:
item = stack.popleft()
descendants.append(item)
- stack.extend(sorted(item._inheriting_mappers, key=lambda m: m.class_.__name__))
+ stack.extend(item._inheriting_mappers)
return util.WeakSequence(descendants)
def polymorphic_iterator(self):
@@ -1835,10 +2212,11 @@ class Mapper(_InspectionAttr):
"""Return an identity-map key for use in storing/retrieving an
item from the identity map.
- row
- A ``sqlalchemy.engine.RowProxy`` instance or a
- dictionary corresponding result-set ``ColumnElement``
- instances to their values within a row.
+ :param row: A :class:`.RowProxy` instance. The columns which are mapped
+ by this :class:`.Mapper` should be locatable in the row, preferably
+ via the :class:`.Column` object directly (as is the case when a
+ :func:`.select` construct is executed), or via string names of the form
+ ``<tablename>_<colname>``.
"""
pk_cols = self.primary_key
@@ -1852,8 +2230,7 @@ class Mapper(_InspectionAttr):
"""Return an identity-map key for use in storing/retrieving an
item from an identity map.
- primary_key
- A list of values indicating the identifier.
+ :param primary_key: A list of values indicating the identifier.
"""
return self._identity_class, tuple(primary_key)
@@ -1862,6 +2239,11 @@ class Mapper(_InspectionAttr):
"""Return the identity key for the given instance, based on
its primary key attributes.
+ If the instance's state is expired, calling this method
+ will result in a database check to see if the object has been deleted.
+ If the row no longer exists,
+ :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised.
+
This value is typically also found on the instance state under the
attribute name `key`.
@@ -1882,6 +2264,11 @@ class Mapper(_InspectionAttr):
"""Return the list of primary key values for the given
instance.
+ If the instance's state is expired, calling this method
+ will result in a database check to see if the object has been deleted.
+ If the row no longer exists,
+ :class:`~sqlalchemy.orm.exc.ObjectDeletedError` is raised.
+
"""
state = attributes.instance_state(instance)
return self._primary_key_from_state(state)
@@ -2070,9 +2457,9 @@ class Mapper(_InspectionAttr):
dep is not None and \
dep is not parent and \
dep.inherit_condition is not None:
- cols = set(sql_util.find_columns(dep.inherit_condition))
+ cols = set(sql_util._find_columns(dep.inherit_condition))
if parent.inherit_condition is not None:
- cols = cols.union(sql_util.find_columns(
+ cols = cols.union(sql_util._find_columns(
parent.inherit_condition))
return fk.parent not in cols and fk.column not in cols
else:
@@ -2107,14 +2494,13 @@ class Mapper(_InspectionAttr):
for m in self.iterate_to_root():
if m._inherits_equated_pairs and \
cols.intersection(
- [l for l, r in m._inherits_equated_pairs]):
+ util.reduce(set.union,
+ [l.proxy_set for l, r in m._inherits_equated_pairs])
+ ):
result[table].append((m, m._inherits_equated_pairs))
return result
-inspection._self_inspects(Mapper)
-log.class_logger(Mapper)
-
def configure_mappers():
"""Initialize the inter-mapper relationships of all mappers that
@@ -2125,8 +2511,7 @@ def configure_mappers():
"""
- global _new_mappers
- if not _new_mappers:
+ if not Mapper._new_mappers:
return
_call_configured = None
@@ -2139,7 +2524,7 @@ def configure_mappers():
try:
# double-check inside mutex
- if not _new_mappers:
+ if not Mapper._new_mappers:
return
# initialize properties on all mappers
@@ -2168,7 +2553,7 @@ def configure_mappers():
mapper._configure_failed = exc
raise
- _new_mappers = False
+ Mapper._new_mappers = False
finally:
_already_compiling = False
finally:
@@ -2220,13 +2605,28 @@ def validates(*names, **kw):
argument "is_remove" which will be a boolean.
.. versionadded:: 0.7.7
+ :param include_backrefs: defaults to ``True``; if ``False``, the
+ validation function will not emit if the originator is an attribute
+ event related via a backref. This can be used for bi-directional
+ :func:`.validates` usage where only one validator should emit per
+ attribute operation.
+
+ .. versionadded:: 0.9.0
+
+ .. seealso::
+
+ :ref:`simple_validators` - usage examples for :func:`.validates`
"""
include_removes = kw.pop('include_removes', False)
+ include_backrefs = kw.pop('include_backrefs', True)
def wrap(fn):
fn.__sa_validators__ = names
- fn.__sa_include_removes__ = include_removes
+ fn.__sa_validation_opts__ = {
+ "include_removes": include_removes,
+ "include_backrefs": include_backrefs
+ }
return fn
return wrap
@@ -2247,7 +2647,7 @@ def _event_on_first_init(manager, cls):
instrumenting_mapper = manager.info.get(_INSTRUMENTOR)
if instrumenting_mapper:
- if _new_mappers:
+ if Mapper._new_mappers:
configure_mappers()
@@ -2262,7 +2662,7 @@ def _event_on_init(state, args, kwargs):
instrumenting_mapper = state.manager.info.get(_INSTRUMENTOR)
if instrumenting_mapper:
- if _new_mappers:
+ if Mapper._new_mappers:
configure_mappers()
if instrumenting_mapper._set_polymorphic_identity:
instrumenting_mapper._set_polymorphic_identity(state)