"""
def __get_context_strategy(self, context, path):
- cls = context.attributes.get(("loaderstrategy", path), None)
+ cls = context.attributes.get(("loaderstrategy", _reduce_path(path)), None)
if cls:
try:
return self.__all_strategies[cls]
if _is_aliased_class(mapper):
searchfor = mapper
+ isa = False
else:
- searchfor = _class_to_mapper(mapper).base_mapper
-
+ searchfor = _class_to_mapper(mapper)
+ isa = True
+
for ent in query._mapper_entities:
- if ent.path_entity is searchfor:
+ if searchfor is ent.path_entity or (isa and searchfor.common_parent(ent.path_entity)):
return ent
else:
if raiseerr:
path_element = mapper = getattr(prop, 'mapper', None)
if path_element:
- path_element = path_element.base_mapper
+ path_element = path_element
# if current_path tokens remain, then
return False
def process_query_property(self, query, paths, mappers):
+ # __get_context_strategy may receive the path in terms of
+ # a base mapper - e.g. options(eagerload_all(Company.employees, Engineer.machines))
+ # in the polymorphic tests leads to "(Person, 'machines')" in
+ # the path due to the mechanics of how the eager strategy builds
+ # up the path
if self.is_chained():
for path in paths:
- query._attributes[("loaderstrategy", path)] = self.get_strategy_class()
+ query._attributes[("loaderstrategy", _reduce_path(path))] = \
+ self.get_strategy_class()
else:
- query._attributes[("loaderstrategy", paths[-1])] = self.get_strategy_class()
+ query._attributes[("loaderstrategy", _reduce_path(paths[-1]))] = \
+ self.get_strategy_class()
def get_strategy_class(self):
raise NotImplementedError()
+def _reduce_path(path):
+ """Convert a (mapper, path) path to use base mappers.
+
+ This is used to allow more open ended selection of loader strategies, i.e.
+ Mapper -> prop1 -> Subclass -> prop2, where Subclass is a sub-mapper
+ of the mapper referened by Mapper.prop1.
+
+ """
+ return tuple([i % 2 != 0 and
+ path[i] or
+ getattr(path[i], 'base_mapper', path[i])
+ for i in xrange(len(path))])
class LoaderStrategy(object):
"""Describe the loading behavior of a StrategizedProperty object.
if is_aliased_class:
self.path_entity = self.entity = self.entity_zero = entity
else:
- self.path_entity = mapper.base_mapper
+ self.path_entity = mapper
self.entity = self.entity_zero = mapper
def set_with_polymorphic(self, query, cls_or_mappers, selectable, discriminator):
if _is_aliased_class(entity):
return entity is self.path_entity
else:
- return entity.base_mapper is self.path_entity
+ return entity.isa(self.path_entity)
def adapt_to_selectable(self, query, sel):
query._entities.append(self)
return entity is self.entity_zero
else:
return not _is_aliased_class(self.entity_zero) and \
- entity.base_mapper.common_parent(self.entity_zero)
+ entity.common_parent(self.entity_zero)
def _resolve_expr_against_query_aliases(self, query, expr, context):
return query._adapt_clause(expr, False, True)
return
path = path + (self.key,)
-
+
+ reduced_path = interfaces._reduce_path(path)
+
# check for user-defined eager alias
- if ("user_defined_eager_row_processor", path) in context.attributes:
- clauses = context.attributes[("user_defined_eager_row_processor", path)]
+ if ("user_defined_eager_row_processor", reduced_path) in context.attributes:
+ clauses = context.attributes[("user_defined_eager_row_processor", reduced_path)]
adapter = entity._get_entity_clauses(context.query, context)
if adapter and clauses:
- context.attributes[("user_defined_eager_row_processor", path)] = clauses = clauses.wrap(adapter)
+ context.attributes[("user_defined_eager_row_processor", reduced_path)] = \
+ clauses = clauses.wrap(adapter)
elif adapter:
- context.attributes[("user_defined_eager_row_processor", path)] = clauses = adapter
+ context.attributes[("user_defined_eager_row_processor", reduced_path)] = \
+ clauses = adapter
add_to_collection = context.primary_columns
# check for join_depth or basic recursion,
# if the current path was not explicitly stated as
# a desired "loaderstrategy" (i.e. via query.options())
- if ("loaderstrategy", path) not in context.attributes:
+ if ("loaderstrategy", reduced_path) not in context.attributes:
if self.join_depth:
if len(path) / 2 > self.join_depth:
return
else:
- if self.mapper.base_mapper in path:
+ if self.mapper.base_mapper in reduced_path:
return
clauses = mapperutil.ORMAdapter(mapperutil.AliasedClass(self.mapper),
)
add_to_collection = context.secondary_columns
- context.attributes[("eager_row_processor", path)] = clauses
+ context.attributes[("eager_row_processor", reduced_path)] = clauses
for value in self.mapper._iterate_polymorphic_properties():
value.setup(
context,
entity,
- path + (self.mapper.base_mapper,),
+ path + (self.mapper,),
clauses,
parentmapper=self.mapper,
column_collection=add_to_collection)
def _create_eager_adapter(self, context, row, adapter, path):
- if ("user_defined_eager_row_processor", path) in context.attributes:
- decorator = context.attributes[("user_defined_eager_row_processor", path)]
+ reduced_path = interfaces._reduce_path(path)
+ if ("user_defined_eager_row_processor", reduced_path) in context.attributes:
+ decorator = context.attributes[("user_defined_eager_row_processor", reduced_path)]
# user defined eagerloads are part of the "primary" portion of the load.
# the adapters applied to the Query should be honored.
if context.adapter and decorator:
decorator = decorator.wrap(context.adapter)
elif context.adapter:
decorator = context.adapter
- elif ("eager_row_processor", path) in context.attributes:
- decorator = context.attributes[("eager_row_processor", path)]
+ elif ("eager_row_processor", reduced_path) in context.attributes:
+ decorator = context.attributes[("eager_row_processor", reduced_path)]
else:
if self._should_log_debug:
self.logger.debug("Could not locate aliased clauses for key: " + str(path))
if eager_adapter is not False:
key = self.key
- _instance = self.mapper._instance_processor(context, path + (self.mapper.base_mapper,), eager_adapter)
+ _instance = self.mapper._instance_processor(context, path + (self.mapper,), eager_adapter)
if not self.uselist:
def execute(state, dict_, row, isnew, **flags):
(root_mapper, propname) = paths[-1][-2:]
prop = mapper.get_property(propname, resolve_synonyms=True)
self.alias = prop.target.alias(self.alias)
- query._attributes[("user_defined_eager_row_processor", paths[-1])] = sql_util.ColumnAdapter(self.alias)
+ query._attributes[
+ ("user_defined_eager_row_processor",
+ interfaces._reduce_path(paths[-1]))
+ ] = sql_util.ColumnAdapter(self.alias)
else:
(root_mapper, propname) = paths[-1][-2:]
mapper = mappers[-1]
prop = mapper.get_property(propname, resolve_synonyms=True)
adapter = query._polymorphic_adapters.get(prop.mapper, None)
- query._attributes[("user_defined_eager_row_processor", paths[-1])] = adapter
+ query._attributes[
+ ("user_defined_eager_row_processor",
+ interfaces._reduce_path(paths[-1]))] = adapter
class _SingleParentValidator(interfaces.AttributeExtension):
def __init__(self, prop):