summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy')
-rw-r--r--lib/sqlalchemy/orm/__init__.py27
-rw-r--r--lib/sqlalchemy/orm/dynamic.py2
-rw-r--r--lib/sqlalchemy/orm/interfaces.py34
-rw-r--r--lib/sqlalchemy/orm/mapper.py23
-rw-r--r--lib/sqlalchemy/orm/properties.py2
-rw-r--r--lib/sqlalchemy/orm/strategies.py41
6 files changed, 71 insertions, 58 deletions
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index 8b32d1a27..d4e436b3b 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -84,6 +84,7 @@ __all__ = (
'eagerload',
'eagerload_all',
'extension',
+ 'immediateload',
'join',
'joinedload',
'joinedload_all',
@@ -335,7 +336,12 @@ def relationship(argument, secondary=None, **kwargs):
``select``. Values include:
* ``select`` - items should be loaded lazily when the property is first
- accessed, using a separate SELECT statement.
+ accessed, using a separate SELECT statement, or identity map
+ fetch for simple many-to-one references.
+
+ * ``immediate`` - items should be loaded as the parents are loaded,
+ using a separate SELECT statement, or identity map fetch for
+ simple many-to-one references. (new as of 0.6.5)
* ``joined`` - items should be loaded "eagerly" in the same query as
that of the parent, using a JOIN or LEFT OUTER JOIN. Whether
@@ -1122,7 +1128,7 @@ def subqueryload_all(*keys):
query.options(subqueryload_all(User.orders, Order.items,
Item.keywords))
- See also: :func:`joinedload_all`, :func:`lazyload`
+ See also: :func:`joinedload_all`, :func:`lazyload`, :func:`immediateload`
"""
return strategies.EagerLazyOption(keys, lazy="subquery", chained=True)
@@ -1134,7 +1140,7 @@ def lazyload(*keys):
Used with :meth:`~sqlalchemy.orm.query.Query.options`.
- See also: :func:`eagerload`, :func:`subqueryload`
+ See also: :func:`eagerload`, :func:`subqueryload`, :func:`immediateload`
"""
return strategies.EagerLazyOption(keys, lazy=True)
@@ -1145,11 +1151,24 @@ def noload(*keys):
Used with :meth:`~sqlalchemy.orm.query.Query.options`.
- See also: :func:`lazyload`, :func:`eagerload`, :func:`subqueryload`
+ See also: :func:`lazyload`, :func:`eagerload`, :func:`subqueryload`, :func:`immediateload`
"""
return strategies.EagerLazyOption(keys, lazy=None)
+def immediateload(*keys):
+ """Return a ``MapperOption`` that will convert the property of the given
+ name into an immediate load.
+
+ Used with :meth:`~sqlalchemy.orm.query.Query.options`.
+
+ See also: :func:`lazyload`, :func:`eagerload`, :func:`subqueryload`
+
+ New as of verison 0.6.5.
+
+ """
+ return strategies.EagerLazyOption(keys, lazy='immediate')
+
def contains_alias(alias):
"""Return a ``MapperOption`` that will indicate to the query that
the main table has been aliased.
diff --git a/lib/sqlalchemy/orm/dynamic.py b/lib/sqlalchemy/orm/dynamic.py
index c5ddaca40..dd2c6e896 100644
--- a/lib/sqlalchemy/orm/dynamic.py
+++ b/lib/sqlalchemy/orm/dynamic.py
@@ -36,7 +36,7 @@ class DynaLoader(strategies.AbstractRelationshipLoader):
)
def create_row_processor(self, selectcontext, path, mapper, row, adapter):
- return (None, None)
+ return None, None, None
log.class_logger(DynaLoader)
diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py
index fa27859ec..c3c9c754f 100644
--- a/lib/sqlalchemy/orm/interfaces.py
+++ b/lib/sqlalchemy/orm/interfaces.py
@@ -436,38 +436,8 @@ class MapperProperty(object):
pass
def create_row_processor(self, selectcontext, path, mapper, row, adapter):
- """Return a 2-tuple consiting of two row processing functions and
- an instance post-processing function.
-
- Input arguments are the query.SelectionContext and the *first*
- applicable row of a result set obtained within
- query.Query.instances(), called only the first time a particular
- mapper's populate_instance() method is invoked for the overall result.
-
- The settings contained within the SelectionContext as well as the
- columns present in the row (which will be the same columns present in
- all rows) are used to determine the presence and behavior of the
- returned callables. The callables will then be used to process all
- rows and instances.
-
- Callables are of the following form::
-
- def new_execute(state, dict_, row, isnew):
- # process incoming instance state and given row.
- # the instance is
- # "new" and was just created upon receipt of this row.
- "isnew" indicates if the instance was newly created as a
- result of reading this row
-
- def existing_execute(state, dict_, row):
- # process incoming instance state and given row. the
- # instance is
- # "existing" and was created based on a previous row.
-
- return (new_execute, existing_execute)
-
- Either of the three tuples can be ``None`` in which case no function
- is called.
+ """Return a 3-tuple consisting of three row processing functions.
+
"""
raise NotImplementedError()
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 378570723..a14ad647a 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -2135,10 +2135,11 @@ class Mapper(object):
state.load_path = load_path
if not new_populators:
- new_populators[:], existing_populators[:] = \
- self._populators(context, path, row,
- adapter)
-
+ self._populators(context, path, row, adapter,
+ new_populators,
+ existing_populators
+ )
+
if isnew:
populators = new_populators
else:
@@ -2309,20 +2310,24 @@ class Mapper(object):
return instance
return _instance
- def _populators(self, context, path, row, adapter):
+ def _populators(self, context, path, row, adapter,
+ new_populators, existing_populators):
"""Produce a collection of attribute level row processor callables."""
- new_populators, existing_populators = [], []
+ delayed_populators = []
for prop in self._props.itervalues():
- newpop, existingpop = prop.create_row_processor(
+ newpop, existingpop, delayedpop = prop.create_row_processor(
context, path,
self, row, adapter)
if newpop:
new_populators.append((prop.key, newpop))
if existingpop:
existing_populators.append((prop.key, existingpop))
- return new_populators, existing_populators
-
+ if delayedpop:
+ delayed_populators.append((prop.key, delayedpop))
+ 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."""
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index 4efd2acc9..0cbbf630d 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -255,7 +255,7 @@ class DescriptorProperty(MapperProperty):
pass
def create_row_processor(self, selectcontext, path, mapper, row, adapter):
- return (None, None)
+ return None, None, None
def merge(self, session, source_state, source_dict,
dest_state, dest_dict, load, _recursive):
diff --git a/lib/sqlalchemy/orm/strategies.py b/lib/sqlalchemy/orm/strategies.py
index 3e6b6a21f..60454eabc 100644
--- a/lib/sqlalchemy/orm/strategies.py
+++ b/lib/sqlalchemy/orm/strategies.py
@@ -88,7 +88,7 @@ class UninstrumentedColumnLoader(LoaderStrategy):
column_collection.append(c)
def create_row_processor(self, selectcontext, path, mapper, row, adapter):
- return None, None
+ return None, None, None
class ColumnLoader(LoaderStrategy):
"""Strategize the loading of a plain column-based MapperProperty."""
@@ -127,11 +127,11 @@ class ColumnLoader(LoaderStrategy):
if col is not None and col in row:
def new_execute(state, dict_, row):
dict_[key] = row[col]
- return new_execute, None
+ return new_execute, None, None
else:
def new_execute(state, dict_, row):
state.expire_attribute_pre_commit(dict_, key)
- return new_execute, None
+ return new_execute, None, None
log.class_logger(ColumnLoader)
@@ -184,7 +184,7 @@ class CompositeColumnLoader(ColumnLoader):
def new_execute(state, dict_, row):
dict_[key] = composite_class(*[row[c] for c in columns])
- return new_execute, None
+ return new_execute, None, None
log.class_logger(CompositeColumnLoader)
@@ -211,7 +211,7 @@ class DeferredColumnLoader(LoaderStrategy):
# fire off on next access.
state.reset(dict_, key)
- return new_execute, None
+ return new_execute, None, None
def init(self):
if hasattr(self.parent_property, 'composite_class'):
@@ -348,7 +348,7 @@ class NoLoader(AbstractRelationshipLoader):
def create_row_processor(self, selectcontext, path, mapper, row, adapter):
def new_execute(state, dict_, row):
state.initialize(self.key)
- return new_execute, None
+ return new_execute, None, None
log.class_logger(NoLoader)
@@ -509,7 +509,7 @@ class LazyLoader(AbstractRelationshipLoader):
# any existing state.
state.reset(dict_, key)
- return new_execute, None
+ return new_execute, None, None
@classmethod
def _create_lazy_clause(cls, prop, reverse_direction=False):
@@ -683,6 +683,23 @@ class LoadLazyAttribute(object):
else:
return None
+class ImmediateLoader(AbstractRelationshipLoader):
+ def init_class_attribute(self, mapper):
+ self.parent_property.\
+ _get_strategy(LazyLoader).\
+ init_class_attribute(mapper)
+
+ def setup_query(self, context, entity,
+ path, adapter, column_collection=None,
+ parentmapper=None, **kwargs):
+ pass
+
+ def create_row_processor(self, context, path, mapper, row, adapter):
+ def execute(state, dict_, row):
+ state.get_impl(self.key).get(state, dict_)
+
+ return None, None, execute
+
class SubqueryLoader(AbstractRelationshipLoader):
def init(self):
super(SubqueryLoader, self).init()
@@ -859,7 +876,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
path = interfaces._reduce_path(path)
if ('subquery', path) not in context.attributes:
- return None, None
+ return None, None, None
local_cols, remote_cols = self._local_remote_columns(self.parent_property)
@@ -903,7 +920,7 @@ class SubqueryLoader(AbstractRelationshipLoader):
state.get_impl(self.key).\
set_committed_value(state, dict_, scalar)
- return execute, None
+ return execute, None, None
log.class_logger(SubqueryLoader)
@@ -1156,7 +1173,7 @@ class EagerLoader(AbstractRelationshipLoader):
"Multiple rows returned with "
"uselist=False for eagerly-loaded attribute '%s' "
% self)
- return new_execute, existing_execute
+ return new_execute, existing_execute, None
else:
def new_execute(state, dict_, row):
collection = attributes.init_state_collection(
@@ -1181,7 +1198,7 @@ class EagerLoader(AbstractRelationshipLoader):
'append_without_event')
context.attributes[(state, key)] = result_list
_instance(row, result_list)
- return new_execute, existing_execute
+ return new_execute, existing_execute, None
else:
return self.parent_property.\
_get_strategy(LazyLoader).\
@@ -1221,6 +1238,8 @@ def factory(identifier):
return LazyLoader
elif identifier == 'subquery':
return SubqueryLoader
+ elif identifier == 'immediate':
+ return ImmediateLoader
else:
return LazyLoader