No title Revision 303866353263 (Mon Mar 01 2010 at 19:07) - Diff Link to this snippet: https://friendpaste.com/3XtkfLWeUNaXHv1JE7qWva Embed: manni perldoc borland colorful default murphy trac fruity autumn bw emacs pastie friendly Show line numbers Wrap lines 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374 def update_fields(self, record_id, fields, cached_record=None): """Safely update a number of fields. 'fields' being a dictionary with path_tuple: new_value for only the fields we want to change the value of, where path_tuple is a tuple of fieldnames indicating the path to the possibly nested field we're interested in. old_record a the copy of the record we most recently read from the database. In the case the underlying document was changed, we try to merge, but only if none of the old values have changed. (i.e., do not overwrite changes originating elsewhere.) This is slightly hairy, so that other code won't have to be. """ # Initially, the record in memory and in the db are the same # as far as we know. (If they're not, we'll get a # ResourceConflict later on, from which we can recover.) if cached_record is None: cached_record = self.db[record_id] if isinstance(cached_record, Record): cached_record = cached_record._data record = copy.deepcopy(cached_record) # Loop until either failure or success has been determined while True: modified = False conflicts = {} # loop through all the fields that need to be modified for path, new_value in fields.items(): if not isinstance(path, tuple): path = (path,) # Walk down in both copies of the record to the leaf # node that represents the field, creating the path in # the in memory record if necessary. db_parent = record cached_parent = cached_record for field in path[:-1]: db_parent = db_parent.setdefault(field, {}) cached_parent = cached_parent.get(field, {}) # Get the values of the fields in the two copies. db_value = db_parent.get(path[-1]) cached_value = cached_parent.get(path[-1]) # If the value we intend to store is already in the # database, we need do nothing, which is our favorite. if db_value == new_value: continue # If the value in the db is different than the value # our copy holds, we have a conflict. We could bail # here, but we can give better feedback if we gather # all the conflicts, so we continue the for loop if db_value != cached_value: conflicts[path] = (db_value, new_value) continue # Otherwise, it is safe to update the field with the # new value. modified = True db_parent[path[-1]] = new_value # If we had conflicts, we can bail. if conflicts: raise FieldsConflict(conflicts) # If we made changes to the document, we'll need to save # it. if modified: try: self.db[record_id] = record except ResourceConflict: # We got a conflict, meaning the record has # changed in the database since we last loaded it # into memory. Let's get a fresh copy and try # again. record = self.db[record_id] continue # If we get here, nothing remains to be done, and we can # take a well deserved break. break