django-model-subscription
Subscribe to django model changes include bulk create/update/delete.
Table of contents
Features
- Using Observer Pattern notify subscribers about changes to a django model.
- Decouple Business logic from Models.save
- Support for bulk actions (Not available using django signals.)
- Use noop subscribers when
settings.SUBSCRIPTION_DISABLE_SUBSCRIBERS
isTrue
which prevents having to mock subscribers that call external services in testing or local development environments. - Show changes to the instance after it has been updated i.e diff's the initial state and the current state.
Installation
$ pip install django-model-subscription
Add model_subscription
to your INSTALLED_APPS
INSTALLED_APPS = [
...,
'model_subscription',
...
]
Usage
Using the SubscriptionModelMixin
and SubscriptionQuerySet
from model_subscription.mixin import SubscriptionModelMixin
from model_subscription.model import SubscriptionQuerySet
class TestModel(SubscriptionModelMixin, models.Model):
name = models.CharField(max_length=255)
objects = SubscriptionQuerySet.as_manager()
Subclassing the SubscriptionModel
base class.
from model_subscription.model import SubscriptionModel
class TestModel(SubscriptionModel):
name = models.CharField(max_length=255)
Creating subscribers.
- Using
OperationType
import logging
from model_subscription.decorators import subscribe
from model_subscription.constants import OperationType
log = logging.getLogger(__name__)
@subscribe(OperationType.CREATE, TestModel)
def handle_create(instance):
log.debug('Created {}'.format(instance.name))
- Using
create_subscription
directly (succinct version).
import logging
from model_subscription.decorators import create_subscription
log = logging.getLogger(__name__)
@create_subscription(TestModel)
def handle_create(instance):
log.debug('Created {}'.format(instance.name))
Decorators
subscribe
: Explicit (Requires a valid OperationType).
(Create, Update, Delete) operations.
create_subscription
: Subscribes to create operation i.e a new instance.
@create_subscription(TestModel)
def handle_create(instance):
log.debug('1. Created {}'.format(instance.name))
update_subscription
: Subscribes to updates also includes (changed_data
).
@update_subscription(TestModel)
def handle_update(instance, changed_data):
log.debug('Updated {} {}'.format(instance.name, changed_data))
delete_subscription
: Subscribes to delete operation:
NOTE: The instance.pk is already set to None.
@delete_subscription(TestModel)
def handle_delete(instance):
log.debug('Deleted {}'.format(instance.name))
(Bulk Create, Bulk Update, Bulk Delete) operations.
bulk_create_subscription
: Subscribe to bulk create operations.
@bulk_create_subscription(TestModel)
def handle_bulk_create(instances):
for instance in instances:
log.debug('Bulk Created {}'.format(instance.name))
bulk_update_subscription
: Subscribe to bulk update operations.
@bulk_update_subscription(TestModel)
def handle_bulk_update(instances):
for instance in instances:
log.debug('Updated {}'.format(instance.name))
bulk_delete_subscription
: Subscribe to bulk delete operations.
@bulk_delete_subscription(TestModel)
def handle_bulk_delete(instances):
for instance in instances:
log.debug('Deleted {}'.format(instance.name))
Setup Subscribers using AppConfig.ready (Recomended)
.
Update you apps.py
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = 'myapp'
def ready(self):
from myapp import subscriptions
Setup Subscribers using auto discovery.
By default the settings.SUBSCRIPTION_AUTO_DISCOVER
is set to False
.
To use auto discovery this is not recommended as it would notify the subscribers wherever the model is used i.e IPython notebook, external scripts.
In your settings.py
add
SUBSCRIPTION_AUTO_DISCOVER = True
Setting up the SUBSCRIPTION_MODULE
NOTE: This is only required when
SUBSCRIPTION_AUTO_DISCOVER = True
SUBSCRIPTION_MODULE = 'subscription'
Credits
If you feel generous and want to show some extra appreciation:
Resources
- https://python-3-patterns-idioms-test.readthedocs.io/en/latest/Observer.html
- https://refactoring.guru/design-patterns/observer
- https://hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838c
TODO's
- Supporting field level subscriptions.
- Support class based subscribers which implements
__call__
- Extend to include custom OperationType.
- Add support for using a single class to manage multiple actions i.e MyClass.update, MyClass.create.