def execute(self, source, dest, obj, child, clearkeys): if source is None: if self.issecondary is False: source = obj elif self.issecondary is True: source = child if clearkeys or source is None: value = None clearkeys = True else: value = self.source_mapper.get_attr_by_column( source, self.source_column) if isinstance(dest, dict): dest[self.dest_column.key] = value else: if clearkeys and self.dest_primary_key(): raise exceptions.AssertionError( "Dependency rule tried to blank-out primary key column '%s' on instance '%s'" % (str(self.dest_column), mapperutil.instance_str(dest))) if logging.is_debug_enabled(self.logger): self.logger.debug( "execute() instances: %s(%s)->%s(%s) ('%s')" % (mapperutil.instance_str(source), str(self.source_column), mapperutil.instance_str(dest), str( self.dest_column), value)) self.dest_mapper.set_attr_by_column(dest, self.dest_column, value)
def _update_impl(self, instance, **kwargs): if instance in self and instance not in self.deleted: return if not hasattr(instance, "_instance_key"): raise exceptions.InvalidRequestError("Instance '%s' is not persisted" % mapperutil.instance_str(instance)) elif self.identity_map.get(instance._instance_key, instance) is not instance: raise exceptions.InvalidRequestError( "Could not update instance '%s', identity key %s; a different instance with the same identity key already exists in this session." % (mapperutil.instance_str(instance), instance._instance_key) ) self._attach(instance)
def refresh(self, instance, attribute_names=None): """Refresh the attributes on the given instance. When called, a query will be issued to the database which will refresh all attributes with their current value. Lazy-loaded relational attributes will remain lazily loaded, so that the instance-wide refresh operation will be followed immediately by the lazy load of that attribute. Eagerly-loaded relational attributes will eagerly load within the single refresh operation. The ``attribute_names`` argument is an iterable collection of attribute names indicating a subset of attributes to be refreshed. """ self._validate_persistent(instance) if ( self.query(_object_mapper(instance))._get( instance._instance_key, refresh_instance=instance._state, only_load_props=attribute_names ) is None ): raise exceptions.InvalidRequestError("Could not refresh instance '%s'" % mapperutil.instance_str(instance))
def _repr_task_element(self, te, attribute=None, process=False): if te.obj is None: objid = "(placeholder)" else: if attribute is not None: objid = "%s.%s" % (mapperutil.instance_str(te.obj), attribute) else: objid = mapperutil.instance_str(te.obj) if self.verbose: return "%s (UOWTaskElement(%s, %s))" % (objid, hex( id(te)), (te.listonly and 'listonly' or (te.isdelete and 'delete' or 'save'))) elif process: return "Process %s" % (objid) else: return "%s %s" % ((te.isdelete and "Delete" or "Save"), objid)
def _delete_impl(self, instance, ignore_transient=False): if instance in self and instance in self.deleted: return if not hasattr(instance, "_instance_key"): if ignore_transient: return else: raise exceptions.InvalidRequestError( "Instance '%s' is not persisted" % mapperutil.instance_str(instance) ) if self.identity_map.get(instance._instance_key, instance) is not instance: raise exceptions.InvalidRequestError( "Instance '%s' is with key %s already persisted with a different identity" % (mapperutil.instance_str(instance), instance._instance_key) ) self._attach(instance) self.uow.register_deleted(instance)
def lazyload(): if self._should_log_debug: self.logger.debug( "lazy load attribute %s on instance %s" % (self.key, mapperutil.instance_str(instance))) if not mapper.has_identity(instance): return None session = sessionlib.object_session(instance) if session is None: try: session = mapper.object_mapper(instance).get_session() except exceptions.InvalidRequestError: raise exceptions.InvalidRequestError( "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)) # if we have a simple straight-primary key load, use mapper.get() # to possibly save a DB round trip q = session.query(self.mapper).autoflush(False) if self.use_get: params = {} for col, bind in self.lazybinds.iteritems(): params[bind.key] = self.parent.get_attr_by_column( instance, col) ident = [] nonnulls = False for primary_key in self.select_mapper.primary_key: bind = self.lazyreverse[primary_key] v = params[bind.key] if v is not None: nonnulls = True ident.append(v) if not nonnulls: return None if options: q = q.options(*options) return q.get(ident) elif self.order_by is not False: q = q.order_by(self.order_by) elif self.secondary is not None and self.secondary.default_order_by( ) is not None: q = q.order_by(self.secondary.default_order_by()) if options: q = q.options(*options) q = q.filter(self.lazy_clause(instance)) result = q.all() if self.uselist: return result else: if result: return result[0] else: return None
def _do_check(self, state, value, oldvalue, initiator): if value is not None: hasparent = initiator.hasparent(attributes.instance_state(value)) if hasparent and oldvalue is not value: raise sa_exc.InvalidRequestError("Instance %s is already associated with an instance " "of %s via its %s attribute, and is only allowed a single parent." % (mapperutil.instance_str(value), state.class_, self.prop) ) return value
def _repr_task_element(self, te, attribute=None, process=False): if te.obj is None: objid = "(placeholder)" else: if attribute is not None: objid = "%s.%s" % (mapperutil.instance_str(te.obj), attribute) else: objid = mapperutil.instance_str(te.obj) if self.verbose: return "%s (UOWTaskElement(%s, %s))" % ( objid, hex(id(te)), (te.listonly and "listonly" or (te.isdelete and "delete" or "save")), ) elif process: return "Process %s" % (objid) else: return "%s %s" % ((te.isdelete and "Delete" or "Save"), objid)
def _validate_persistent(self, instance): """Validate that the given instance is persistent within this ``Session``. """ if instance not in self: raise exceptions.InvalidRequestError( "Instance '%s' is not persistent within this Session" % mapperutil.instance_str(instance) )
def _do_check(self, state, value, oldvalue, initiator): if value is not None: hasparent = initiator.hasparent(attributes.instance_state(value)) if hasparent and oldvalue is not value: raise sa_exc.InvalidRequestError( "Instance %s is already associated with an instance " "of %s via its %s attribute, and is only allowed a single parent." % (mapperutil.instance_str(value), state.class_, self.prop)) return value
def _save_impl(self, instance, **kwargs): if hasattr(instance, "_instance_key"): raise exceptions.InvalidRequestError( "Instance '%s' is already persistent" % mapperutil.instance_str(instance) ) else: # TODO: consolidate the steps here attributes.manage(instance) instance._entity_name = kwargs.get("entity_name", None) self._attach(instance) self.uow.register_new(instance)
def _attach(self, instance): old_id = getattr(instance, '_sa_session_id', None) if old_id != self.hash_key: if old_id is not None and old_id in _sessions and instance in _sessions[old_id]: raise exceptions.InvalidRequestError("Object '%s' is already attached " "to session '%s' (this is '%s')" % (mapperutil.instance_str(instance), old_id, id(self))) key = getattr(instance, '_instance_key', None) if key is not None: self.identity_map[key] = instance instance._sa_session_id = self.hash_key
def lazyload(): self.logger.debug("lazy load attribute %s on instance %s" % (self.key, mapperutil.instance_str(instance))) params = {} allparams = True # if the instance wasnt loaded from the database, then it cannot lazy load # child items. one reason for this is that a bi-directional relationship # will not update properly, since bi-directional uses lazy loading functions # in both directions, and this instance will not be present in the lazily-loaded # results of the other objects since its not in the database if not mapper.has_identity(instance): return None #print "setting up loader, lazywhere", str(self.lazywhere), "binds", self.lazybinds for col, bind in self.lazybinds.iteritems(): params[bind.key] = self.parent.get_attr_by_column(instance, col) if params[bind.key] is None: allparams = False break if not allparams: return None session = sessionlib.object_session(instance) if session is None: try: session = mapper.object_mapper(instance).get_session() except exceptions.InvalidRequestError: raise exceptions.InvalidRequestError("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)) # if we have a simple straight-primary key load, use mapper.get() # to possibly save a DB round trip if self.use_get: ident = [] for primary_key in self.select_mapper.pks_by_table[self.select_mapper.mapped_table]: bind = self.lazyreverse[primary_key] ident.append(params[bind.key]) return session.query(self.mapper).get(ident) elif self.order_by is not False: order_by = self.order_by elif self.secondary is not None and self.secondary.default_order_by() is not None: order_by = self.secondary.default_order_by() else: order_by = False result = session.query(self.mapper, with_options=options).select_whereclause(self.lazywhere, order_by=order_by, params=params) if self.uselist: return result else: if len(result): return result[0] else: return None
def register_object(self, obj, isdelete=False, listonly=False, postupdate=False, post_update_cols=None, **kwargs): """Add an object to this ``UOWTransaction`` to be updated in the database. This operation has the combined effect of locating/creating an appropriate ``UOWTask`` object, and calling its ``append()`` method which then locates/creates an appropriate ``UOWTaskElement`` object. """ #print "REGISTER", repr(obj), repr(getattr(obj, '_instance_key', None)), str(isdelete), str(listonly) # if object is not in the overall session, do nothing if not self.uow._is_valid(obj): if self._should_log_debug: self.logger.debug( "object %s not part of session, not registering for flush" % (mapperutil.instance_str(obj))) return if self._should_log_debug: self.logger.debug( "register object for flush: %s isdelete=%s listonly=%s postupdate=%s" % (mapperutil.instance_str(obj), isdelete, listonly, postupdate)) mapper = object_mapper(obj) task = self.get_task_by_mapper(mapper) if postupdate: task.append_postupdate(obj, post_update_cols) return task.append(obj, listonly, isdelete=isdelete, **kwargs)
def _clone(self, sess=None): # note we're returning an entirely new Query class instance here # without any assignment capabilities; # the class of this query is determined by the session. instance = self.instance if sess is None: sess = object_session(instance) if sess is None: try: sess = object_mapper(instance).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" % (mapperutil.instance_str(instance), self.attr.key)) return sess.query(self.attr.target_mapper).with_parent( instance, self.attr.key)
def merge(self, object, entity_name=None, _recursive=None): """Copy the state of the given `object` onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with ``cascade="merge"``. """ if _recursive is None: _recursive = util.Set() if entity_name is not None: mapper = _class_mapper(object.__class__, entity_name=entity_name) else: mapper = _object_mapper(object) if mapper in _recursive or object in _recursive: return None _recursive.add(mapper) _recursive.add(object) try: key = getattr(object, '_instance_key', None) if key is None: merged = attribute_manager.new_instance(mapper.class_) else: if key in self.identity_map: merged = self.identity_map[key] else: merged = self.get(mapper.class_, key[1]) if merged is None: raise exceptions.AssertionError( "Instance %s has an instance key but is not persisted" % mapperutil.instance_str(object)) for prop in mapper.iterate_properties: prop.merge(self, object, merged, _recursive) if key is None: self.save(merged, entity_name=mapper.entity_name) return merged finally: _recursive.remove(mapper)
def execute(self, source, dest, obj, child, clearkeys): if source is None: if self.issecondary is False: source = obj elif self.issecondary is True: source = child if clearkeys or source is None: value = None clearkeys = True else: value = self.source_mapper.get_attr_by_column(source, self.source_column) if isinstance(dest, dict): dest[self.dest_column.key] = value else: if clearkeys and self.dest_primary_key(): raise exceptions.AssertionError("Dependency rule tried to blank-out primary key column '%s' on instance '%s'" % (str(self.dest_column), mapperutil.instance_str(dest))) if logging.is_debug_enabled(self.logger): self.logger.debug("execute() instances: %s(%s)->%s(%s) ('%s')" % (mapperutil.instance_str(source), str(self.source_column), mapperutil.instance_str(dest), str(self.dest_column), value)) self.dest_mapper.set_attr_by_column(dest, self.dest_column, value)
def _clone(self, sess=None): # note we're returning an entirely new Query class instance # here without any assignment capabilities; the class of this # query is determined by the session. instance = self.instance if sess is None: sess = object_session(instance) if sess is None: raise sa_exc.UnboundExecutionError( "Parent instance %s is not bound to a Session, and no " "contextual session is established; lazy load operation " "of attribute '%s' cannot proceed" % ( mapperutil.instance_str(instance), self.attr.key)) if self.query_class: query = self.query_class(self.attr.target_mapper, session=sess) else: query = sess.query(self.attr.target_mapper) query._criterion = self._criterion query._order_by = self._order_by return query
def _clone(self, sess=None): # note we're returning an entirely new Query class instance # here without any assignment capabilities; the class of this # query is determined by the session. instance = self.instance if sess is None: sess = object_session(instance) if sess is None: raise sa_exc.UnboundExecutionError( "Parent instance %s is not bound to a Session, and no " "contextual session is established; lazy load operation " "of attribute '%s' cannot proceed" % ( mapperutil.instance_str(instance), self.attr.key)) if self.query_class: query = self.query_class(self.attr.target_mapper, session=sess) else: query = sess.query(self.attr.target_mapper) query = query.with_parent(instance, self.attr.key) if self.attr.order_by: query = query.order_by(self.attr.order_by) return query
def merge(self, object, entity_name=None, _recursive=None): """Copy the state of the given `object` onto the persistent object with the same identifier. If there is no persistent instance currently associated with the session, it will be loaded. Return the persistent instance. If the given instance is unsaved, save a copy of and return it as a newly persistent instance. The given instance does not become associated with the session. This operation cascades to associated instances if the association is mapped with ``cascade="merge"``. """ if _recursive is None: _recursive = util.Set() if entity_name is not None: mapper = _class_mapper(object.__class__, entity_name=entity_name) else: mapper = _object_mapper(object) if mapper in _recursive or object in _recursive: return None _recursive.add(mapper) _recursive.add(object) try: key = getattr(object, '_instance_key', None) if key is None: merged = mapper._create_instance(self) else: if key in self.identity_map: merged = self.identity_map[key] else: merged = self.get(mapper.class_, key[1]) if merged is None: raise exceptions.AssertionError("Instance %s has an instance key but is not persisted" % mapperutil.instance_str(object)) for prop in mapper.props.values(): prop.merge(self, object, merged, _recursive) if key is None: self.save(merged, entity_name=mapper.entity_name) return merged finally: _recursive.remove(mapper)
def get(self, obj, passive=False, raiseerr=True): """Retrieve a value from the given object. If a callable is assembled on this object's attribute, and passive is False, the callable will be executed and the resulting value will be set as the new value for this attribute. """ try: return obj.__dict__[self.key] except KeyError: state = obj._state # if an instance-wide "trigger" was set, call that # and start again if state.has_key('trigger'): trig = state['trigger'] del state['trigger'] trig() return self.get(obj, passive=passive, raiseerr=raiseerr) if self.uselist: callable_ = self._get_callable(obj) if callable_ is not None: if passive: return InstrumentedAttribute.PASSIVE_NORESULT self.logger.debug("Executing lazy callable on %s.%s" % (orm_util.instance_str(obj), self.key)) values = callable_() l = InstrumentedList(self, obj, self._adapt_list(values), init=False) # if a callable was executed, then its part of the "committed state" # if any, so commit the newly loaded data orig = state.get('original', None) if orig is not None: orig.commit_attribute(self, obj, l) else: # note that we arent raising AttributeErrors, just creating a new # blank list and setting it. # this might be a good thing to be changeable by options. l = InstrumentedList(self, obj, self._blank_list(), init=False) obj.__dict__[self.key] = l return l else: callable_ = self._get_callable(obj) if callable_ is not None: if passive: return InstrumentedAttribute.PASSIVE_NORESULT self.logger.debug("Executing lazy callable on %s.%s" % (orm_util.instance_str(obj), self.key)) value = callable_() obj.__dict__[self.key] = value # if a callable was executed, then its part of the "committed state" # if any, so commit the newly loaded data orig = state.get('original', None) if orig is not None: orig.commit_attribute(self, obj) return value else: # note that we arent raising AttributeErrors, just returning None. # this might be a good thing to be changeable by options. return None
def register_object(self, obj, isdelete = False, listonly = False, postupdate=False, post_update_cols=None, **kwargs): """Add an object to this ``UOWTransaction`` to be updated in the database. This operation has the combined effect of locating/creating an appropriate ``UOWTask`` object, and calling its ``append()`` method which then locates/creates an appropriate ``UOWTaskElement`` object. """ #print "REGISTER", repr(obj), repr(getattr(obj, '_instance_key', None)), str(isdelete), str(listonly) # if object is not in the overall session, do nothing if not self.uow._is_valid(obj): if logging.is_debug_enabled(self.logger): self.logger.debug("object %s not part of session, not registering for flush" % (mapperutil.instance_str(obj))) return if logging.is_debug_enabled(self.logger): self.logger.debug("register object for flush: %s isdelete=%s listonly=%s postupdate=%s" % (mapperutil.instance_str(obj), isdelete, listonly, postupdate)) mapper = object_mapper(obj) task = self.get_task_by_mapper(mapper) if postupdate: task.append_postupdate(obj, post_update_cols) return task.append(obj, listonly, isdelete=isdelete, **kwargs)
def lazyload(): self.logger.debug("lazy load attribute %s on instance %s" % (self.key, mapperutil.instance_str(instance))) params = {} allparams = True # if the instance wasnt loaded from the database, then it cannot lazy load # child items. one reason for this is that a bi-directional relationship # will not update properly, since bi-directional uses lazy loading functions # in both directions, and this instance will not be present in the lazily-loaded # results of the other objects since its not in the database if not mapper.has_identity(instance): return None #print "setting up loader, lazywhere", str(self.lazywhere), "binds", self.lazybinds for col, bind in self.lazybinds.iteritems(): params[bind.key] = self.parent.get_attr_by_column( instance, col) if params[bind.key] is None: allparams = False break if not allparams: return None session = sessionlib.object_session(instance) if session is None: try: session = mapper.object_mapper(instance).get_session() except exceptions.InvalidRequestError: raise exceptions.InvalidRequestError( "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)) # if we have a simple straight-primary key load, use mapper.get() # to possibly save a DB round trip if self.use_get: ident = [] for primary_key in self.select_mapper.pks_by_table[ self.select_mapper.mapped_table]: bind = self.lazyreverse[primary_key] ident.append(params[bind.key]) return session.query(self.mapper).get(ident) elif self.order_by is not False: order_by = self.order_by elif self.secondary is not None and self.secondary.default_order_by( ) is not None: order_by = self.secondary.default_order_by() else: order_by = False result = session.query(self.mapper, with_options=options).select_whereclause( self.lazywhere, order_by=order_by, params=params) if self.uselist: return result else: if len(result): return result[0] else: return None
def _clone(self, sess=None): # note we're returning an entirely new Query class instance here # without any assignment capabilities; # the class of this query is determined by the session. instance = self.instance if sess is None: sess = object_session(instance) if sess is None: try: sess = object_mapper(instance).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" % (mapperutil.instance_str(instance), self.attr.key)) q = sess.query(self.attr.target_mapper).with_parent(instance, self.attr.key) if self.attr.order_by: q = q.order_by(self.attr.order_by) return q
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.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: if result: return result[0] else: return None
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.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: if result: return result[0] else: return None