Usage
django-bulk-tracker will emit a signal whenever you update, create, or delete a record in the database
django-bulk-tracker supports bulk operations:
queryset.update()queryset.bulk_update()queryset.bulk_create()queryset.delete()
and single operations:
create()save() # update and createdelete()
all you need to do is to define your Model and inherit from:
from bulk_tracker.models import BulkTrackerModel
class Author(BulkTrackerModel):
first_name = models.CharField(max_length=200)
last_name = models.CharField(max_length=200)
OR if you have a custom queryset inherit from or Don’t want to support single-operation:
from bulk_tracker.managers import BulkTrackerQuerySet
class MyModelQuerySet(BulkTrackerQuerySet):
def do_something_custom(self):
pass
now you can listen to the signals post_update_signal, post_create_signal, post_delete_signal:
@receiver(post_update_signal, sender=MyModel)
def i_am_a_receiver_function(
sender,
objects: list[ModifiedObject[MyModel]],
tracking_info_: TrackingInfo | None = None,
**kwargs,
):
do_stuff()
Hint
all signals have the same signature for consistency, and also in case you want to assign one function to listen to multiple signals
ModifiedObject is a very simple object, it contains 2 attributes:
1- instance this is your model instance after it has been updated, or created
2- changed_values is dict[str, Any] which contains the changed fields only in case of post_update_signal,
in case of post_create_signal and post_delete_signal, changed_values will be an empty dict {}
Optionally you can pass tracking_info_ to your functions, as in:
from bulk_tracker.helper_objects import TrackingInfo
def a_function_that_updates_records():
user = self.request.user
MyModel.objects.filter(name='john').update(
name='jack',
tracking_info_=TrackingInfo(user=user, comment="Updated from a function", kwargs={'app-build':1.1.8}),
)
Hint
tracking_info_ has a trailing underscore to avoid collision with your actual fields
you can use TrackingInfo to implement any kind of behavior like logging in your
signal handlers and you need to capture more info about the operation that is happening.
For single operations as well
create()save() # update and createdelete()
to support we rely on the amazing library django-model-utils to track the model instances
do the above
you need to to inherit your model from
BulkTrackerModeladd
tracker = FieldTracker()to your model
as in
from bulk_tracker.models import BulkTrackerModel
from model_utils import FieldTracker
class MyModel(BulkTrackerModel):
objects = MyModelQuerySet.as_manager() # MyModelManager() if you have
tracker = FieldTracker()
Complete Example
# models.py
from bulk_tracker.models import BulkTrackerModel
from model_utils import FieldTracker
from myapp.managers import MyModelManager
class MyModel(BulkTrackerModel):
first_field = models.CharField()
second_field = models.CharField()
objects = MyModelManager()
tracker = FieldTracker()
# managers.py
from bulk_tracker.managers import BulkTrackerQuerySet # optional
class MyModelQuerySet(BulkTrackerQuerySet):
pass
class MyModelManager(BulkTrackerManager.from_queryset(MyModelQuerySet)): # optional
pass
# signal_handlers.py
from bulk_tracker.signals import post_update_signal
from bulk_tracker.helper_objects import ModifiedObject, TrackingInfo
@receiver(post_update_signal, sender=MyModel)
def i_am_a_receiver_function(
sender,
objects: list[ModifiedObject[MyModel]],
tracking_info_: TrackingInfo | None = None,
**kwargs,
):
user = tracking_info_.user if tracking_info_ else None
for modified_objects in modified_objects:
if 'name' in modified_object.changed_values:
log(f"field 'name' has changed by {user or ''}")
notify_user()