AppEngine(或Django)中有叫insert_or_update的快捷方法吗?为什么没有?
我发现我经常会遇到这样的情况:(我觉得这应该很常见吧?)
Google App Engine
# Insert if object doesn't exist, Otherwise Update Object
obj = get_or_insert('123', title='Hello World')
obj.title = 'Hello World'
obj.puts()
Django
# Insert if object doesn't exist, Otherwise Update Object
obj, created = Model.objects.get_or_create(id='123',
defaults={'title':'Hello World'}
if not created:
obj.title = 'Hello World'
obj.save()
如果我能直接调用 insert_or_update
方法,那该多好啊,对吧?比如:
# AppEngine
obj = insert_or_update('123', title='Hello World')
# Django
obj = Model.objects.insert_or_update(id='123', defaults={'title': 'Hello World'})
更新
回到 App Engine 的 文档,我发现了关于 get_or_insert
方法的说明。大概是这样的:
def txn(key_name, **kwds):
entity = Story.get_by_key_name(key_name, parent=kwds.get('parent'))
if entity is None:
entity = Story(key_name=key_name, **kwds)
entity.put()
return entity
def get_or_insert(key_name, **kwargs):
return db.run_in_transaction(txn, key_name, **kwargs)
get_or_insert('some key', title="The Three Little Pigs")
我在想,为什么在 App Engine 和 Django 中都没有创建一个 insert_or_update
方法呢。
App Engine 的解决方法
@classmethod
def insert_or_update(cls, key_name, parent=None, **kwargs):
def _tx():
entity = cls.get_by_key_name(key_name, parent=parent)
if entity:
for key in kwargs:
setattr(entity, key, kwargs[key])
else:
entity = cls(key_name=key_name, parent=parent, **kwargs)
entity.put()
return entity
return db.run_in_transaction(_tx)
Django 的解决方案
3 个回答
这可能没有你想的那么普遍。就我个人而言,我并不常常需要这样做,偶尔需要的时候,通常也只是比你提到的 insert_or_update
方法多两三行代码,这并不足以让我觉得一定要有这个功能,或者让我有动力自己去添加它。不过,我过去确实用过 MySQL 的 INSERT … ON DUPLICATE KEY UPDATE
这个功能很多次。
我自己也有过几次这样的经历,就在几周前,我在想为什么 Django 没有 M2M(多对多)关系的 toggle
方法……最后我自己做了一个:
https://github.com/Matt-Stevens/django/commit/f7c36
编辑
Django 1.7 看起来会有这个 "UPSERT" 的功能,https://github.com/django/django/commit/6272d
如果我理解这个问题没错的话:
obj, created = Model.objects.get_or_create(id='123',
defaults={'title':'Hello World'})
上面的内容应该换成下面这个:
obj, created = Model.objects.get_or_create(foo='alpha', bar='beta'
defaults={'title':'Hello World'})
这里的 foo 和 bar 是模型中的字段。这样可以解决在django中出现的重复问题。至于appengine的情况就不太确定了。希望这对你有帮助!
Django
update_or_create(来自Django官方文档)
update_or_create(defaults=None, **kwargs)
在Django 1.7版本中新增加的功能。
这个方法很方便,可以用给定的 kwargs
来更新一个对象,如果没有这个对象,就会创建一个新的。defaults
是一个字典,里面包含了(字段,值)的配对,用来更新对象。
这个方法会返回一个元组 (object, created)
,其中 object
是被创建或更新的对象,而 created
是一个布尔值,表示是否创建了一个新对象。
update_or_create
方法会根据给定的 kwargs
从数据库中查找一个对象。如果找到了,就会更新 defaults
字典中传入的字段。
这个方法的目的是为了简化一些重复的代码。例如:
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
for key, value in updated_values.iteritems():
setattr(obj, key, value)
obj.save()
except Person.DoesNotExist:
updated_values.update({'first_name': 'John', 'last_name': 'Lennon'})
obj = Person(**updated_values)
obj.save()
当模型中的字段数量增加时,这种模式会变得相当复杂。上面的例子可以用 update_or_create()
这样重写:
obj, created = Person.objects.update_or_create(
first_name='John', last_name='Lennon', defaults=updated_values)
关于如何解析传入 kwargs
的名称的详细描述,可以查看 get_or_create()
。
正如上面在 get_or_create()
中所描述的,这个方法可能会出现竞争条件,如果数据库层面没有强制唯一性,可能会导致同时插入多行数据。
AppEngine
目前还不太幸运,正如Matt之前提到的,这可能不是一个常见的使用场景。也许在AppEngine中,这种使用场景并不多。