【问题标题】:Update multiple objects at once in Django?在 Django 中一次更新多个对象?
【发布时间】:2016-04-20 17:46:27
【问题描述】:

我正在使用 Django 1.9。我有一个 Django 表,它表示按月组织的特定度量值的值,并带有原始值和百分位数:

class MeasureValue(models.Model):
    org = models.ForeignKey(Org, null=True, blank=True)
    month = models.DateField()
    calc_value = models.FloatField(null=True, blank=True)
    percentile = models.FloatField(null=True, blank=True)

每月通常有 10,000 个左右。我的问题是我是否可以加快在模型上设置值的过程。

目前,我通过使用 Django 过滤器查询检索一个月的所有测量值,将其转换为 pandas 数据框,然后使用 scipy 的rankdata 设置排名和百分位数来计算百分位数。我这样做是因为 pandas 和 rankdata 高效,能够忽略空值,并且能够以我想要的方式处理重复值,所以我对这种方法很满意:

records = MeasureValue.objects.filter(month=month).values()
df = pd.DataFrame.from_records(records)
// use calc_value to set percentile on each row, using scipy's rankdata

但是,我需要从数据框中检索每个百分位值,并将其设置回模型实例。现在我通过迭代数据框的行并更新每个实例来做到这一点:

for i, row in df.iterrows():
    mv = MeasureValue.objects.get(org=row.org, month=month)
    if (row.percentile is None) or np.isnan(row.percentile):
        row.percentile = None
    mv.percentile = row.percentile
    mv.save()

不出所料,这很慢。是否有任何有效的 Django 方法来加速它,通过编写单个数据库而不是数万?我有checked the documentation,但看不到一个。

【问题讨论】:

  • 你看过QuerySet.update()吗?
  • @Two-BitAlchemist 我不确定它是否有帮助,因为我需要在每一行上设置不同的值...?
  • 哦,我错过了这个要求,抱歉

标签: python django


【解决方案1】:

原子事务可以减少在循环中花费的时间:

from django.db import transaction

with transaction.atomic():
    for i, row in df.iterrows():
        mv = MeasureValue.objects.get(org=row.org, month=month)

        if (row.percentile is None) or np.isnan(row.percentile): 
            # if it's already None, why set it to None?
            row.percentile = None

        mv.percentile = row.percentile
        mv.save()

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

通过使用with transaction.atomic(),所有插入都被分组到一个事务中。提交事务所需的时间在所有包含的插入语句中分摊,因此每个插入语句的时间大大减少。

【讨论】:

  • 谢谢。为什么这会减少时间?文档没有帮助docs.djangoproject.com/en/1.9/topics/db/transactions/…
  • 因为查询被组合成一个大查询,只执行一次。假设保存到数据库需要 10 毫秒,如果您保存 100 个运行自动提交的对象,则需要 10 毫秒 * 100,但如果您使用事务,则只需 10 毫秒 * 1(粗略)
  • 非常有帮助的完整答案。实际上我正在寻找其他东西,但得到了很好的选择
【解决方案2】:

从 Django 2.2 开始,您可以使用 bulk_update() queryset 方法有效地更新提供的模型实例上的给定字段,通常只需一个查询:

objs = [
    Entry.objects.create(headline='Entry 1'),
    Entry.objects.create(headline='Entry 2'),
]
objs[0].headline = 'This is entry 1'
objs[1].headline = 'This is entry 2'
Entry.objects.bulk_update(objs, ['headline'])

在旧版本的 Django 中,您可以使用 update()Case/When,例如:

from django.db.models import Case, When

Entry.objects.filter(
    pk__in=headlines  # `headlines` is a pk -> headline mapping
).update(
    headline=Case(*[When(pk=entry_pk, then=headline)
                    for entry_pk, headline in headlines.items()]))

【讨论】:

  • 感谢您将旧版本放在那里。我无法访问bulk_update,因此了解update 电话令人放心。
  • Case, Whentransactions 快吗?
【解决方案3】:

实际上,尝试@Eugene Yarmash 的回答我发现我收到了这个错误:

FieldError: Joined field references are not permitted in this query

但我相信迭代update 仍然比多次保存更快,我希望使用事务也应该加快速度。

因此,对于不提供 bulk_update 的 Django 版本,假设 Eugene 的答案中使用的数据相同,其中 headlines 是一个 pk -> 标题映射:

from django.db import transaction

with transaction.atomic():
    for entry_pk, headline in headlines.items():
        Entry.objects.filter(pk=entry_pk).update(headline=headline)

【讨论】:

    猜你喜欢
    • 2021-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-10
    相关资源
    最近更新 更多