28

I'm working with mongoengine in Django,
this is my document defination:

class Location(mongoengine.Document):  
    user_id = mongoengine.IntField(required=True)  
    point = mongoengine.GeoPointField(required=True)

I want to do this:
given a user_id and a point:
if there is no document that have this user_id, create one with the user_id and point and save it;
else update the document with user_id with point.
Can I do this in one statement with mongoengine?

wong2
  • 34,358
  • 48
  • 134
  • 179
  • 1
    possible duplicate of [mongodb: insert if not exists](http://stackoverflow.com/questions/2801008/mongodb-insert-if-not-exists) - The answer is: use `upsert`. – mac Dec 09 '11 at 15:17
  • @mac I think there maybe something even simpler. – wong2 Dec 09 '11 at 15:17

5 Answers5

47

Note that get_or_create is now scheduled to be deprecated, because with no transaction support in MongoDB it cannot ensure atomicity.

The preferred way is update with upsert:

Location.objects(user_id=user_id).update_one(set__point=point, upsert=True)

More on upserts on the MongoDB documentation.

Nicolas Cortot
  • 6,591
  • 34
  • 44
  • How can I do the same thing but with ```user_id==-1``` when the object does not exist? – arthur.sw Aug 12 '14 at 12:57
  • 1
    @arthur.sw with MongoDB, the object ID for a new document is generated client-side, so you just need to create a new ID with `user_id= ObjectId()`. – Nicolas Cortot Aug 12 '14 at 16:55
  • 2
    There is a new way to do it since version 0.9 (explained [here](http://stackoverflow.com/a/25863633/719276)): location = Location.objects(user_id=user_id).modify(upsert=True, new=True, set__point=point) – arthur.sw Jan 20 '15 at 12:57
  • 1
    There is `upsert_one` method too: http://docs.mongoengine.org/apireference.html#mongoengine.queryset.QuerySet.upsert_one – Hieu Jan 27 '17 at 10:15
8

There is a new way to do it since version 0.9 (explained here):

location = Location.objects(user_id=user_id).modify(upsert=True, new=True, set__point=point)

It returns the created or updated object.

Community
  • 1
  • 1
arthur.sw
  • 11,052
  • 9
  • 47
  • 104
5

this is what I came up with:

location = Location.objects.get_or_create(user_id=user_id)[0]  
location.point = point  
location.save()
wong2
  • 34,358
  • 48
  • 134
  • 179
  • 3
    `location, created = Location.objects.get_or_create(user_id=user_id)` Is the preferred way of doing it! But yes that will do what you need! – Ross Dec 09 '11 at 15:53
  • 4
    `get_or_create()` is now [deprecated](https://groups.google.com/d/topic/mongoengine-users/U_1t2eYJqCA/discussion). `upsert` is [recommended](https://groups.google.com/d/topic/mongoengine-users/C-i7tKkjPg8/discussion) by the Mongoengine crew. See @NCao's answer for more info. – Inactivist May 07 '13 at 11:27
1

Or you can add a method to your class object via:

class Location(mongoengine.Document):  
    user_id = mongoengine.IntField(required=True)  
    point = mongoengine.GeoPointField(required=True)

    def register(self):
        # if doesnt exist, create user id and point field
        if not Location.objects(user_id=self.user_id):
            self.user_id = self.user_id
            self.point = self.point
            self.save()
            return True
        # does exist, do nothing
        else:
            return False
danywigglebutt
  • 238
  • 1
  • 17
0

My solution to update of not exist was this:

    def upsert(self, data: dict, query: dict):
        try:
            sets = {}
            for key in data.keys():
                sets[f"set__{key}"] = data[key]
            response = self.model.objects(**query).update_one(upsert=True, **sets)
            return response
        except Exception as ex:
            # Logger(str(ex)).log_error()
            return False

or if you want, you can use the method update.