如何使用Django批量更新?


163

我想用Django更新表格-原始SQL中的内容如下:

update tbl_name set name = 'foo' where name = 'bar'

我的第一个结果是这样的-但这很讨厌,不是吗?

list = ModelClass.objects.filter(name = 'bar')
for obj in list:
    obj.name = 'foo'
    obj.save()

有没有更优雅的方式?


1
您可能正在寻找批量插入。看看stackoverflow.com/questions/4294088/...
普拉莫德

我不喜欢插入新数据-仅更新现有数据。
ThomasSchwärzl2012年

3
也许在select_for_update的帮助下?docs.djangoproject.com/en/dev/ref/models/querysets/…–
Jure C.

这种ModelClass方法有什么不好的?再喂到Django的是:stackoverflow.com/questions/16853649/...
西罗桑蒂利郝海东冠状病六四事件法轮功

Answers:


256

更新:

Django 2.2版本现在具有bulk_update

旧答案:

请参阅以下django文档部分

一次更新多个对象

简而言之,您应该可以使用:

ModelClass.objects.filter(name='bar').update(name="foo")

您还可以使用F对象来执行诸如增加行数之类的操作:

from django.db.models import F
Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)

请参阅文档

但是,请注意:

  • 这不会使用ModelClass.save方法(因此,如果您内部有逻辑,则不会被触发)。
  • 没有Django信号将被发射。
  • 您无法.update()在切片的QuerySet上执行,而必须在原始QuerySet上执行,因此您需要依靠.filter()and .exclude()方法。

27
另请注意,由于不使用save(),因此不会更新DateTimeField带有auto_now=True(“修改的”列)的字段。
亚瑟

3
但是ModelClass.objects.filter(name = 'bar').update(name="foo")不能满足批量更新的目的,如果我对不同的id有不同的数据,该如何不使用loop呢?
沙申克2015年

@shihon我不确定我是否正确,但是我在答案中添加了示例。
jb。

@Shashank您是否找到适合您的情况的解决方案?我也有同样的情况。
Sourav Prem

无法使用F对象在.update方法中引用不同的模型……例如,您不能使用Entry.objects.all().update(title=F('blog__title'))。Docs对此很少提及。如果要从另一个模型中提取数据以更新条目,则必须运行for循环
sean.hudson 18'Aug

31

考虑使用GitHub上的django-bulk-update found 。

安装: pip install django-bulk-update

实施:(直接从项目自述文件中获取的代码)

from bulk_update.helper import bulk_update

random_names = ['Walter', 'The Dude', 'Donny', 'Jesus']
people = Person.objects.all()

for person in people:
    r = random.randrange(4)
    person.name = random_names[r]

bulk_update(people)  # updates all columns using the default db

更新:正如Marc在评论中指出的那样,这不适合一次更新数千行。尽管它适用于10到100的小批量。适合您的批处理大小取决于您的CPU和查询复杂性。该工具更像是手推车,而不是自卸车。


16
我尝试过django-bulk-update,但我个人不鼓励使用它。它在内部做的是创建一个看起来像这样的SQL语句:UPDATE“ table” SET“ field” = CASE“ id”当%s THEN%s时%s THEN%s [...] %s,%s,[...]);。几行就可以了(不需要批量更新器时),但是对于10,000行来说,查询是如此复杂,以至于postgres在100%理解查询的情况下花费更多的时间在CPU上,而不是节省了写入磁盘的时间。 。
马克·加西亚

1
@MarcGarcia好点。我发现许多开发人员在不了解外部库影响的情况下使用外部库
Dejell

3
@MarcGarcia我不同意批量更新没有价值,只有在需要数千次更新时才真正需要。出于您提到的原因,建议不要一次使用它执行10,000行,但是一次使用它更新50行比使用50个单独的更新请求访问数据库要有效得多。
珠穆朗玛峰,2016年

3
我发现的最佳解决方案是:a)使用@ transaction.atomic装饰器,该装饰器通过使用单个事务来提高性能,或者b)在临时表中进行批量插入,然后从临时表到原始表进行UPDATE。
马克·加西亚

1
我知道这是一个旧线程,但是实际上CASE / WHERE不是唯一的方法。对于PostgreSQL,还有其他方法,但是它们是特定于数据库的,例如stackoverflow.com/a/18799497,但是我不确定在ANSI SQL中是否可行
Ilian Iliev

21

Django 2.2版本现在有一个bulk_update方法(发行说明)。

https://docs.djangoproject.com/zh-CN/stable/ref/models/querysets/#bulk-update

例:

# get a pk: record dictionary of existing records
updates = YourModel.objects.filter(...).in_bulk()
....
# do something with the updates dict
....
if hasattr(YourModel.objects, 'bulk_update') and updates:
    # Use the new method
    YourModel.objects.bulk_update(updates.values(), [list the fields to update], batch_size=100)
else:
    # The old & slow way
    with transaction.atomic():
        for obj in updates.values():
            obj.save(update_fields=[list the fields to update])


8

如果要在一组行上设置相同的值,则可以将update()方法与任何查询项结合使用来更新一个查询中的所有行:

some_list = ModelClass.objects.filter(some condition).values('id')
ModelClass.objects.filter(pk__in=some_list).update(foo=bar)

如果要根据某些条件用不同的值更新行的集合,则最好在情况下可以根据值批处理更新。假设您有1000行要在其中设置X值之一的列,那么您可以预先准备批处理,然后只运行X更新查询(每个查询都具有上述第一个示例的形式)+初始SELECT -查询。

如果每一行都需要一个唯一值,则无法避免每次更新都进行一次查询。如果在后一种情况下需要性能,则可以考虑使用其他结构,例如CQRS /事件源。


1

表中更新了IT返回的对象数。

update_counts = ModelClass.objects.filter(name='bar').update(name="foo")

您可以参考此链接以获取有关批量更新和创建的更多信息。 批量更新和创建

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.