mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-06-03 22:35:46 -04:00
- Added a new "lazyload" option "immediateload".
Issues the usual "lazy" load operation automatically as the object is populated. The use case here is when loading objects to be placed in an offline cache, or otherwise used after the session isn't available, and straight 'select' loading, not 'joined' or 'subquery', is desired. [ticket:1914]
This commit is contained in:
@@ -6,6 +6,15 @@ CHANGES
|
||||
0.6.5
|
||||
=====
|
||||
- orm
|
||||
- Added a new "lazyload" option "immediateload".
|
||||
Issues the usual "lazy" load operation automatically
|
||||
as the object is populated. The use case
|
||||
here is when loading objects to be placed in
|
||||
an offline cache, or otherwise used after
|
||||
the session isn't available, and straight 'select'
|
||||
loading, not 'joined' or 'subquery', is desired.
|
||||
[ticket:1914]
|
||||
|
||||
- Fixed recursion bug which could occur when moving
|
||||
an object from one reference to another, with
|
||||
backrefs involved, where the initiating parent
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user