Django の bulk_create() でパフォーマンスを爆上げ! 高速データ作成のヒント

2024-11-07

django.db.models.query.QuerySet.bulk_create() は、Django における強力な機能で、一度のデータベース操作で複数のオブジェクトを効率的に作成することができます。これは、従来の save() メソッドを個別に呼び出すよりも、大幅なパフォーマンス向上を実現できます。

使い方

from django.db import transaction

objects_to_create = [
    MyModel(field1=value1, field2=value2),
    MyModel(field1=value3, field2=value4),
    # ...
]

with transaction.atomic():
    MyModel.objects.bulk_create(objects_to_create)

上記のコード例では、MyModel インスタンスのリスト objects_to_create を用意し、トランザクション内で bulk_create() メソッドを呼び出すことで、一度の操作で複数のオブジェクトを作成しています。

利点

  • トランザクションの一貫性
    bulk_create() はトランザクション内で実行されるため、データの整合性を保ちながらオブジェクトを作成することができます。
  • コードの簡潔化
    複雑なループや条件分岐を必要とせず、簡潔で読みやすいコードとなります。
  • パフォーマンス向上
    個別に save() メソッドを呼び出すよりも、大幅な処理速度の向上が期待できます。特に、大量のオブジェクトを作成する場合に効果を発揮します。
  • シグナルの送信
    bulk_create() は、個別に save() メソッドを呼び出す場合に送信されるシグナルを送信しません。シグナルが必要な場合は、個別に save() メソッドを使用する必要があります。
  • 一意制約の検証
    bulk_create() は一意制約の検証を行いません。そのため、重複するデータが存在する場合は、エラーが発生する可能性があります。
  • 主キーの自動割り当て
    bulk_create() を使用する場合、主キーは自動的に割り当てられます。自分で主キーを設定したい場合は、個別に save() メソッドを使用する必要があります。
  • bulk_create() は、データベースによってはサポートされていない場合があります。詳細については、Django 公式ドキュメントを参照してください。
  • bulk_create() は、Django 1.7 以降で使用可能です。


from django.db import transaction

# MyModel インスタンスのリストを作成
objects_to_create = [
    MyModel(field1="value1", field2="value2"),
    MyModel(field1="value3", field2="value4"),
    # ...
]

# トランザクション内で bulk_create() を呼び出す
with transaction.atomic():
    MyModel.objects.bulk_create(objects_to_create)

例2:主キーの設定

from django.db import transaction

# 主キーを設定した MyModel インスタンスのリストを作成
objects_to_create = [
    MyModel(id=1, field1="value1", field2="value2"),
    MyModel(id=2, field1="value3", field2="value4"),
    # ...
]

# トランザクション内で bulk_create() を呼び出す
with transaction.atomic():
    MyModel.objects.bulk_create(objects_to_create)

例3:関連オブジェクトの作成

from django.db import transaction

# MyModel インスタンスと関連オブジェクトを作成
objects_to_create = [
    MyModel(field1="value1", field2="value2", related_object=RelatedModel(field3="value3")),
    MyModel(field1="value4", field2="value5", related_object=RelatedModel(field3="value6")),
    # ...
]

# トランザクション内で bulk_create() を呼び出す
with transaction.atomic():
    MyModel.objects.bulk_create(objects_to_create)
from django.db import transaction

try:
    # MyModel インスタンスのリストを作成
    objects_to_create = [
        MyModel(field1="value1", field2="value2"),
        MyModel(field1="value1", field2="value2"),  # 重複データ
        # ...
    ]

    # トランザクション内で bulk_create() を呼び出す
    with transaction.atomic():
        MyModel.objects.bulk_create(objects_to_create)
except IntegrityError as e:
    # エラー処理を実行
    print(f"エラーが発生しました: {e}")


代替方法

  1. ループで save() メソッドを個別に呼び出す
for obj in objects_to_create:
    obj.save()

これは最も基本的な方法ですが、bulk_create() に比べて処理速度が遅くなります。また、コードが冗長になり、可読性が低下する可能性があります。

  1. itertools.islice() と save() メソッドを組み合わせて使用する
from itertools import islice

batch_size = 100
for batch in islice(objects_to_create, None, batch_size):
    MyModel.objects.bulk_save(batch)

この方法は、bulk_create() と同様にトランザクション内で処理を行い、パフォーマンスを向上させることができます。ただし、bulk_create() ほどシンプルではないため、理解や使用がやや複雑になります。

  1. サードパーティ製のライブラリを使用する

これらのライブラリは、bulk_create() よりも高速で柔軟な機能を提供する場合があります。しかし、導入や設定が必要となるため、bulk_create() よりも複雑になります。

どの方法を選択すべきか

最適な方法は、作成するオブジェクトの数、データベースの種類、パフォーマンス要件、および開発者のスキルレベルによって異なります。

  • 大量のオブジェクトを作成する場合
    • データベースのパフォーマンスが十分であれば、bulk_create() が最も簡単で効率的な方法です。
    • データベースのパフォーマンスが懸念事項である場合、またはより柔軟な機能が必要な場合は、itertools.islice()save() メソッドを組み合わせて使用する、またはサードパーティ製のライブラリを検討することができます。
  • 少量のオブジェクトを作成する場合
    ループで save() メソッドを個別に呼び出すのが最もシンプルで適切です。