https://django-bulk-tracker.readthedocs.io/en/latest/
pip install -r requirements_dev.txt
pytest
Here's the provided text translated from reStructuredText (.rst) format to Markdown (.md):
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 create
delete()
All you need to do is 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:
instance
this is your model instance after it has been updated, or createdchanged_values
is a dict[str, Any] which contains the changed fields only in the case ofpost_update_signal
, in the case ofpost_create_signal
andpost_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'}, is_robust=True),
)
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.
create()
save() # update and create
delete()
To support, we rely on the amazing library django-model-utils
to track the model instances:
- Do the above
- You need to inherit your model from
BulkTrackerModel
- Add
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()
robust_send
if you have multiple receivers for the same signal, and you want to make sure that all of them are executed, even if one of them raises an exception. You can add TrackingInfo(is_robust=True)
in your operation. You can read more about robust_send
in the official documentation.
As in:
MyModel.objects.filter(name='john').update(
name='jack',
tracking_info_=TrackingInfo(is_robust=True),
)
# 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_object in modified_objects:
if 'name' in modified_object.changed_values:
log(f"field 'name' has changed by {user or ''}")
notify_user()
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 create
delete()
All you need to do is 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:
instance
this is your model instance after it has been updated, or createdchanged_values
is a dict[str, Any] which contains the changed fields only in the case ofpost_update_signal
, in the case ofpost_create_signal
andpost_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'}, is_robust=True),
)
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.
create()
save() # update and create
delete()
To support, we rely on the amazing library django-model-utils
to track the model instances:
- Do the above
- You need to inherit your model from
BulkTrackerModel
- Add
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()
robust_send
if you have multiple receivers for the same signal, and you want to make sure that all of them are executed, even if one of them raises an exception. You can add TrackingInfo(is_robust=True)
in your operation. You can read more about robust_send
in the official documentation.
As in:
MyModel.objects.filter(name='john').update(
name='jack',
tracking_info_=TrackingInfo(is_robust=True),
)
# 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_object in modified_objects:
if 'name' in modified_object.changed_values:
log(f"field 'name' has changed by {user or ''}")
notify_user()
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 create
delete()
All you need to do is 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:
instance
this is your model instance after it has been updated, or createdchanged_values
is a dict[str, Any] which contains the changed fields only in the case ofpost_update_signal
, in the case ofpost_create_signal
andpost_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'}, is_robust=True),
)
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.
create()
save() # update and create
delete()
To support, we rely on the amazing library django-model-utils
to track the model instances:
- Do the above
- You need to inherit your model from
BulkTrackerModel
- Add
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()
robust_send
if you have multiple receivers for the same signal, and you want to make sure that all of them are executed, even if one of them raises an exception. You can add TrackingInfo(is_robust=True)
in your operation. You can read more about robust_send
in the official documentation.
As in:
MyModel.objects.filter(name='john').update(
name='jack',
tracking_info_=TrackingInfo(is_robust=True),
)
# 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_object in modified_objects:
if 'name' in modified_object.changed_values:
log(f"field 'name' has changed by {user or ''}")
notify_user()
If you have great ideas for django-bulk-tracker, or if you like to improve something, feel free to fork this repository and/or create a pull request. I'm open for suggestions. If you like to discuss something with me (about django-bulk-tracker), please open an issue.