📄 strategies.py
字号:
if result: return result[0] else: return None class EagerLoader(AbstractRelationLoader): """Loads related objects inline with a parent query.""" def init(self): super(EagerLoader, self).init() self.clauses = {} self.join_depth = self.parent_property.join_depth def init_class_attribute(self): # class-level eager strategy; add the PropertyLoader # to the parent's list of "eager loaders"; this tells the Query # that eager loaders will be used in a normal query self.parent._eager_loaders.add(self.parent_property) # initialize a lazy loader on the class level attribute self.parent_property._get_strategy(LazyLoader).init_class_attribute() def setup_query(self, context, parentclauses=None, parentmapper=None, **kwargs): """Add a left outer join to the statement thats being constructed.""" path = context.path # 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 self.join_depth: if len(path) / 2 > self.join_depth: return else: if self.mapper.base_mapper in path: return if parentmapper is None: localparent = context.mapper else: localparent = parentmapper if context.eager_joins: towrap = context.eager_joins else: towrap = context.from_clause # create AliasedClauses object to build up the eager query. this is cached after 1st creation. try: clauses = self.clauses[path] except KeyError: clauses = mapperutil.PropertyAliasedClauses(self.parent_property, self.parent_property.polymorphic_primaryjoin, self.parent_property.polymorphic_secondaryjoin, parentclauses) self.clauses[path] = clauses # place the "row_decorator" from the AliasedClauses into the QueryContext, where it will # be picked up in create_row_processor() when results are fetched context.attributes[("eager_row_processor", path)] = clauses.row_decorator if self.secondaryjoin is not None: context.eager_joins = sql.outerjoin(towrap, clauses.secondary, clauses.primaryjoin).outerjoin(clauses.alias, clauses.secondaryjoin) # TODO: check for "deferred" cols on parent/child tables here ? this would only be # useful if the primary/secondaryjoin are against non-PK columns on the tables (and therefore might be deferred) if self.order_by is False and self.secondary.default_order_by() is not None: context.eager_order_by += clauses.secondary.default_order_by() else: context.eager_joins = towrap.outerjoin(clauses.alias, clauses.primaryjoin) # ensure all the cols on the parent side are actually in the # columns clause (i.e. are not deferred), so that aliasing applied by the Query propagates # those columns outward. This has the effect of "undefering" those columns. for col in sql_util.find_columns(clauses.primaryjoin): if localparent.mapped_table.c.contains_column(col): context.primary_columns.append(col) if self.order_by is False and clauses.alias.default_order_by() is not None: context.eager_order_by += clauses.alias.default_order_by() if clauses.order_by: context.eager_order_by += util.to_list(clauses.order_by) for value in self.select_mapper.iterate_properties: context.exec_with_path(self.select_mapper, value.key, value.setup, context, parentclauses=clauses, parentmapper=self.select_mapper) def _create_row_decorator(self, selectcontext, row, path): """Create a *row decorating* function that will apply eager aliasing to the row. Also check that an identity key can be retrieved from the row, else return None. """ #print "creating row decorator for path ", "->".join([str(s) for s in path]) if ("eager_row_processor", path) in selectcontext.attributes: decorator = selectcontext.attributes[("eager_row_processor", path)] if decorator is None: decorator = lambda row: row else: if self._should_log_debug: self.logger.debug("Could not locate aliased clauses for key: " + str(path)) return None try: decorated_row = decorator(row) # check for identity key identity_key = self.select_mapper.identity_key_from_row(decorated_row) # and its good return decorator except KeyError, k: # no identity key - dont return a row processor, will cause a degrade to lazy if self._should_log_debug: self.logger.debug("could not locate identity key from row '%s'; missing column '%s'" % (repr(decorated_row), str(k))) return None def create_row_processor(self, selectcontext, mapper, row): row_decorator = self._create_row_decorator(selectcontext, row, selectcontext.path) if row_decorator is not None: def execute(instance, row, isnew, **flags): decorated_row = row_decorator(row) if not self.uselist: if self._should_log_debug: self.logger.debug("eagerload scalar instance on %s" % mapperutil.attribute_str(instance, self.key)) if isnew: # set a scalar object instance directly on the # parent object, bypassing InstrumentedAttribute # event handlers. # instance.__dict__[self.key] = self.select_mapper._instance(selectcontext, decorated_row, None) else: # call _instance on the row, even though the object has been created, # so that we further descend into properties self.select_mapper._instance(selectcontext, decorated_row, None) else: if isnew or self.key not in instance._state.appenders: # appender_key can be absent from selectcontext.attributes with isnew=False # when self-referential eager loading is used; the same instance may be present # in two distinct sets of result columns if self._should_log_debug: self.logger.debug("initialize UniqueAppender on %s" % mapperutil.attribute_str(instance, self.key)) collection = attributes.init_collection(instance, self.key) appender = util.UniqueAppender(collection, 'append_without_event') instance._state.appenders[self.key] = appender result_list = instance._state.appenders[self.key] if self._should_log_debug: self.logger.debug("eagerload list instance on %s" % mapperutil.attribute_str(instance, self.key)) self.select_mapper._instance(selectcontext, decorated_row, result_list) if self._should_log_debug: self.logger.debug("Returning eager instance loader for %s" % str(self)) return (execute, execute, None) else: if self._should_log_debug: self.logger.debug("eager loader %s degrading to lazy loader" % str(self)) return self.parent_property._get_strategy(LazyLoader).create_row_processor(selectcontext, mapper, row) def __str__(self): return str(self.parent) + "." + self.key EagerLoader.logger = logging.class_logger(EagerLoader)class EagerLazyOption(StrategizedOption): def __init__(self, key, lazy=True, chained=False, mapper=None): super(EagerLazyOption, self).__init__(key, mapper) self.lazy = lazy self.chained = chained def is_chained(self): return not self.lazy and self.chained def process_query_property(self, query, paths): if self.lazy: if paths[-1] in query._eager_loaders: query._eager_loaders = query._eager_loaders.difference(util.Set([paths[-1]])) else: if not self.chained: paths = [paths[-1]] res = util.Set() for path in paths: if len(path) - len(query._current_path) == 2: res.add(path) query._eager_loaders = query._eager_loaders.union(res) super(EagerLazyOption, self).process_query_property(query, paths) def get_strategy_class(self): if self.lazy: return LazyLoader elif self.lazy is False: return EagerLoader elif self.lazy is None: return NoLoaderEagerLazyOption.logger = logging.class_logger(EagerLazyOption)# TODO: enable FetchMode option. currently # this class does nothing. will require Query# to swich between using its "polymorphic" selectable# and its regular selectable in order to make decisions# (therefore might require that FetchModeOperation is performed# only as the first operation on a Query.)class FetchModeOption(PropertyOption): def __init__(self, key, type): super(FetchModeOption, self).__init__(key) if type not in ('join', 'select'): raise exceptions.ArgumentError("Fetchmode must be one of 'join' or 'select'") self.type = type def process_query_property(self, query, properties): query.attributes[('fetchmode', properties[-1])] = self.type class RowDecorateOption(PropertyOption): def __init__(self, key, decorator=None, alias=None): super(RowDecorateOption, self).__init__(key) self.decorator = decorator self.alias = alias def process_query_property(self, query, paths): if self.alias is not None and self.decorator is None: (mapper, propname) = paths[-1][-2:] prop = mapper.get_property(propname, resolve_synonyms=True) if isinstance(self.alias, basestring): self.alias = prop.target.alias(self.alias) self.decorator = mapperutil.create_row_adapter(self.alias, prop.target) query._attributes[("eager_row_processor", paths[-1])] = self.decoratorRowDecorateOption.logger = logging.class_logger(RowDecorateOption)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -