如何使用Django进行"批量更新"?

Tho*_*rzl 133 django django-models

我想用Django更新一个表 - 在原始SQL中是这样的:

update tbl_name set name = 'foo' where name = 'bar'
Run Code Online (Sandbox Code Playgroud)

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

list = ModelClass.objects.filter(name = 'bar')
for obj in list:
    obj.name = 'foo'
    obj.save()
Run Code Online (Sandbox Code Playgroud)

有更优雅的方式吗?

jb.*_*jb. 221

请参阅以下django文档部分

一次更新多个对象

总之,你应该可以使用:

ModelClass.objects.filter(name='bar').update(name="foo")
Run Code Online (Sandbox Code Playgroud)

您还可以使用F对象来执行增加行的操作:

from django.db.models import F
Entry.objects.all().update(n_pingbacks=F('n_pingbacks') + 1)
Run Code Online (Sandbox Code Playgroud)

请参阅文档:https://docs.djangoproject.com/en/1.9/topics/db/queries/

但请注意:

  • 这不会使用ModelClass.save方法(所以如果你内部有一些逻辑将不会被触发).
  • 不会发出django信号.

  • 另请注意,由于不使用`save()`,因此不会更新带有`auto_now = True`("modified"列)的`DateTimeField`字段. (25认同)
  • 但是`ModelClass.objects.filter(name ='bar').update(name ="foo")`不符合批量更新的目的,如果我对不同的id有不同的数据我怎么能不使用循环呢? (3认同)

nu *_*est 30

考虑使用django-bulk-update发现这里在GitHub上.

安装: 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
Run Code Online (Sandbox Code Playgroud)

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

  • 我尝试了django-bulk-update,我个人不鼓励使用它.它在内部的作用是创建一个如下所示的单个SQL语句:UPDATE"table"SET"field"= CASE"id"WHEN%s THEN%s WHEN%s THEN%s [...] WHERE id in( %s,%s,[...]);. 对于少数行来说这是好的(当不需要批量更新程序时),但是对于10,000,查询是如此复杂,postgres花费更多时间在CPU上100%理解查询,而不是节省写入磁盘的时间. (11认同)
  • @MarcGarcia我不同意批量更新没有价值,只有在需要进行数千次更新时才真正需要.由于您提到的原因,使用它一次执行10,000行是不可取的,但使用它一次更新50行比使用50个单独的更新请求命中数据库要高效得多. (3认同)
  • 我找到的最佳解决方案是:a)使用@ transaction.atomic decorator,它通过使用单个事务来提高性能,或者b)在临时表中进行批量插入,然后从临时表到原始表进行UPDATE. (3认同)
  • @MarcGarcia 好点。我发现许多开发人员在不知道它们的影响的情况下使用外部库 (2认同)

Jas*_*sir 18

这是我在互联网上找到的关于上述问题的有用内容

https://www.sankalpjonna.com/learn-django/running-a-bulk-update-with-django

低效的方式

model_qs= ModelClass.objects.filter(name = 'bar')
for obj in model_qs:
    obj.name = 'foo'
    obj.save()
Run Code Online (Sandbox Code Playgroud)

有效的方法

ModelClass.objects.filter(name = 'bar').update(name="foo") # for single value 'foo' or add loop
Run Code Online (Sandbox Code Playgroud)

使用批量更新

update_list = []
model_qs= ModelClass.objects.filter(name = 'bar')
for model_obj in model_qs:
    model_obj.name = "foo" # Or what ever the value is for simplicty im providing foo only
    update_list.append(model_obj)
    
ModelClass.objects.bulk_update(update_list,['name'])
Run Code Online (Sandbox Code Playgroud)

使用原子事务

from django.db import transaction

with transaction.atomic():
    model_qs = ModelClass.objects.filter(name = 'bar')
    for obj in model_qs:
       ModelClass.objects.filter(name = 'bar').update(name="foo")
Run Code Online (Sandbox Code Playgroud)

有赞成票吗?提前致谢:感谢您的关注;)

  • 你的bulk_update示例对我来说毫无意义——为什么你要从数据库中重新提取model_obj?与您引用的文章相同 - 它的bulk_update示例根本不需要任何数据库选择,它只需执行 `User.objects.bulk_update([User(id=k, Score=v) for k, v in user_ids_dict.items( )], ['分数'])`... (3认同)

vel*_*lis 14

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

https://docs.djangoproject.com/en/dev/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])
Run Code Online (Sandbox Code Playgroud)


And*_*röm 12

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

some_list = ModelClass.objects.filter(some condition).values('id')
ModelClass.objects.filter(pk__in=some_list).update(foo=bar)
Run Code Online (Sandbox Code Playgroud)

如果要根据某些条件更新具有不同值的行集合,最好可以根据值批量更新。假设您有 1000 行,您想将一列设置为 X 值之一,然后您可以预先准备批次,然后只运行 X 更新查询(每个基本上都具有上面第一个示例的形式)+初始 SELECT -询问。

如果每一行都需要一个唯一值,则无法避免每次更新一次查询。如果您在后一种情况下需要性能,也许可以查看其他架构,例如 CQRS/事件源。


kat*_*aja 12

要更新相同的值,我们可以简单地使用它

ModelClass.objects.filter(name = 'bar').update(name='foo')
Run Code Online (Sandbox Code Playgroud)

使用不同的值进行更新

ob_list = ModelClass.objects.filter(name = 'bar')
obj_to_be_update = []
for obj in obj_list:
    obj.name = "Dear "+obj.name
    obj_to_be_update.append(obj)
ModelClass.objects.bulk_update(obj_to_be_update, ['name'], batch_size=1000)
Run Code Online (Sandbox Code Playgroud)

它不会每次都触发保存信号,而是将所有要更新的对象保留在列表中并立即触发更新信号。