summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/mapper.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/orm/mapper.py')
-rw-r--r--lib/sqlalchemy/orm/mapper.py369
1 files changed, 28 insertions, 341 deletions
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 044c52d5c..761e1af56 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -13,24 +13,23 @@ This is a semi-private module; the main configurational API of the ORM is
available in :class:`~sqlalchemy.orm.`.
"""
-
+from __future__ import absolute_import
import types
import weakref
import operator
from itertools import chain, groupby
-deque = __import__('collections').deque
+from collections import deque
-from sqlalchemy import sql, util, log, exc as sa_exc, event, schema
-from sqlalchemy.sql import expression, visitors, operators, util as sqlutil
-from sqlalchemy.orm import instrumentation, attributes, sync, \
- exc as orm_exc, unitofwork, events
-from sqlalchemy.orm.interfaces import MapperProperty, EXT_CONTINUE, \
+from .. import sql, util, log, exc as sa_exc, event, schema
+from ..sql import expression, visitors, operators, util as sql_util
+from . import instrumentation, attributes, sync, \
+ exc as orm_exc, unitofwork, events, loading
+from .interfaces import MapperProperty, EXT_CONTINUE, \
PropComparator
-from sqlalchemy.orm.util import _INSTRUMENTOR, _class_to_mapper, \
+from .util import _INSTRUMENTOR, _class_to_mapper, \
_state_mapper, class_mapper, instance_str, state_str,\
- PathRegistry
-
+ PathRegistry, _none_set
import sys
sessionlib = util.importlater("sqlalchemy.orm", "session")
properties = util.importlater("sqlalchemy.orm", "properties")
@@ -46,7 +45,6 @@ __all__ = (
_mapper_registry = weakref.WeakKeyDictionary()
_new_mappers = False
_already_compiling = False
-_none_set = frozenset([None])
_memoized_configured_property = util.group_expirable_memoized_property()
@@ -466,7 +464,7 @@ class Mapper(object):
# immediate table of the inherited mapper, not its
# full table which could pull in other stuff we dont
# want (allows test/inheritance.InheritTest4 to pass)
- self.inherit_condition = sqlutil.join_condition(
+ self.inherit_condition = sql_util.join_condition(
self.inherits.local_table,
self.local_table)
self.mapped_table = sql.join(
@@ -475,7 +473,7 @@ class Mapper(object):
self.inherit_condition)
fks = util.to_set(self.inherit_foreign_keys)
- self._inherits_equated_pairs = sqlutil.criterion_as_pairs(
+ self._inherits_equated_pairs = sql_util.criterion_as_pairs(
self.mapped_table.onclause,
consider_as_foreign_keys=fks)
else:
@@ -717,7 +715,7 @@ class Mapper(object):
def _configure_pks(self):
- self.tables = sqlutil.find_tables(self.mapped_table)
+ self.tables = sql_util.find_tables(self.mapped_table)
self._pks_by_table = {}
self._cols_by_table = {}
@@ -782,12 +780,12 @@ class Mapper(object):
# determine primary key from argument or mapped_table pks -
# reduce to the minimal set of columns
if self._primary_key_argument:
- primary_key = sqlutil.reduce_columns(
+ primary_key = sql_util.reduce_columns(
[self.mapped_table.corresponding_column(c) for c in
self._primary_key_argument],
ignore_nonexistent_tables=True)
else:
- primary_key = sqlutil.reduce_columns(
+ primary_key = sql_util.reduce_columns(
self._pks_by_table[self.mapped_table],
ignore_nonexistent_tables=True)
@@ -1289,7 +1287,7 @@ class Mapper(object):
mappers = []
if selectable is not None:
- tables = set(sqlutil.find_tables(selectable,
+ tables = set(sql_util.find_tables(selectable,
include_aliases=True))
mappers = [m for m in mappers if m.local_table in tables]
@@ -1698,10 +1696,12 @@ class Mapper(object):
if self.inherits and not self.concrete:
statement = self._optimized_get_statement(state, attribute_names)
if statement is not None:
- result = session.query(self).from_statement(statement).\
- _load_on_ident(None,
- only_load_props=attribute_names,
- refresh_state=state)
+ result = loading.load_on_ident(
+ session.query(self).from_statement(statement),
+ None,
+ only_load_props=attribute_names,
+ refresh_state=state
+ )
if result is False:
if has_key:
@@ -1727,10 +1727,11 @@ class Mapper(object):
% state_str(state))
return
- result = session.query(self)._load_on_ident(
- identity_key,
- refresh_state=state,
- only_load_props=attribute_names)
+ result = loading.load_on_ident(
+ session.query(self),
+ identity_key,
+ refresh_state=state,
+ only_load_props=attribute_names)
# if instance is pending, a refresh operation
# may not complete (even if PK attributes are assigned)
@@ -1750,7 +1751,7 @@ class Mapper(object):
props = self._props
tables = set(chain(
- *[sqlutil.find_tables(c, check_columns=True)
+ *[sql_util.find_tables(c, check_columns=True)
for key in attribute_names
for c in props[key].columns]
))
@@ -1866,7 +1867,7 @@ class Mapper(object):
for t in mapper.tables:
table_to_mapper[t] = mapper
- sorted_ = sqlutil.sort_tables(table_to_mapper.iterkeys())
+ sorted_ = sql_util.sort_tables(table_to_mapper.iterkeys())
ret = util.OrderedDict()
for t in sorted_:
ret[t] = table_to_mapper[t]
@@ -1924,320 +1925,6 @@ class Mapper(object):
return result
-
- def _instance_processor(self, context, path, adapter,
- polymorphic_from=None,
- only_load_props=None, refresh_state=None,
- polymorphic_discriminator=None):
-
- """Produce a mapper level row processor callable
- which processes rows into mapped instances."""
-
- # note that this method, most of which exists in a closure
- # called _instance(), resists being broken out, as
- # attempts to do so tend to add significant function
- # call overhead. _instance() is the most
- # performance-critical section in the whole ORM.
-
- pk_cols = self.primary_key
-
- if polymorphic_from or refresh_state:
- polymorphic_on = None
- else:
- if polymorphic_discriminator is not None:
- polymorphic_on = polymorphic_discriminator
- else:
- polymorphic_on = self.polymorphic_on
- polymorphic_instances = util.PopulateDict(
- self._configure_subclass_mapper(
- context, path, adapter)
- )
-
- version_id_col = self.version_id_col
-
- if adapter:
- pk_cols = [adapter.columns[c] for c in pk_cols]
- if polymorphic_on is not None:
- polymorphic_on = adapter.columns[polymorphic_on]
- if version_id_col is not None:
- version_id_col = adapter.columns[version_id_col]
-
- identity_class = self._identity_class
-
- new_populators = []
- existing_populators = []
- eager_populators = []
- load_path = context.query._current_path + path \
- if context.query._current_path.path \
- else path
-
- def populate_state(state, dict_, row, isnew, only_load_props):
- if isnew:
- if context.propagate_options:
- state.load_options = context.propagate_options
- if state.load_options:
- state.load_path = load_path
-
- if not new_populators:
- self._populators(context, path, row, adapter,
- new_populators,
- existing_populators,
- eager_populators
- )
-
- if isnew:
- populators = new_populators
- else:
- populators = existing_populators
-
- if only_load_props is None:
- for key, populator in populators:
- populator(state, dict_, row)
- elif only_load_props:
- for key, populator in populators:
- if key in only_load_props:
- populator(state, dict_, row)
-
- session_identity_map = context.session.identity_map
-
- listeners = self.dispatch
-
- translate_row = listeners.translate_row or None
- create_instance = listeners.create_instance or None
- populate_instance = listeners.populate_instance or None
- append_result = listeners.append_result or None
- populate_existing = context.populate_existing or self.always_refresh
- invoke_all_eagers = context.invoke_all_eagers
-
- if self.allow_partial_pks:
- is_not_primary_key = _none_set.issuperset
- else:
- is_not_primary_key = _none_set.issubset
-
- def _instance(row, result):
- if not new_populators and invoke_all_eagers:
- self._populators(context, path, row, adapter,
- new_populators,
- existing_populators,
- eager_populators
- )
-
- if translate_row:
- for fn in translate_row:
- ret = fn(self, context, row)
- if ret is not EXT_CONTINUE:
- row = ret
- break
-
- if polymorphic_on is not None:
- discriminator = row[polymorphic_on]
- if discriminator is not None:
- _instance = polymorphic_instances[discriminator]
- if _instance:
- return _instance(row, result)
-
- # determine identity key
- if refresh_state:
- identitykey = refresh_state.key
- if identitykey is None:
- # super-rare condition; a refresh is being called
- # on a non-instance-key instance; this is meant to only
- # occur within a flush()
- identitykey = self._identity_key_from_state(refresh_state)
- else:
- identitykey = (
- identity_class,
- tuple([row[column] for column in pk_cols])
- )
-
- instance = session_identity_map.get(identitykey)
- if instance is not None:
- state = attributes.instance_state(instance)
- dict_ = attributes.instance_dict(instance)
-
- isnew = state.runid != context.runid
- currentload = not isnew
- loaded_instance = False
-
- if not currentload and \
- version_id_col is not None and \
- context.version_check and \
- self._get_state_attr_by_column(
- state,
- dict_,
- self.version_id_col) != \
- row[version_id_col]:
-
- raise orm_exc.StaleDataError(
- "Instance '%s' has version id '%s' which "
- "does not match database-loaded version id '%s'."
- % (state_str(state),
- self._get_state_attr_by_column(
- state, dict_,
- self.version_id_col),
- row[version_id_col]))
- elif refresh_state:
- # out of band refresh_state detected (i.e. its not in the
- # session.identity_map) honor it anyway. this can happen
- # if a _get() occurs within save_obj(), such as
- # when eager_defaults is True.
- state = refresh_state
- instance = state.obj()
- dict_ = attributes.instance_dict(instance)
- isnew = state.runid != context.runid
- currentload = True
- loaded_instance = False
- else:
- # check for non-NULL values in the primary key columns,
- # else no entity is returned for the row
- if is_not_primary_key(identitykey[1]):
- return None
-
- isnew = True
- currentload = True
- loaded_instance = True
-
- if create_instance:
- for fn in create_instance:
- instance = fn(self, context,
- row, self.class_)
- if instance is not EXT_CONTINUE:
- manager = attributes.manager_of_class(
- instance.__class__)
- # TODO: if manager is None, raise a friendly error
- # about returning instances of unmapped types
- manager.setup_instance(instance)
- break
- else:
- instance = self.class_manager.new_instance()
- else:
- instance = self.class_manager.new_instance()
-
- dict_ = attributes.instance_dict(instance)
- state = attributes.instance_state(instance)
- state.key = identitykey
-
- # attach instance to session.
- state.session_id = context.session.hash_key
- session_identity_map.add(state)
-
- if currentload or populate_existing:
- # state is being fully loaded, so populate.
- # add to the "context.progress" collection.
- if isnew:
- state.runid = context.runid
- context.progress[state] = dict_
-
- if populate_instance:
- for fn in populate_instance:
- ret = fn(self, context, row, state,
- only_load_props=only_load_props,
- instancekey=identitykey, isnew=isnew)
- if ret is not EXT_CONTINUE:
- break
- else:
- populate_state(state, dict_, row, isnew, only_load_props)
- else:
- populate_state(state, dict_, row, isnew, only_load_props)
-
- if loaded_instance:
- state.manager.dispatch.load(state, context)
- elif isnew:
- state.manager.dispatch.refresh(state, context, only_load_props)
-
- elif state in context.partials or state.unloaded or eager_populators:
- # state is having a partial set of its attributes
- # refreshed. Populate those attributes,
- # and add to the "context.partials" collection.
- if state in context.partials:
- isnew = False
- (d_, attrs) = context.partials[state]
- else:
- isnew = True
- attrs = state.unloaded
- context.partials[state] = (dict_, attrs)
-
- if populate_instance:
- for fn in populate_instance:
- ret = fn(self, context, row, state,
- only_load_props=attrs,
- instancekey=identitykey, isnew=isnew)
- if ret is not EXT_CONTINUE:
- break
- else:
- populate_state(state, dict_, row, isnew, attrs)
- else:
- populate_state(state, dict_, row, isnew, attrs)
-
- for key, pop in eager_populators:
- if key not in state.unloaded:
- pop(state, dict_, row)
-
- if isnew:
- state.manager.dispatch.refresh(state, context, attrs)
-
-
- if result is not None:
- if append_result:
- for fn in append_result:
- if fn(self, context, row, state,
- result, instancekey=identitykey,
- isnew=isnew) is not EXT_CONTINUE:
- break
- else:
- result.append(instance)
- else:
- result.append(instance)
-
- return instance
- return _instance
-
- def _populators(self, context, path, row, adapter,
- new_populators, existing_populators, eager_populators):
- """Produce a collection of attribute level row processor
- callables."""
-
- delayed_populators = []
- pops = (new_populators, existing_populators, delayed_populators,
- eager_populators)
- for prop in self._props.itervalues():
- for i, pop in enumerate(prop.create_row_processor(
- context, path,
- self, row, adapter)):
- if pop is not None:
- pops[i].append((prop.key, pop))
-
- if delayed_populators:
- new_populators.extend(delayed_populators)
-
- def _configure_subclass_mapper(self, context, path, adapter):
- """Produce a mapper level row processor callable factory for mappers
- inheriting this one."""
-
- def configure_subclass_mapper(discriminator):
- try:
- mapper = self.polymorphic_map[discriminator]
- except KeyError:
- raise AssertionError(
- "No such polymorphic_identity %r is defined" %
- discriminator)
- if mapper is self:
- return None
-
- # replace the tip of the path info with the subclass mapper
- # being used, that way accurate "load_path" info is available
- # for options invoked during deferred loads, e.g.
- # query(Person).options(defer(Engineer.machines, Machine.name)).
- # for AliasedClass paths, disregard this step (new in 0.8).
- return mapper._instance_processor(
- context,
- path.parent[mapper]
- if not path.is_aliased_class
- else path,
- adapter,
- polymorphic_from=self)
- return configure_subclass_mapper
-
log.class_logger(Mapper)
def configure_mappers():