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
'eagerload',
'eagerload_all',
'extension',
+ 'immediateload',
'join',
'joinedload',
'joinedload_all',
``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
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)
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)
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.
)
def create_row_processor(self, selectcontext, path, mapper, row, adapter):
- return (None, None)
+ return None, None, None
log.class_logger(DynaLoader)
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()
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:
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."""
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):
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."""
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)
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)
# 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'):
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)
# 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):
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()
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)
state.get_impl(self.key).\
set_committed_value(state, dict_, scalar)
- return execute, None
+ return execute, None, None
log.class_logger(SubqueryLoader)
"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(
'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).\
return LazyLoader
elif identifier == 'subquery':
return SubqueryLoader
+ elif identifier == 'immediate':
+ return ImmediateLoader
else:
return LazyLoader