2

I've been using the get_or_create method with MongoEngine in a Django app. Today, I noticed there were a few duplicate entries. I came across this in the MongoEngine API Reference for get_or_create:

This requires two separate operations and therefore a race condition exists. Because there are no transactions in mongoDB other approaches should be investigated, to ensure you don’t accidentally duplicate data when using this method. This is now scheduled to be removed before 1.0

Should I be using something like this?:

from models import Post
post = Post(name='hello')
try:
    Posts.objects.get(name=post.name)
    print "exists"
except:
    post.save()
    print "saved"

Will that solve my problem? Is there a better way?

wohlgejm
  • 140
  • 1
  • 9
  • Your suggestion won't solve the problem. Suppose you have two instances ("threads") of your program running. Both run completely in sync. So, both threads do the existence check at the same time, and no object exists yet. They then go ahead and both save → duplicate – sk1p Jan 15 '14 at 01:34

2 Answers2

6

To perform an upsert use the following syntax:

Posts.objects(name="hello").update(set__X=Y, upsert=True)

That will add a post with the name "hello" and where X = Y if it doesn't already exist, otherwise it will update an existing post just setting X = Y.

Ross
  • 17,861
  • 2
  • 55
  • 73
  • 2
    The issue with this replacement is that it doesn't return 1) "did I insert or did I update?" 2) what is the ID of the new/updated object(s)? See http://stackoverflow.com/questions/22176934/how-do-i-tell-if-i-inserted-during-upsert-in-mongoengine – Scott Stafford Mar 07 '14 at 14:33
  • 1
    What I dislike about the update method is that you can't just simply pass it a simple dictionary to specify the update properties. You have to prepend "set__" in before each key. – Robert Moskal Jan 31 '15 at 17:21
0

If you need pass a dictionery, can do this:

post = Post.objects(name="hello").first() or Post(name="hello")

then you can update with something like this:

# data = dictionary_with_data
for field, value in data.items():
    post[field] = value

post.save()
alper
  • 2,919
  • 9
  • 53
  • 102
Sarctiann
  • 11
  • 4