class ColumnLoader(LoaderStrategy):
+ """Default column loader."""
+
def init(self):
super(ColumnLoader, self).init()
self.columns = self.parent_property.columns
# our mapped column is not present in the row. check if we need to initialize a polymorphic
# row fetcher used by inheritance.
(hosted_mapper, needs_tables) = selectcontext.attributes.get(('polymorphic_fetch', mapper), (None, None))
+
if hosted_mapper is None:
return (None, None, None)
if hosted_mapper.polymorphic_fetch == 'deferred':
# 'deferred' polymorphic row fetcher, put a callable on the property.
+ # create a deferred column loader which will query the remaining not-yet-loaded tables in an inheritance load.
+ # the mapper for the object creates the WHERE criterion using the mapper who originally
+ # "hosted" the query and the list of tables which are unloaded between the "hosted" mapper
+ # and this mapper. (i.e. A->B->C, the query used mapper A. therefore will need B's and C's tables
+ # in the query).
+
+ # deferred loader strategy
+ strategy = self.parent_property._get_strategy(DeferredColumnLoader)
+
+ # full list of ColumnProperty objects to be loaded in the deferred fetch
+ props = [p for p in mapper.iterate_properties if isinstance(p.strategy, ColumnLoader) and p.columns[0].table in needs_tables]
+
+ # TODO: we are somewhat duplicating efforts from mapper._get_poly_select_loader
+ # and should look for ways to simplify.
+ cond, param_names = mapper._deferred_inheritance_condition(hosted_mapper, needs_tables)
+ statement = sql.select(needs_tables, cond, use_labels=True)
+ def create_statement(instance):
+ params = {}
+ for c in param_names:
+ params[c.name] = mapper.get_attr_by_column(instance, c)
+ return (statement, params)
+
def new_execute(instance, row, isnew, **flags):
if isnew:
- sessionlib.attribute_manager.init_instance_attribute(instance, self.key, callable_=self._get_deferred_inheritance_loader(instance, mapper, hosted_mapper, needs_tables))
+ loader = strategy.setup_loader(instance, props=props, create_statement=create_statement)
+ sessionlib.attribute_manager.init_instance_attribute(instance, self.key, callable_=loader)
+
if self._should_log_debug:
self.logger.debug("Returning deferred column fetcher for %s %s" % (mapper, self.key))
+
return (new_execute, None, None)
else:
# immediate polymorphic row fetcher. no processing needed for this row.
self.logger.debug("Returning no column fetcher for %s %s" % (mapper, self.key))
return (None, None, None)
- def _get_deferred_inheritance_loader(self, instance, mapper, hosted_mapper, needs_tables):
- # create a deferred column loader which will query the remaining not-yet-loaded tables in an inheritance load.
- # the mapper for the object creates the WHERE criterion using the mapper who originally
- # "hosted" the query and the list of tables which are unloaded between the "hosted" mapper
- # and this mapper. (i.e. A->B->C, the query used mapper A. therefore will need B's and C's tables
- # in the query).
- def create_statement():
- # TODO: the SELECT statement here should be cached in the selectcontext. we are somewhat duplicating
- # efforts from mapper._get_poly_select_loader as well and should look
- # for ways to simplify.
- cond, param_names = mapper._deferred_inheritance_condition(hosted_mapper, needs_tables)
- statement = sql.select(needs_tables, cond, use_labels=True)
- params = {}
- for c in param_names:
- params[c.name] = mapper.get_attr_by_column(instance, c)
- return (statement, params)
-
- # install the create_statement() callable using the deferred loading strategy
- strategy = self.parent_property._get_strategy(DeferredColumnLoader)
-
- # assemble list of all ColumnProperties which will need to be loaded
- props = [p for p in mapper.iterate_properties if isinstance(p.strategy, ColumnLoader) and p.columns[0].table in needs_tables]
-
- # set the deferred loader on the instance attribute
- return strategy.setup_loader(instance, props=props, create_statement=create_statement)
-
ColumnLoader.logger = logging.class_logger(ColumnLoader)
class DeferredColumnLoader(LoaderStrategy):
- """Describes an object attribute that corresponds to a table
- column, which also will *lazy load* its value from the table.
-
- This is per-column lazy loading.
- """
+ """Deferred column loader, a per-column or per-column-group lazy loader."""
def create_row_processor(self, selectcontext, mapper, row):
if self.group is not None and selectcontext.attributes.get(('undefer', self.group), False):
params[param_map[primary_key].key] = ident[i]
statement = sql.select([p.columns[0] for p in group], clause, from_obj=[localparent.mapped_table], use_labels=True)
else:
- statement, params = create_statement()
+ statement, params = create_statement(instance)
# TODO: have the "fetch of one row" operation go through the same channels as a query._get()
# deferred load of several attributes should be a specialized case of a query refresh operation