django-bulk-tracker
===============
[![Build Status](https://github.com/hassaanalansary/django-bulk-tracker/actions/workflows/tests.yml/badge.svg)](https://github.com/hassaanalansary/django-bulk-tracker/actions)
[![Build Status](https://github.com/hassaanalansary/django-bulk-tracker/workflows/Test/badge.svg)](https://github.com/hassaanalansary/django-bulk-tracker/actions)
[![codecov](https://codecov.io/gh/hassaanalansary/django-bulk-tracker/branch/master/graph/badge.svg)](https://codecov.io/gh/hassaanalansary/django-bulk-tracker)
[![Supported Python versions](https://img.shields.io/pypi/pyversions/django-bulk-tracker.svg)](https://pypi.python.org/pypi/django-bulk-tracker)
[![Supported Django versions](https://img.shields.io/pypi/djversions/django-bulk-tracker.svg)](https://pypi.python.org/pypi/django-bulk-tracker)
Documentation
=============
https://django-bulk-tracker.readthedocs.io/en/latest/
Run tests
==========
```shell
pip install -r requirements_dev.txt
pytest
```
Here's the provided text translated from reStructuredText (.rst) format to Markdown (.md):
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 create`
- `delete()`
All you need to do is define your Model and inherit from:
```python
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:
```python
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`:
```python
@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 a dict[str, Any] which contains the changed fields only in the case of `post_update_signal`, in the 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:
```python
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.
For single operations as well
-----------------------------
- `create()`
- `save() # update and create`
- `delete()`
To support, we rely on the amazing library `django-model-utils` to track the model instances:
1. Do the above
2. You need to inherit your model from `BulkTrackerModel`
3. Add `tracker = FieldTracker()` to your model
As in:
```python
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
----------
`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](https://docs.djangoproject.com/en/5.0/topics/signals/#sending-signals).
As in:
```python
MyModel.objects.filter(name='john').update(
name='jack',
tracking_info_=TrackingInfo(is_robust=True),
)
```
Complete Example
================
```python
# 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()
```
```python
# managers.py
from bulk_tracker.managers import BulkTrackerQuerySet # optional
class MyModelQuerySet(BulkTrackerQuerySet):
pass
class MyModelManager(BulkTrackerManager.from_queryset(MyModelQuerySet)): # optional
pass
```
```python
# 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()
```
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 create`
- `delete()`
All you need to do is define your Model and inherit from:
```python
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:
```python
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`:
```python
@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 a dict[str, Any] which contains the changed fields only in the case of `post_update_signal`, in the 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:
```python
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.
For single operations as well
-----------------------------
- `create()`
- `save() # update and create`
- `delete()`
To support, we rely on the amazing library `django-model-utils` to track the model instances:
1. Do the above
2. You need to inherit your model from `BulkTrackerModel`
3. Add `tracker = FieldTracker()` to your model
As in:
```python
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
----------
`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](https://docs.djangoproject.com/en/5.0/topics/signals/#sending-signals).
As in:
```python
MyModel.objects.filter(name='john').update(
name='jack',
tracking_info_=TrackingInfo(is_robust=True),
)
```
Complete Example
================
```python
# 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()
```
```python
# managers.py
from bulk_tracker.managers import BulkTrackerQuerySet # optional
class MyModelQuerySet(BulkTrackerQuerySet):
pass
class MyModelManager(BulkTrackerManager.from_queryset(MyModelQuerySet)): # optional
pass
```
```python
# 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()
```
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 create`
- `delete()`
All you need to do is define your Model and inherit from:
```python
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:
```python
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`:
```python
@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 a dict[str, Any] which contains the changed fields only in the case of `post_update_signal`, in the 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:
```python
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.
For single operations as well
-----------------------------
- `create()`
- `save() # update and create`
- `delete()`
To support, we rely on the amazing library `django-model-utils` to track the model instances:
1. Do the above
2. You need to inherit your model from `BulkTrackerModel`
3. Add `tracker = FieldTracker()` to your model
As in:
```python
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
----------
`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](https://docs.djangoproject.com/en/5.0/topics/signals/#sending-signals).
As in:
```python
MyModel.objects.filter(name='john').update(
name='jack',
tracking_info_=TrackingInfo(is_robust=True),
)
```
Complete Example
================
```python
# 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()
```
```python
# managers.py
from bulk_tracker.managers import BulkTrackerQuerySet # optional
class MyModelQuerySet(BulkTrackerQuerySet):
pass
class MyModelManager(BulkTrackerManager.from_queryset(MyModelQuerySet)): # optional
pass
```
```python
# 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()
```
Contribute
==========
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.
#### GitHub repository: https://github.com/hassaanalansary/django-bulk-tracker
#### issue tracker: https://github.com/hassaanalansary/django-bulk-tracker/issues
Raw data
{
"_id": null,
"home_page": "https://github.com/hassaanalansary/django-bulk-tracker",
"name": "django-bulk-tracker",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.9",
"maintainer_email": null,
"keywords": "django, django-bulk-tracker, bulk-update, bulk-create, signals, django-signals",
"author": "Hassaan Alansary",
"author_email": "hassaanalansary@yahoo.com",
"download_url": "https://files.pythonhosted.org/packages/af/4d/bdf62d584f387731a329e3bd8230ab9f446f6facf9190506277e4ef8104f/django_bulk_tracker-0.2.1.tar.gz",
"platform": null,
"description": "django-bulk-tracker\n===============\n\n[![Build Status](https://github.com/hassaanalansary/django-bulk-tracker/actions/workflows/tests.yml/badge.svg)](https://github.com/hassaanalansary/django-bulk-tracker/actions)\n[![Build Status](https://github.com/hassaanalansary/django-bulk-tracker/workflows/Test/badge.svg)](https://github.com/hassaanalansary/django-bulk-tracker/actions)\n[![codecov](https://codecov.io/gh/hassaanalansary/django-bulk-tracker/branch/master/graph/badge.svg)](https://codecov.io/gh/hassaanalansary/django-bulk-tracker)\n[![Supported Python versions](https://img.shields.io/pypi/pyversions/django-bulk-tracker.svg)](https://pypi.python.org/pypi/django-bulk-tracker)\n[![Supported Django versions](https://img.shields.io/pypi/djversions/django-bulk-tracker.svg)](https://pypi.python.org/pypi/django-bulk-tracker)\n\nDocumentation\n=============\nhttps://django-bulk-tracker.readthedocs.io/en/latest/\n\nRun tests\n==========\n```shell\npip install -r requirements_dev.txt\npytest\n```\nHere's the provided text translated from reStructuredText (.rst) format to Markdown (.md):\n\nUsage\n=====\n\n`django-bulk-tracker` will emit a signal whenever you update, create, or delete a record in the database.\n\n`django-bulk-tracker` supports bulk operations:\n\n- `queryset.update()`\n- `queryset.bulk_update()`\n- `queryset.bulk_create()`\n- `queryset.delete()`\n\nand single operations:\n\n- `create()`\n- `save() # update and create`\n- `delete()`\n\nAll you need to do is define your Model and inherit from:\n\n```python\nfrom bulk_tracker.models import BulkTrackerModel\n\nclass Author(BulkTrackerModel):\n first_name = models.CharField(max_length=200)\n last_name = models.CharField(max_length=200)\n```\n\nOR if you have a custom queryset inherit from or Don't want to support single-operation:\n\n```python\nfrom bulk_tracker.managers import BulkTrackerQuerySet\n\nclass MyModelQuerySet(BulkTrackerQuerySet):\n def do_something_custom(self):\n pass\n```\n\nNow you can listen to the signals `post_update_signal`, `post_create_signal`, `post_delete_signal`:\n\n```python\n@receiver(post_update_signal, sender=MyModel)\ndef i_am_a_receiver_function(\n sender,\n objects: list[ModifiedObject[MyModel]],\n tracking_info_: TrackingInfo | None = None,\n **kwargs,\n):\n do_stuff()\n```\n\n**Hint:** All signals have the same signature for consistency and also in case you want to assign one function to listen to multiple signals.\n\n`ModifiedObject` is a very simple object, it contains 2 attributes:\n1. `instance` this is your model instance after it has been updated, or created\n2. `changed_values` is a dict[str, Any] which contains the changed fields only in the case of `post_update_signal`, in the case of `post_create_signal` and `post_delete_signal`, `changed_values` will be an empty dict `{}`.\n\n**Optionally** you can pass `tracking_info_` to your functions, as in:\n\n```python\nfrom bulk_tracker.helper_objects import TrackingInfo\n\ndef a_function_that_updates_records():\n user = self.request.user\n MyModel.objects.filter(name='john').update(\n name='jack',\n tracking_info_=TrackingInfo(user=user, comment=\"Updated from a function\", kwargs={'app-build':'1.1.8'}, is_robust=True),\n )\n```\n\n**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.\n\nFor single operations as well\n-----------------------------\n\n- `create()`\n- `save() # update and create`\n- `delete()`\n\nTo support, we rely on the amazing library `django-model-utils` to track the model instances:\n\n1. Do the above\n2. You need to inherit your model from `BulkTrackerModel`\n3. Add `tracker = FieldTracker()` to your model\n\nAs in:\n\n```python\nfrom bulk_tracker.models import BulkTrackerModel\nfrom model_utils import FieldTracker\n\nclass MyModel(BulkTrackerModel):\n objects = MyModelQuerySet.as_manager() # MyModelManager() if you have\n tracker = FieldTracker()\n```\n\nRobust Send\n----------\n\n`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](https://docs.djangoproject.com/en/5.0/topics/signals/#sending-signals).\n\nAs in:\n\n```python\nMyModel.objects.filter(name='john').update(\n name='jack',\n tracking_info_=TrackingInfo(is_robust=True),\n)\n```\n\nComplete Example\n================\n\n```python\n# models.py\nfrom bulk_tracker.models import BulkTrackerModel\nfrom model_utils import FieldTracker\n\nfrom myapp.managers import MyModelManager\n\nclass MyModel(BulkTrackerModel):\n first_field = models.CharField()\n second_field = models.CharField()\n\n objects = MyModelManager()\n tracker = FieldTracker()\n```\n\n```python\n# managers.py\nfrom bulk_tracker.managers import BulkTrackerQuerySet # optional\n\nclass MyModelQuerySet(BulkTrackerQuerySet):\n pass\n\nclass MyModelManager(BulkTrackerManager.from_queryset(MyModelQuerySet)): # optional\n pass\n```\n\n```python\n# signal_handlers.py\nfrom bulk_tracker.signals import post_update_signal\nfrom bulk_tracker.helper_objects import ModifiedObject, TrackingInfo\n\n@receiver(post_update_signal, sender=MyModel)\ndef i_am_a_receiver_function(\n sender,\n objects: list[ModifiedObject[MyModel]],\n tracking_info_: TrackingInfo | None = None,\n **kwargs,\n):\n user = tracking_info_.user if tracking_info_ else None\n for modified_object in modified_objects:\n if 'name' in modified_object.changed_values:\n log(f\"field 'name' has changed by {user or ''}\")\n notify_user()\n```\n\n\n\nUsage\n=====\n\n`django-bulk-tracker` will emit a signal whenever you update, create, or delete a record in the database.\n\n`django-bulk-tracker` supports bulk operations:\n\n- `queryset.update()`\n- `queryset.bulk_update()`\n- `queryset.bulk_create()`\n- `queryset.delete()`\n\nand single operations:\n\n- `create()`\n- `save() # update and create`\n- `delete()`\n\nAll you need to do is define your Model and inherit from:\n\n```python\nfrom bulk_tracker.models import BulkTrackerModel\n\nclass Author(BulkTrackerModel):\n first_name = models.CharField(max_length=200)\n last_name = models.CharField(max_length=200)\n```\n\nOR if you have a custom queryset inherit from or Don't want to support single-operation:\n\n```python\nfrom bulk_tracker.managers import BulkTrackerQuerySet\n\nclass MyModelQuerySet(BulkTrackerQuerySet):\n def do_something_custom(self):\n pass\n```\n\nNow you can listen to the signals `post_update_signal`, `post_create_signal`, `post_delete_signal`:\n\n```python\n@receiver(post_update_signal, sender=MyModel)\ndef i_am_a_receiver_function(\n sender,\n objects: list[ModifiedObject[MyModel]],\n tracking_info_: TrackingInfo | None = None,\n **kwargs,\n):\n do_stuff()\n```\n\n**Hint:** All signals have the same signature for consistency and also in case you want to assign one function to listen to multiple signals.\n\n`ModifiedObject` is a very simple object, it contains 2 attributes:\n1. `instance` this is your model instance after it has been updated, or created\n2. `changed_values` is a dict[str, Any] which contains the changed fields only in the case of `post_update_signal`, in the case of `post_create_signal` and `post_delete_signal`, `changed_values` will be an empty dict `{}`.\n\n**Optionally** you can pass `tracking_info_` to your functions, as in:\n\n```python\nfrom bulk_tracker.helper_objects import TrackingInfo\n\ndef a_function_that_updates_records():\n user = self.request.user\n MyModel.objects.filter(name='john').update(\n name='jack',\n tracking_info_=TrackingInfo(user=user, comment=\"Updated from a function\", kwargs={'app-build':'1.1.8'}, is_robust=True),\n )\n```\n\n**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.\n\nFor single operations as well\n-----------------------------\n\n- `create()`\n- `save() # update and create`\n- `delete()`\n\nTo support, we rely on the amazing library `django-model-utils` to track the model instances:\n\n1. Do the above\n2. You need to inherit your model from `BulkTrackerModel`\n3. Add `tracker = FieldTracker()` to your model\n\nAs in:\n\n```python\nfrom bulk_tracker.models import BulkTrackerModel\nfrom model_utils import FieldTracker\n\nclass MyModel(BulkTrackerModel):\n objects = MyModelQuerySet.as_manager() # MyModelManager() if you have\n tracker = FieldTracker()\n```\n\nRobust Send\n----------\n\n`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](https://docs.djangoproject.com/en/5.0/topics/signals/#sending-signals).\n\nAs in:\n\n```python\nMyModel.objects.filter(name='john').update(\n name='jack',\n tracking_info_=TrackingInfo(is_robust=True),\n)\n```\n\nComplete Example\n================\n\n```python\n# models.py\nfrom bulk_tracker.models import BulkTrackerModel\nfrom model_utils import FieldTracker\n\nfrom myapp.managers import MyModelManager\n\nclass MyModel(BulkTrackerModel):\n first_field = models.CharField()\n second_field = models.CharField()\n\n objects = MyModelManager()\n tracker = FieldTracker()\n```\n\n```python\n# managers.py\nfrom bulk_tracker.managers import BulkTrackerQuerySet # optional\n\nclass MyModelQuerySet(BulkTrackerQuerySet):\n pass\n\nclass MyModelManager(BulkTrackerManager.from_queryset(MyModelQuerySet)): # optional\n pass\n```\n\n```python\n# signal_handlers.py\nfrom bulk_tracker.signals import post_update_signal\nfrom bulk_tracker.helper_objects import ModifiedObject, TrackingInfo\n\n@receiver(post_update_signal, sender=MyModel)\ndef i_am_a_receiver_function(\n sender,\n objects: list[ModifiedObject[MyModel]],\n tracking_info_: TrackingInfo | None = None,\n **kwargs,\n):\n user = tracking_info_.user if tracking_info_ else None\n for modified_object in modified_objects:\n if 'name' in modified_object.changed_values:\n log(f\"field 'name' has changed by {user or ''}\")\n notify_user()\n```\n\n\n\nUsage\n=====\n\n`django-bulk-tracker` will emit a signal whenever you update, create, or delete a record in the database.\n\n`django-bulk-tracker` supports bulk operations:\n\n- `queryset.update()`\n- `queryset.bulk_update()`\n- `queryset.bulk_create()`\n- `queryset.delete()`\n\nand single operations:\n\n- `create()`\n- `save() # update and create`\n- `delete()`\n\nAll you need to do is define your Model and inherit from:\n\n```python\nfrom bulk_tracker.models import BulkTrackerModel\n\nclass Author(BulkTrackerModel):\n first_name = models.CharField(max_length=200)\n last_name = models.CharField(max_length=200)\n```\n\nOR if you have a custom queryset inherit from or Don't want to support single-operation:\n\n```python\nfrom bulk_tracker.managers import BulkTrackerQuerySet\n\nclass MyModelQuerySet(BulkTrackerQuerySet):\n def do_something_custom(self):\n pass\n```\n\nNow you can listen to the signals `post_update_signal`, `post_create_signal`, `post_delete_signal`:\n\n```python\n@receiver(post_update_signal, sender=MyModel)\ndef i_am_a_receiver_function(\n sender,\n objects: list[ModifiedObject[MyModel]],\n tracking_info_: TrackingInfo | None = None,\n **kwargs,\n):\n do_stuff()\n```\n\n**Hint:** All signals have the same signature for consistency and also in case you want to assign one function to listen to multiple signals.\n\n`ModifiedObject` is a very simple object, it contains 2 attributes:\n1. `instance` this is your model instance after it has been updated, or created\n2. `changed_values` is a dict[str, Any] which contains the changed fields only in the case of `post_update_signal`, in the case of `post_create_signal` and `post_delete_signal`, `changed_values` will be an empty dict `{}`.\n\n**Optionally** you can pass `tracking_info_` to your functions, as in:\n\n```python\nfrom bulk_tracker.helper_objects import TrackingInfo\n\ndef a_function_that_updates_records():\n user = self.request.user\n MyModel.objects.filter(name='john').update(\n name='jack',\n tracking_info_=TrackingInfo(user=user, comment=\"Updated from a function\", kwargs={'app-build':'1.1.8'}, is_robust=True),\n )\n```\n\n**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.\n\nFor single operations as well\n-----------------------------\n\n- `create()`\n- `save() # update and create`\n- `delete()`\n\nTo support, we rely on the amazing library `django-model-utils` to track the model instances:\n\n1. Do the above\n2. You need to inherit your model from `BulkTrackerModel`\n3. Add `tracker = FieldTracker()` to your model\n\nAs in:\n\n```python\nfrom bulk_tracker.models import BulkTrackerModel\nfrom model_utils import FieldTracker\n\nclass MyModel(BulkTrackerModel):\n objects = MyModelQuerySet.as_manager() # MyModelManager() if you have\n tracker = FieldTracker()\n```\n\nRobust Send\n----------\n\n`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](https://docs.djangoproject.com/en/5.0/topics/signals/#sending-signals).\n\nAs in:\n\n```python\nMyModel.objects.filter(name='john').update(\n name='jack',\n tracking_info_=TrackingInfo(is_robust=True),\n)\n```\n\nComplete Example\n================\n\n```python\n# models.py\nfrom bulk_tracker.models import BulkTrackerModel\nfrom model_utils import FieldTracker\n\nfrom myapp.managers import MyModelManager\n\nclass MyModel(BulkTrackerModel):\n first_field = models.CharField()\n second_field = models.CharField()\n\n objects = MyModelManager()\n tracker = FieldTracker()\n```\n\n```python\n# managers.py\nfrom bulk_tracker.managers import BulkTrackerQuerySet # optional\n\nclass MyModelQuerySet(BulkTrackerQuerySet):\n pass\n\nclass MyModelManager(BulkTrackerManager.from_queryset(MyModelQuerySet)): # optional\n pass\n```\n\n```python\n# signal_handlers.py\nfrom bulk_tracker.signals import post_update_signal\nfrom bulk_tracker.helper_objects import ModifiedObject, TrackingInfo\n\n@receiver(post_update_signal, sender=MyModel)\ndef i_am_a_receiver_function(\n sender,\n objects: list[ModifiedObject[MyModel]],\n tracking_info_: TrackingInfo | None = None,\n **kwargs,\n):\n user = tracking_info_.user if tracking_info_ else None\n for modified_object in modified_objects:\n if 'name' in modified_object.changed_values:\n log(f\"field 'name' has changed by {user or ''}\")\n notify_user()\n```\n\n\n\nContribute\n==========\n\nIf you have great ideas for django-bulk-tracker, or if you like to improve something,\nfeel free to fork this repository and/or create a pull request.\nI'm open for suggestions.\nIf you like to discuss something with me (about django-bulk-tracker), please open an issue.\n\n\n#### GitHub repository: https://github.com/hassaanalansary/django-bulk-tracker\n#### issue tracker: https://github.com/hassaanalansary/django-bulk-tracker/issues\n",
"bugtrack_url": null,
"license": "Mozilla Public License 2.0",
"summary": "A Django app that allows you to have more control over bulk operations.",
"version": "0.2.1",
"project_urls": {
"Homepage": "https://github.com/hassaanalansary/django-bulk-tracker"
},
"split_keywords": [
"django",
" django-bulk-tracker",
" bulk-update",
" bulk-create",
" signals",
" django-signals"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "2125629de017d8a9976d87db4a4c8954e7a86ed8da8472d33f6281f6c55f9d97",
"md5": "d9321f6598b23fbd609340d79d3f2319",
"sha256": "c88b7e27ab3b402b1d8b8a36a06396b3b2d8fa0a82fac90287e1c05ec714f0d7"
},
"downloads": -1,
"filename": "django_bulk_tracker-0.2.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "d9321f6598b23fbd609340d79d3f2319",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": ">=3.9",
"size": 16633,
"upload_time": "2024-07-24T23:50:43",
"upload_time_iso_8601": "2024-07-24T23:50:43.397946Z",
"url": "https://files.pythonhosted.org/packages/21/25/629de017d8a9976d87db4a4c8954e7a86ed8da8472d33f6281f6c55f9d97/django_bulk_tracker-0.2.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "af4dbdf62d584f387731a329e3bd8230ab9f446f6facf9190506277e4ef8104f",
"md5": "55123af444dbeae355d0c4ec36d1012a",
"sha256": "c6d088fad3675bf05dc137448a5f3c8a8d99dd25ad740793dca3fdcf68e3af55"
},
"downloads": -1,
"filename": "django_bulk_tracker-0.2.1.tar.gz",
"has_sig": false,
"md5_digest": "55123af444dbeae355d0c4ec36d1012a",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.9",
"size": 19754,
"upload_time": "2024-07-24T23:50:45",
"upload_time_iso_8601": "2024-07-24T23:50:45.333883Z",
"url": "https://files.pythonhosted.org/packages/af/4d/bdf62d584f387731a329e3bd8230ab9f446f6facf9190506277e4ef8104f/django_bulk_tracker-0.2.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-07-24 23:50:45",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "hassaanalansary",
"github_project": "django-bulk-tracker",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"requirements": [
{
"name": "Django",
"specs": [
[
"==",
"3.2.0"
]
]
},
{
"name": "django-model-utils",
"specs": [
[
"==",
"4.0.0"
]
]
}
],
"tox": true,
"lcname": "django-bulk-tracker"
}