Registry / database / django-bulk-update

django-bulk-update

JSON →
library2.2.0pypypi✓ verified 91d ago

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.

pip install django-bulk-update
INSTALL
IMPORT
SIG · DJANGO-BULK-UPDATE
D
django-bulk-update
databasepythonv2.2.0
Install
3.5s avg
Import
581ms
Disk
66MB
Pass rate
10/ 10
Env Coverage10 / 10
glibc
3.9–3.13
musl
3.9–3.13
Install & Compatibility
Where this runs
tested against v2.2.0 · pip install
no network on importno background threads
Install × environment matrix
Each cell = how many times install + import succeeded across repeated harness runs. Partial = flaky.
glibc = Debian/Ubuntu slim · musl = Alpine Linux
musl
py 3.10–3.920 runs
installs and imports cleanly · install 0.0s · import 0.615s · 66.5MB
glibc
py 3.10–3.920 runs
installs and imports cleanly · install 3.5s · import 0.548s · 67MB
66MB installed
● package 66MB
Code
Verified usage

Verified import paths — ran on the pinned version, not inferred.

BulkUpdateManager
✓ from django_bulk_update.manager import BulkUpdateManager
Subclassing or assigning this manager to your model's `objects` attribute enables the `bulk_update` method.

This quickstart demonstrates how to integrate `BulkUpdateManager` into a Django model, create initial instances using `bulk_create`, modify these instances in memory, and then efficiently persist changes to specific fields across all modified objects using a single `bulk_update` query. It includes a complete, runnable standalone Django setup with an in-memory SQLite database.

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)
Debug
Known issues
breakingThe `on_conflict` parameter, which previously allowed specifying behavior on conflict during an update, was removed in version 2.0.0.
fix
If you relied on `on_conflict`, you must manually implement conflict resolution logic or re-evaluate your update strategy. For simple 'ignore duplicates' cases with `bulk_create` (not `bulk_update`), consider using `ignore_duplicates=True`.
affects: 2.0.0+
gotchaOmitting 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.
fix
Always explicitly list only the fields that *must* be updated in `update_fields`. This optimizes the generated SQL query and prevents unintended updates to other fields.
affects: All
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).
fix
If related objects require updating, perform those operations separately using standard ORM methods or other bulk operations specifically for those related models.
affects: All
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.
fix
Manage the `update_fields` argument carefully. If you only want to update fields that have genuinely changed, you need to implement your own dirty tracking logic or filter the list of objects and fields before calling `bulk_update`.
affects: All
Errors
Common errors & fixes
'QuerySet' object has no attribute 'pk'
This error occurs when attempting to pass a Django QuerySet object directly to `bulk_update`, as `bulk_update` expects a list of individual model instances, each with a primary key to identify the record to update.
fix
First, retrieve the model instances into a list, modify them as needed, and then pass that list of instances to `bulk_update`. 

```python
# Incorrect
# MyModel.objects.filter(some_field=value).bulk_update([...]) 

# Correct
my_objects = list(MyModel.objects.filter(some_field=value))
for obj in my_objects:
    obj.field_to_update = new_value
MyModel.objects.bulk_update(my_objects, ['field_to_update'])
```
ValueError: All bulk_update() objects must have a primary key set.
`bulk_update` is designed exclusively for updating existing database records. This error is raised when you attempt to pass new model instances that have not yet been saved to the database (and thus lack a primary key) to the `bulk_update` function.
fix
Ensure that all model instances provided to `bulk_update` have already been saved to the database and possess a primary key. For creating multiple new objects in a single query, use `Model.objects.bulk_create()` instead.

```python
# Incorrect (if 'new_obj' hasn't been saved yet)
# MyModel.objects.bulk_update([new_obj], ['field_name'])

# Correct (for updating existing objects)
existing_obj = MyModel.objects.get(pk=1)
existing_obj.field_name = 'updated_value'
MyModel.objects.bulk_update([existing_obj], ['field_name'])

# Correct (for creating new objects)
new_obj_1 = MyModel(field_name='value1')
new_obj_2 = MyModel(field_name='value2')
MyModel.objects.bulk_create([new_obj_1, new_obj_2])
```
ModuleNotFoundError: No module named 'django_bulk_update'
This error indicates that the Python interpreter cannot find the `django_bulk_update` module, typically because the library is not installed or the import path for its `bulk_update` helper function is incorrect.
fix
First, ensure the library is installed in your environment:

```bash
pip install django-bulk-update==2.2.0
```

Then, import the `bulk_update` function from its correct location:

```python
from django_bulk_update.helper import bulk_update
```
'Manager' object has no attribute 'bulk_update'
This error occurs when `bulk_update` is mistakenly called directly on a Django `Manager` object (e.g., `MyModel.objects.bulk_update(...)`). Django's built-in `bulk_update` is a `QuerySet` method, while `django-bulk-update` provides a helper function that takes a list of instances.
fix
If using Django's built-in `bulk_update`, call it on a `QuerySet`. If using `django-bulk-update`'s helper, import it and pass a list of instances.

```python
# Incorrect
# MyModel.objects.bulk_update(my_objects, ['field_name'])

# Correct (using Django's built-in bulk_update on a QuerySet)
my_objects = list(MyModel.objects.filter(some_condition=True))
for obj in my_objects:
    obj.field_name = 'new_value'
MyModel.objects.bulk_update(my_objects, ['field_name'])

# Correct (using django-bulk-update helper)
from django_bulk_update.helper import bulk_update
my_objects = list(MyModel.objects.filter(some_condition=True))
for obj in my_objects:
    obj.field_name = 'new_value'
bulk_update(my_objects, update_fields=['field_name'])
```
Upgrade
Version history
2.2.0latest on PyPI · released Aug 12, 2017
Audit
Dependencies
DjangorequiredRequired for Django ORM integration. Supports Django>=2.2.
Agent activity
6 hits · last 30 days
node
6
Resources
django-bulk-update — pip install django-bulk-update · libregistry