Django 更新一条可能不存在的特定数据是 Get 性能好还是 Filter 好

2021-01-26 19:47:17 +08:00
 Phishion

我想优化一下性能,但是有一个地方有 2 种写法,我不知道哪种好一点,就是比如有条数据进来,我要先判断这条数据在不在,如果在就更新,不在就直接新建一条

伪代码如下:

方法 1 )

try:
    article = Article.objects.get(id=9999)
    触发:更新逻辑
except:
    触发:新建逻辑

方法 2 )

article = Article.objects.filter(id=9999)
if article.exists():
    exist_article = article[0]
    触发:更新逻辑
else:
    触发:新建逻辑

感觉方法 1 有点奇怪,但是似乎就访问一次数据库,方法二啰嗦一点,不知道有没有老鸟能提供帮助?

2607 次点击
所在节点    Django
19 条回复
wuwukai007
2021-01-26 19:50:13 +08:00
get_or_create
youngce
2021-01-26 19:51:38 +08:00
Article.objects.update_or_create() ?
Phishion
2021-01-26 19:51:49 +08:00
@wuwukai007 请问,这种分装好的底层又是怎么实现的呢?
renmu123
2021-01-26 19:51:58 +08:00
1 比较符合 Python 哲学
Phishion
2021-01-26 19:55:51 +08:00
@wuwukai007
我看它文档里面还真是 try 出来的,真原始啊
zeroDev
2021-01-26 19:55:58 +08:00
应该用 try except 结构,避免判断
参考 Python 官方文档 https://docs.python.org/3.6/glossary.html#term-eafp
zeroDev
2021-01-26 19:56:54 +08:00
@Phishion try 就避免了很多条件判断,写 Python 本身就是为了快速开发嘛
Phishion
2021-01-26 19:57:23 +08:00
@zeroDev 好的 我瞧瞧
Phishion
2021-01-26 19:58:08 +08:00
@zeroDev 我一直潜意识认为报错会有额外的性能开销,所以就感觉用 try 处理正常业务逻辑很奇怪
wuwukai007
2021-01-26 20:04:18 +08:00
你可以这样理解,如果存在的可能新远远大于 不存在,那么使用 try 的性能不就会搞的多吗,不用 try,每次都要判断
Phishion
2021-01-26 20:07:05 +08:00
@wuwukai007 有道理,谢谢!
Geek981108
2021-01-26 20:07:29 +08:00
文档如是说:
update_or_create()
update_or_create(defaults=None, **kwargs)
A convenience method for updating an object with the given kwargs, creating a new one if necessary. The defaults is a dictionary of (field, value) pairs used to update the object. The values in defaults can be callables.

Returns a tuple of (object, created), where object is the created or updated object and created is a boolean specifying whether a new object was created.

The update_or_create method tries to fetch an object from database based on the given kwargs. If a match is found, it updates the fields passed in the defaults dictionary.

This is meant as a shortcut to boilerplatish code. For example:

defaults = {'first_name': 'Bob'}
try:
obj = Person.objects.get(first_name='John', last_name='Lennon')
for key, value in defaults.items():
setattr(obj, key, value)
obj.save()
except Person.DoesNotExist:
new_values = {'first_name': 'John', 'last_name': 'Lennon'}
new_values.update(defaults)
obj = Person(**new_values)
obj.save()
This pattern gets quite unwieldy as the number of fields in a model goes up. The above example can be rewritten using update_or_create() like so:

obj, created = Person.objects.update_or_create(
first_name='John', last_name='Lennon',
defaults={'first_name': 'Bob'},
)
For detailed description how names passed in kwargs are resolved see get_or_create().

As described above in get_or_create(), this method is prone to a race-condition which can result in multiple rows being inserted simultaneously if uniqueness is not enforced at the database level.

Like get_or_create() and create(), if you’re using manually specified primary keys and an object needs to be created but the key already exists in the database, an IntegrityError is raised.
lixuda
2021-01-26 20:10:52 +08:00
我用这样,不知道性能好不好
article = Article.objects.filter(id=9999).first()
if(article):
Phishion
2021-01-26 20:13:12 +08:00
@lixuda 我觉得用它封装好的性能最好,判断太多代码也显得不整洁
lixuda
2021-01-26 21:45:31 +08:00
@Phishion 看错了,是更新逻辑,update_or_create
Austaras
2021-01-27 00:51:32 +08:00
有的数据库支持 upsert
chaleaoch
2021-01-27 09:21:57 +08:00
你看源码,其实这俩逻辑差不多. get 里面就是 filter + try catch
nonduality
2021-01-27 15:17:55 +08:00
你的方法 2 会引发两次查询,优化文档经常提到不要滥用 exists():如果后续仍需要使用到对象数据,就无须用 exists()去判断对象存在与否。

用 #13 的方法也可以。
Phishion
2021-01-27 18:33:37 +08:00
@nonduality
@chaleaoch
@lixuda
感谢提供指导!

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/748663

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX