def flush(self, session, objects=None): # this context will track all the objects we want to save/update/delete, # and organize a hierarchical dependency structure. it also handles # communication with the mappers and relationships to fire off SQL # and synchronize attributes between related objects. echo = logging.is_info_enabled(self.logger) flush_context = UOWTransaction(self, session) # create the set of all objects we want to operate upon if objects is not None: # specific list passed in objset = util.Set(objects) else: # or just everything objset = util.Set(self.identity_map.values()).union(self.new) # detect persistent objects that have changes dirty = self.locate_dirty() # store objects whose fate has been decided processed = util.Set() # put all saves/updates into the flush context. detect orphans and throw them into deleted. for obj in self.new.union(dirty).intersection(objset).difference( self.deleted): if obj in processed: continue if object_mapper(obj)._is_orphan(obj): for c in [obj] + list( object_mapper(obj).cascade_iterator('delete', obj)): if c in processed: continue flush_context.register_object(c, isdelete=True) processed.add(c) else: flush_context.register_object(obj) processed.add(obj) # put all remaining deletes into the flush context. for obj in self.deleted: if (objset is not None and not obj in objset) or obj in processed: continue flush_context.register_object(obj, isdelete=True) trans = session.create_transaction(autoflush=False) flush_context.transaction = trans try: flush_context.execute() except: trans.rollback() raise trans.commit() flush_context.post_exec()
def register_object(self, obj, isdelete=False, listonly=False, postupdate=False, post_update_cols=None, **kwargs): """adds an object to this UOWTransaction to be updated in the database. 'isdelete' indicates whether the object is to be deleted or saved (update/inserted). 'listonly', indicates that only this object's dependency relationships should be refreshed/updated to reflect a recent save/upcoming delete operation, but not a full save/delete operation on the object itself, unless an additional save/delete registration is entered for the 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): return mapper = object_mapper(obj) self.mappers.add(mapper) task = self.get_task_by_mapper(mapper) if postupdate: task.append_postupdate(obj, post_update_cols) return # for a cyclical task, things need to be sorted out already, # so this object should have already been added to the appropriate sub-task # can put an assertion here to make sure.... if task.circular: return task.append(obj, listonly, isdelete=isdelete, **kwargs)
def set(self, event, obj, newvalue, oldvalue): # process "save_update" cascade rules for when an instance is attached to another instance sess = object_session(obj) if sess is not None: if newvalue is not None and self.cascade is not None and self.cascade.save_update and newvalue not in sess: mapper = object_mapper(obj) prop = mapper.props[self.key] ename = prop.mapper.entity_name sess.save_or_update(newvalue, entity_name=ename)
def append(self, event, obj, item): # process "save_update" cascade rules for when an instance is appended to the list of another instance sess = object_session(obj) if sess is not None: if self.cascade is not None and self.cascade.save_update and item not in sess: mapper = object_mapper(obj) prop = mapper.props[self.key] ename = prop.mapper.entity_name sess.save_or_update(item, entity_name=ename)
def register_clean(self, obj): if obj in self.new: self.new.remove(obj) if not hasattr(obj, '_instance_key'): mapper = object_mapper(obj) obj._instance_key = mapper.instance_key(obj) if hasattr(obj, '_sa_insert_order'): delattr(obj, '_sa_insert_order') self.identity_map[obj._instance_key] = obj attribute_manager.commit(obj)
def lazyload(): if self._should_log_debug: self.logger.debug("deferred load %s group %s" % (mapperutil.attribute_str( instance, self.key), str(self.group))) try: pk = self.parent.pks_by_table[self.columns[0].table] except KeyError: pk = self.columns[0].table.primary_key clause = sql.and_() for primary_key in pk: attr = self.parent.get_attr_by_column(instance, primary_key) if not attr: return None clause.clauses.append(primary_key == attr) session = sessionlib.object_session(instance) if session is None: raise exceptions.InvalidRequestError( "Parent instance %s is not bound to a Session; deferred load operation of attribute '%s' cannot proceed" % (instance.__class__, self.key)) localparent = mapper.object_mapper(instance) if self.group is not None: groupcols = [ p for p in localparent.props.values() if isinstance(p.strategy, DeferredColumnLoader) and p.group == self.group ] result = session.execute( localparent, sql.select([g.columns[0] for g in groupcols], clause, use_labels=True), None) try: row = result.fetchone() for prop in groupcols: if prop is self: continue # set a scalar object instance directly on the object, # bypassing SmartProperty event handlers. sessionlib.attribute_manager.init_instance_attribute( instance, prop.key, uselist=False) instance.__dict__[prop.key] = row[prop.columns[0]] return row[self.columns[0]] finally: result.close() else: return session.scalar( localparent, sql.select([self.columns[0]], clause, use_labels=True), None)
def is_deleted(self, obj): mapper = object_mapper(obj) task = self.get_task_by_mapper(mapper) return task.is_deleted(obj)
def unregister_object(self, obj): #print "UNREGISTER", obj mapper = object_mapper(obj) task = self.get_task_by_mapper(mapper) if obj in task.objects: task.delete(obj)
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.mapper.pks_by_table[ self.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