Django Bulk Update
django-bulk-update is a Django library that enables efficient bulk updates of multiple model instances using a single database query. It significantly improves performance compared to iterating and calling `save()` on each object individually. The current version is 2.2.0, and the project maintains an active but not rapid release cadence, ensuring compatibility with recent Django versions.
Warnings
- breaking The `on_conflict` parameter, which previously allowed specifying behavior on conflict during an update, was removed in version 2.0.0.
- gotcha Omitting the `update_fields` argument, or providing an excessively long list of fields, can severely degrade performance. By default, it might attempt to update all fields.
- gotcha `bulk_update` operates exclusively on the direct fields of the model instances provided. It does not handle updates to related objects (e.g., ManyToMany fields, OneToMany relations, or ForeignKey fields pointing to other models).
- gotcha `bulk_update` does not automatically track 'dirty' fields. All fields listed in `update_fields` will be included in the `UPDATE` statement for every object provided, even if an instance's value for a given field has not actually changed.
Install
-
pip install django-bulk-update
Imports
- BulkUpdateManager
from django_bulk_update.manager import BulkUpdateManager
Quickstart
import os
import django
from django.conf import settings
from django.db import models
from django_bulk_update.manager import BulkUpdateManager
# --- Minimal Django setup for a runnable standalone script ---
# Configure settings for an in-memory SQLite database
if not settings.configured:
settings.configure(
DEBUG=True,
INSTALLED_APPS=[
'django.contrib.auth',
'django.contrib.contenttypes',
'myapp' # Dummy app for model definition
],
DATABASES={'default': {'ENGINE': 'django.db.backends.sqlite3', 'NAME': ':memory:'}}
)
django.setup()
# Define a simple Django model
class MyModel(models.Model):
name = models.CharField(max_length=255)
age = models.IntegerField()
objects = BulkUpdateManager() # Use the custom manager
class Meta:
app_label = 'myapp' # Required for standalone scripts without full project structure
def __str__(self):
return f"MyModel(id={self.id}, name='{self.name}', age={self.age})"
# --- Create and apply migrations for the in-memory database ---
# This mimics `python manage.py makemigrations myapp` and `python manage.py migrate myapp`
from django.core.management import call_command
from io import StringIO
# Redirect output to prevent verbose console output from migrations
stdout_redirect = StringIO()
stderr_redirect = StringIO()
try:
# Make migrations for 'myapp'
call_command('makemigrations', 'myapp', interactive=False, stdout=stdout_redirect, stderr=stderr_redirect)
# Apply migrations
call_command('migrate', 'myapp', interactive=False, stdout=stdout_redirect, stderr=stderr_redirect)
except Exception as e:
print(f"Error setting up database: {e}")
print(f"Stdout: {stdout_redirect.getvalue()}")
print(f"Stderr: {stderr_redirect.getvalue()}")
exit(1)
print("Database and model schema initialized successfully.\n")
# --- Quickstart core functionality ---
print("Creating objects with bulk_create...")
objs_to_create = [MyModel(name=f"User {i}", age=i) for i in range(5)]
MyModel.objects.bulk_create(objs_to_create)
# Retrieve them to ensure IDs are set and to have fresh instances
objs = list(MyModel.objects.all().order_by('id'))
print("Original objects:")
for obj in objs:
print(obj)
print("\nModifying objects in memory...")
for obj in objs:
obj.age += 10 # Increment age
obj.name = f"Updated {obj.name} (v2)" # Change name
# Perform the bulk update for specific fields
# Only 'name' and 'age' fields will be included in the UPDATE query
MyModel.objects.bulk_update(objs, update_fields=['name', 'age'])
print("\nObjects after bulk_update (fetched from DB):")
for obj in MyModel.objects.all().order_by('id'):
print(obj)