Package: django-model-subscription
django-model-subscriptionTable of contentsMotivationInstallationUsageDecoratorsSetup Subscribers using AppConfig.readySetup Subscribers with auto discoveryCreditsResourcesMotivationUsing Observer Pattern notify subscribers about changes to a django model.Decouple Business logic from Models.saveSupport for bulk actions (Not available using django signals.)Use noop subscribers whensettings.SUBSCRIPTION_RUN_EXTERNALisFalsewhich prevents having to mock subscribers that call external services in testing, 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$pipinstalldjango-model-subscriptionAddmodel_subscriptionto your INSTALLED_APPSINSTALLED_APPS=[...,'model_subscription',...]UsageUsing theSubscriptionModelMixinandSubscriptionQuerySetfrommodel_subscription.mixinimportSubscriptionModelMixinfrommodel_subscription.modelimportSubscriptionQuerySetclassTestModel(SubscriptionModelMixin,models.Model):name=models.CharField(max_length=255)objects=SubscriptionQuerySet.as_manager()Subclassing theSubscriptionModelbase class.frommodel_subscription.modelimportSubscriptionModelclassTestModel(SubscriptionModel):name=models.CharField(max_length=255)Creating subscribers.UsingOperationTypeimportloggingfrommodel_subscription.decoratorsimportsubscribefrommodel_subscription.constantsimportOperationTypelog=logging.getLogger(__name__)@subscribe(OperationType.CREATE,TestModel)defhandle_create(instance):log.debug('Created{}'.format(instance.name))Usingcreate_subscriptiondirectly (succinct version).importloggingfrommodel_subscription.decoratorsimportcreate_subscriptionlog=logging.getLogger(__name__)@create_subscription(TestModel)defhandle_create(instance):log.debug('Created{}'.format(instance.name))Decoratorssubscribe: Explicit (Requires a valid OperationType).(Create, Update, Delete) operations.create_subscription: Subscribes to create operation i.e a new instance.@create_subscription(TestModel)defhandle_create(instance):log.debug('1. Created{}'.format(instance.name))update_subscription: Subscribes to updates also includes (changed_data).@update_subscription(TestModel)defhandle_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)defhandle_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)defhandle_bulk_create(instances):forinstanceininstances:log.debug('Bulk Created{}'.format(instance.name))bulk_update_subscription: Subscribe to bulk update operations.@bulk_update_subscription(TestModel)defhandle_bulk_update(instances):forinstanceininstances:log.debug('Updated{}'.format(instance.name))bulk_delete_subscription: Subscribe to bulk delete operations.@bulk_delete_subscription(TestModel)defhandle_bulk_delete(instances):forinstanceininstances:log.debug('Deleted{}'.format(instance.name))Setup Subscribers using AppConfig.ready(Recomended).Update youapps.pyfromdjango.appsimportAppConfigclassMyAppConfig(AppConfig):name='myapp'defready(self):frommyappimportsubscriptionsSetup Subscribers using auto discovery.By default thesettings.SUBSCRIPTION_AUTO_DISCOVERis set toFalse.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 yoursettings.pyaddSUBSCRIPTION_AUTO_DISCOVER=TrueSetting up theSUBSCRIPTION_MODULENOTE: This is only required whenSUBSCRIPTION_AUTO_DISCOVER = TrueSUBSCRIPTION_MODULE='subscription'Creditsdjango-lifecycleIf you feel generous and want to show some extra appreciation:Resourceshttps://python-3-patterns-idioms-test.readthedocs.io/en/latest/Observer.htmlhttps://refactoring.guru/design-patterns/observerhttps://hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838cTODO'sSupporting 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.