]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
have paths represented as their actual mapper, not the base mapper, allowing
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 11 Jan 2010 20:26:34 +0000 (20:26 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 11 Jan 2010 20:26:34 +0000 (20:26 +0000)
more information for custom mapper opts to see what's going on.  add a new _reduce_path()
function to apply to the path as stored in dictionaries, adds a slight cost overhead.

examples/beaker_caching/meta.py
lib/sqlalchemy/orm/interfaces.py
lib/sqlalchemy/orm/query.py
lib/sqlalchemy/orm/strategies.py

index 4103baa325990b7148a1e3f88faf36ff78be0a3f..25fa1d4cf138b16e48a59387166f9fe84a084ccd 100644 (file)
@@ -170,7 +170,7 @@ class FromCache(MapperOption):
         """
         if self.cls_ is not None and query._current_path:
             mapper, key = query._current_path[-2:]
-            if issubclass(self.cls_, mapper.class_) and key == self.propname:
+            if self.cls_ is mapper.class_ and key == self.propname:
                 self._set_query_cache(query)
 
     def process_query(self, query):
index fa7670420543ecadd3213f7db39b533fd96ce304..d8ba9ea96d0426f2509b834b182a162b5fbba285 100644 (file)
@@ -642,7 +642,7 @@ class StrategizedProperty(MapperProperty):
     """
 
     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]
@@ -762,11 +762,13 @@ class PropertyOption(MapperOption):
 
         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:
@@ -852,7 +854,7 @@ class PropertyOption(MapperOption):
                     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
@@ -911,15 +913,34 @@ class StrategizedOption(PropertyOption):
         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.
index 5b2fbb5240e47dd3bb3b3d8339939ca73bc7fc4e..c90c163e96726406cb179754a1bf58ea685a3f20 100644 (file)
@@ -1967,7 +1967,7 @@ class _MapperEntity(_QueryEntity):
         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):
@@ -1989,7 +1989,7 @@ class _MapperEntity(_QueryEntity):
         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)
@@ -2151,7 +2151,7 @@ class _ColumnEntity(_QueryEntity):
             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)
index a3cc06b910a2f3e5467b5e39175903f3b48508ae..0970f383623a2d1aea6c4853956cbaab1b5a64f4 100644 (file)
@@ -628,16 +628,20 @@ class EagerLoader(AbstractRelationLoader):
             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
             
@@ -645,12 +649,12 @@ class EagerLoader(AbstractRelationLoader):
             # 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), 
@@ -664,13 +668,13 @@ class EagerLoader(AbstractRelationLoader):
             )
 
             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)
@@ -757,16 +761,17 @@ class EagerLoader(AbstractRelationLoader):
 
         
     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))
@@ -788,7 +793,7 @@ class EagerLoader(AbstractRelationLoader):
         
         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):
@@ -892,13 +897,18 @@ class LoadEagerFromAliasOption(PropertyOption):
                 (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):