📄 strategies.py
字号:
self._register_attribute(self.parent.class_, dynamic=True, target_mapper=self.parent_property.mapper) def create_row_processor(self, selectcontext, mapper, row): return (None, None, None)DynaLoader.logger = logging.class_logger(DynaLoader) class NoLoader(AbstractRelationLoader): def init_class_attribute(self): self.is_class_level = True self._register_attribute(self.parent.class_) def create_row_processor(self, selectcontext, mapper, row): def new_execute(instance, row, ispostselect, **flags): if not ispostselect: if self._should_log_debug: self.logger.debug("initializing blank scalar/collection on %s" % mapperutil.attribute_str(instance, self.key)) self._init_instance_attribute(instance) return (new_execute, None, None)NoLoader.logger = logging.class_logger(NoLoader) class LazyLoader(AbstractRelationLoader): def init(self): super(LazyLoader, self).init() (self.lazywhere, self.lazybinds, self.equated_columns) = self._create_lazy_clause(self.parent_property) self.logger.info(str(self.parent_property) + " lazy loading clause " + str(self.lazywhere)) # determine if our "lazywhere" clause is the same as the mapper's # get() clause. then we can just use mapper.get() #from sqlalchemy.orm import query self.use_get = not self.uselist and self.mapper._get_clause[0].compare(self.lazywhere) if self.use_get: self.logger.info(str(self.parent_property) + " will use query.get() to optimize instance loads") def init_class_attribute(self): self.is_class_level = True self._register_attribute(self.parent.class_, callable_=self.class_level_loader) def lazy_clause(self, instance, reverse_direction=False): if instance is None: return self._lazy_none_clause(reverse_direction) if not reverse_direction: (criterion, lazybinds, rev) = (self.lazywhere, self.lazybinds, self.equated_columns) else: (criterion, lazybinds, rev) = LazyLoader._create_lazy_clause(self.parent_property, reverse_direction=reverse_direction) bind_to_col = dict([(lazybinds[col].key, col) for col in lazybinds]) def visit_bindparam(bindparam): mapper = reverse_direction and self.parent_property.mapper or self.parent_property.parent if bindparam.key in bind_to_col: # use the "committed" (database) version to get query column values bindparam.value = mapper._get_committed_attr_by_column(instance, bind_to_col[bindparam.key]) return visitors.traverse(criterion, clone=True, visit_bindparam=visit_bindparam) def _lazy_none_clause(self, reverse_direction=False): if not reverse_direction: (criterion, lazybinds, rev) = (self.lazywhere, self.lazybinds, self.equated_columns) else: (criterion, lazybinds, rev) = LazyLoader._create_lazy_clause(self.parent_property, reverse_direction=reverse_direction) bind_to_col = dict([(lazybinds[col].key, col) for col in lazybinds]) def visit_binary(binary): mapper = reverse_direction and self.parent_property.mapper or self.parent_property.parent if isinstance(binary.left, expression._BindParamClause) and binary.left.key in bind_to_col: # reverse order if the NULL is on the left side binary.left = binary.right binary.right = expression.null() binary.operator = operators.is_ elif isinstance(binary.right, expression._BindParamClause) and binary.right.key in bind_to_col: binary.right = expression.null() binary.operator = operators.is_ return visitors.traverse(criterion, clone=True, visit_binary=visit_binary) def class_level_loader(self, instance, options=None, path=None): if not mapper.has_mapper(instance): return None localparent = mapper.object_mapper(instance) # adjust for the PropertyLoader associated with the instance # not being our own PropertyLoader. This can occur when entity_name # mappers are used to map different versions of the same PropertyLoader # to the class. prop = localparent.get_property(self.key) if prop is not self.parent_property: return prop._get_strategy(LazyLoader).setup_loader(instance) return LoadLazyAttribute(instance, self.key, options, path) def setup_loader(self, instance, options=None, path=None): return LoadLazyAttribute(instance, self.key, options, path) def create_row_processor(self, selectcontext, mapper, row): if not self.is_class_level or len(selectcontext.options): def new_execute(instance, row, ispostselect, **flags): if not ispostselect: if self._should_log_debug: self.logger.debug("set instance-level lazy loader on %s" % mapperutil.attribute_str(instance, self.key)) # we are not the primary manager for this attribute on this class - set up a per-instance lazyloader, # which will override the class-level behavior self._init_instance_attribute(instance, callable_=self.setup_loader(instance, selectcontext.options, selectcontext.query._current_path + selectcontext.path)) return (new_execute, None, None) else: def new_execute(instance, row, ispostselect, **flags): if not ispostselect: if self._should_log_debug: self.logger.debug("set class-level lazy loader on %s" % mapperutil.attribute_str(instance, self.key)) # we are the primary manager for this attribute on this class - reset its per-instance attribute state, # so that the class-level lazy loader is executed when next referenced on this instance. # this usually is not needed unless the constructor of the object referenced the attribute before we got # to load data into it. instance._state.reset(self.key) return (new_execute, None, None) def _create_lazy_clause(cls, prop, reverse_direction=False): (primaryjoin, secondaryjoin, remote_side) = (prop.polymorphic_primaryjoin, prop.polymorphic_secondaryjoin, prop.remote_side) binds = {} equated_columns = {} def should_bind(targetcol, othercol): if not prop._col_is_part_of_mappings(targetcol): return False if reverse_direction and not secondaryjoin: return targetcol in remote_side else: return othercol in remote_side def visit_binary(binary): if not isinstance(binary.left, sql.ColumnElement) or not isinstance(binary.right, sql.ColumnElement): return leftcol = binary.left rightcol = binary.right equated_columns[rightcol] = leftcol equated_columns[leftcol] = rightcol if should_bind(leftcol, rightcol): if leftcol in binds: binary.left = binds[leftcol] else: binary.left = binds[leftcol] = sql.bindparam(None, None, type_=binary.right.type) # the "left is not right" compare is to handle part of a join clause that is "table.c.col1==table.c.col1", # which can happen in rare cases (test/orm/relationships.py RelationTest2) if leftcol is not rightcol and should_bind(rightcol, leftcol): if rightcol in binds: binary.right = binds[rightcol] else: binary.right = binds[rightcol] = sql.bindparam(None, None, type_=binary.left.type) lazywhere = primaryjoin if not secondaryjoin or not reverse_direction: lazywhere = visitors.traverse(lazywhere, clone=True, visit_binary=visit_binary) if secondaryjoin is not None: if reverse_direction: secondaryjoin = visitors.traverse(secondaryjoin, clone=True, visit_binary=visit_binary) lazywhere = sql.and_(lazywhere, secondaryjoin) return (lazywhere, binds, equated_columns) _create_lazy_clause = classmethod(_create_lazy_clause) LazyLoader.logger = logging.class_logger(LazyLoader)class LoadLazyAttribute(object): """callable, serializable loader object used by LazyLoader""" def __init__(self, instance, key, options, path): self.instance = instance self.key = key self.options = options self.path = path def __getstate__(self): return {'instance':self.instance, 'key':self.key, 'options':self.options, 'path':serialize_path(self.path)} def __setstate__(self, state): self.instance = state['instance'] self.key = state['key'] self.options= state['options'] self.path = deserialize_path(state['path']) def __call__(self): instance = self.instance if not mapper.has_identity(instance): return None instance_mapper = mapper.object_mapper(instance) prop = instance_mapper.get_property(self.key) strategy = prop._get_strategy(LazyLoader) if strategy._should_log_debug: strategy.logger.debug("lazy load attribute %s on instance %s" % (self.key, mapperutil.instance_str(instance))) session = sessionlib.object_session(instance) if session is None: try: session = instance_mapper.get_session() except exceptions.InvalidRequestError: raise exceptions.UnboundExecutionError("Parent instance %s is not bound to a Session, and no contextual session is established; lazy load operation of attribute '%s' cannot proceed" % (instance.__class__, self.key)) q = session.query(prop.mapper).autoflush(False) if self.path: q = q._with_current_path(self.path) # if we have a simple primary key load, use mapper.get() # to possibly save a DB round trip if strategy.use_get: ident = [] allnulls = True for primary_key in prop.select_mapper.primary_key: val = instance_mapper._get_committed_attr_by_column(instance, strategy.equated_columns[primary_key]) allnulls = allnulls and val is None ident.append(val) if allnulls: return None if self.options: q = q._conditional_options(*self.options) return q.get(ident) if strategy.order_by is not False: q = q.order_by(strategy.order_by) elif strategy.secondary is not None and strategy.secondary.default_order_by() is not None: q = q.order_by(strategy.secondary.default_order_by()) if self.options: q = q._conditional_options(*self.options) q = q.filter(strategy.lazy_clause(instance)) result = q.all() if strategy.uselist: return result else:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -