【问题标题】:Updating selection of objects, each with a different value in bulk (Django)更新对象的选择,每个对象都具有不同的批量值(Django)
【发布时间】:2023-04-11 03:48:02
【问题描述】:

假设我有一个 python 字典,其中键是现有的用户 ID,值是要添加到这些用户现有分数的分数。

例如:{1: 1580, 4: 540, 2: 678}(这可以延伸到n k,v 对)

我需要更新所有这些用户对象的分数(updated_score = original_score + new_score)。一种方法是迭代,如下所示:

from django.db.models import F
scores = {1: 1580, 4: 540, 2: 678}
for user_id,score_to_add in scores.iteritems():
    UserProfile.objects.filter(user_id=user_id).update(score=F('score')+score_to_add)

但这是多个数据库调用。我可以在一个电话中完成吗?一个说明性的例子会很棒。如您所料,这是针对 Django 项目的。

【问题讨论】:

  • 从未使用过,但 django-bulk-update 可能有帮助? github.com/aykut/django-bulk-update
  • 我认为这是不可行的,因为您需要在每一行中使用不同的值。正如@DA--所说,您应该使用这个包或将for 循环包装在transaction.atomic() 上下文中。

标签: django django-orm


【解决方案1】:

类似的东西:

from django.db.models import F
from django.db import transaction

with transaction.atomic():
    scores = {1: 1580, 4: 540, 2: 678}
    for user_id,score_to_add in scores:
        UserProfile.objects.filter(user_id=user_id).update(score=F('score')+score_to_add)

更多关于here

你也可以看看this answer

[更新]:

TL;DR:它不会进行一次数据库查询,但它会更快,因为每个查询都没有数据库开销。

正如his answer 中的文档和@ahmed 所说:

Django 的默认行为是以自动提交模式运行。每个查询都是 立即提交到数据库,除非事务是 活跃。

通过使用with transaction.atomic(),所有插入都被分组到一个 单笔交易。提交事务所需的时间是 摊销在所有封闭的插入语句中,因此每次的时间 大大减少了insert语句。

【讨论】:

  • 那么准确地说,这是否意味着单个数据库操作?
  • 嗯“有点”单个数据库操作(更新了我的答案)。每次都没有数据库开销。我不相信你问的问题对于 Django ORM 是可行的,除非你使用 extra 来编写自定义 SQL 查询。
  • 是的,我也阅读了您链接到的文档。我认为这主要会帮助我避免比赛条件。这也可以称为优化——但是当字典很大时会发生什么?对表执行的更新将阻止所有其他更新。所以从技术上讲,随着n 的扩展,事务的原子性质又回来咬我们。你不觉得吗?如果它是一个简单的 for 循环,就可以避免这种情况。
  • 引用here如果“相同”事务[...]同时运行,则不会发生任何特殊情况。它们将简单地同时运行。不保证顺序。所以,在我看来,n 的大小不会影响桌面上的更新。那将是另一个很好的 SO 问题。
【解决方案2】:

@nik_m 提出的transaction.atomic() 是个好主意,但您也应该在单个请求中从数据库中获取记录。

from django.db.models import F
from django.db import transaction

with transaction.atomic():
    scores = {1: 1580, 4: 540, 2: 678}
    users_to_update = UserProfile.objects.filter(
        user_id__in=scores.keys()
    ) 
    for user in users_to_update:
        user.update(score=F('score') + scores[user.user_id])

【讨论】:

  • 这也好不到哪里去;事实上它会做额外的查询。 nik 的版本根本不需要获取对象。
  • @DanielRoseman 你确定吗?他的版本迭代 dict 并为每条记录进行提取(使用.filter())。不是吗?
  • 没有。过滤器本身不会进行提取。每个项目只有一个 UPDATE 调用。
猜你喜欢
  • 2011-06-11
  • 2023-03-29
  • 2016-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-04
  • 2022-09-07
  • 1970-01-01
相关资源
最近更新 更多