MongoLite icon indicating copy to clipboard operation
MongoLite copied to clipboard

Please consider using something other than the pymongo save()

Open tonymillion opened this issue 12 years ago • 0 comments

save() in pymongo is "brain damaged". Please consider implementing change tracking and using find_and_modify() to update the document.

Rather than just complain about it, here is some sample code which implements change tracking. Its 'almost' drop in (you'll need to implement the key_in_skeleton function)


class Document(dict):

    __fields = ['_id','username', 'age']

    def key_in_skeleton(self, key):
        #implement this properly pls!
        return key in self.__fields


    def __init__(self, *args, **kwargs):
        self.clear_ops()
        dict.__init__(self, *args, **kwargs)

    def __setitem__(self, key, value):
        if self.key_in_skeleton(key):
            t = ('$set', key, value)
            self.__ops[key] = t

        return super(Document, self).__setitem__(key, value)

    def inc(self, key, value=1):
        t = ('$inc', key, value)
        self.__ops[key] = t

    def dec(self, key, value=1):
        t = ('$inc', key, -abs(value))
        self.__ops[key] = t

    @property
    def operations(self):
        change_set_dict = {}

        for key in self.__ops:
            change = self.__ops[key]
            ctype   = change[0]
            key     = change[1]
            value   = change[2]

            temp = change_set_dict.get(ctype)
            if not temp:
                temp = {}
                change_set_dict[ctype] = temp
            temp[key] = value

        return change_set_dict;


    def clear_ops(self):
        self.__ops = {}
        pass


    def save(self, safe=True):
        if hasattr(self, 'pre_save'):
            self.pre_save()

        new = not self.has_key('_id')
        print 'new=', new

        if new:
            if hasattr(self, 'pre_insert'):
                self.pre_insert()
        else:
            if hasattr(self, 'pre_update'):
                self.pre_update()

        col = self.collection()

        ops = self.operations
        print ops
        if new:
            #if this is an insert, generate an ObjectId and continue!
            res = col.find_and_modify(query={'_id':ObjectId()}, update=ops, upsert=True, new=True)
            self._id = res['_id']
            self.update(res)

            if hasattr(self, 'post_insert'):
                self.post_insert()
        else:
            res = col.find_and_modify(query={'_id':self._id}, update=ops, upsert=True, new=True)

            self.update(res)

            if hasattr(self, 'post_update'):
                self.pre_update()

        self.clear_ops()

        if hasattr(self, 'post_save'):
            self.post_save()

        pass

    pass

tonymillion avatar Feb 21 '13 12:02 tonymillion