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/...
Pramodさん

新しいデータを挿入したくありません。既存のものを更新してください。
ThomasSchwärzl2012

3
多分select_for_updateの助けを借りて?docs.djangoproject.com/en/dev/ref/models/querysets/…–
Jure C.

ModelClassアプローチについて厄介なことは何ですか?次にようジャンゴにフィード:stackoverflow.com/questions/16853649/...
チロSantilli郝海东冠状病六四事件法轮功

回答:


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メソッドを使用しません(そのため、内部にロジックがある場合はトリガーされません)。
  • ジャンゴシグナルは送信されません。
  • .update()スライスされたQuerySet に対してを実行することはできません。それは元のQuerySet上にある必要があるため、.filter()および.exclude()メソッドに依存する必要があります。

27
使用していないの結果としてことにも注意してくださいsave()DateTimeFieldとのフィールドauto_now=True(「改変」の列が)更新されません。
アーサー

3
しかしModelClass.objects.filter(name = 'bar').update(name="foo")、IDごとに異なるデータがある場合、ループを使用せずにそれを行うにはどうすればよいですか?
Shashank

@shihon正解かどうかはわかりませんが、答えに例を追加しました。
jb。

@Shashankあなたはあなたのケースの解決策をまだ見つけましたか?私も同じシナリオを持っています。
Sourav Prem 2017

Fオブジェクトを使用して.updateメソッドで異なるモデルを参照することはできません...たとえば、は使用できませんEntry.objects.all().update(title=F('blog__title'))。ドキュメントはこれについて少し言及しています。別のモデルからデータをプルしてエントリを更新する場合は、forループを実行する必要があります
sean.hudson

31

使用を検討してdjango-bulk-updateGitHubの上でここに

インストール: pip install django-bulk-update

実装:(プロジェクトのReadMeファイルから直接取得したコード)

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" WHEN%s THEN%s WHEN%s THEN%s [...] WHERE id in( %s、%s、[...]);。これはいくつかの行では大丈夫ですが(バルクアップデーターが不要な場合)、10,000の場合、クエリは非常に複雑になるため、ディスクへの書き込みを保存する時間よりも、postgresがCPUを100%理解するのに費やす時間が長くなります。 。
マルクガルシア

1
@MarcGarcia良い点。多くの開発者が影響を知らずに外部ライブラリを使用していることがわかりました
Dejell

3
@MarcGarcia私は、大量の更新は価値がなく、数千の更新が必要な場合にのみ本当に必要であることに同意しません。これを使用して一度に10,000行を実行することはお勧めできませんが、一度に50行を更新するために使用すると、50の個別の更新要求でデータベースにアクセスするよりもはるかに効率的です。
nu everest

3
私が見つけた最良の解決策は次のとおりです:a)@ transaction.atomicデコレーターを使用して単一のトランザクションを使用することでパフォーマンスを向上させる、またはb)一時テーブルに一括挿入してから一時テーブルから元のテーブルにUPDATEする。
マルクガルシア

1
私はこれが古いスレッドであることを知っていますが、実際にはCASE / WHEREが唯一の方法ではありません。PostgreSQLには他のアプローチもありますが、DB固有です(例:stackoverflow.com/a/18799497)。ただし、これがANSI SQLで可能かどうかは不明です
Iliev

21

Django 2.2バージョンにbulk_updateメソッドが追加されました(リリースノート)。

https://docs.djangoproject.com/en/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()メソッドを任意のクエリ用語と組み合わせて使用​​して、1つのクエリのすべての行を更新できます。

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

いくつかの条件に応じて異なる値で行のコレクション更新する場合は、値に従って更新をバッチ処理することができます。たとえば、列をX値の1つに設定する行が1000行あり、バッチを事前に準備してから、X更新クエリのみを実行できます(それぞれ基本的に上記の最初の例の形式です)+最初のSELECT -query。

場合はすべての行が一意の値を必要とし、更新ごとにクエリを回避するための方法はありません。後者のケースでパフォーマンスが必要な場合は、CQRS /イベントソーシングなどの他のアーキテクチャを検討してください。


1

ITは、テーブルで更新されたオブジェクトの数を返します。

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

このリンクを参照して、一括更新と一括作成の詳細を確認できます。 一括更新と作成

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.