Source code for thorn.decorators

"""

    thorn.webhook.decorators
    ========================

    Webhook decorators.

"""
from __future__ import absolute_import, unicode_literals

from itertools import chain

from six import iteritems as items, itervalues as values
from six.moves import UserDict


[docs]class webhook_model(UserDict): """Decorates models to send webhooks based changes to that model. :keyword on_create: Event to dispatch whenever an instance of this model is created (``post_save``). :keyword on_change: Event to dispatch whenever an instance of this model is changed (``post_save``). :keyword on_delete: Event to dispatch whenever an instance of this model is deleted (``post_delete``). :keyword on_$event: Additional user defined events., :keyword sender_field: Default field used as a sender for events, e.g. ``"account.user"``, will use ``instance.account.user``. Individual events can override the sender field user. :keyword reverse: A :class:`thorn.reverse.model_reverser` instance (or any callable taking an model instance as argument), that describes how to get the URL for an instance of this model. Individual events can override the reverser used. **Examples** Simple article model, where the URL reference is retrieved by ``reverse('article-detail', kwargs={'uuid': article.uuid})``: .. code-block:: python @webhook_model( on_create=ModelEvent('article.created'), on_change=ModelEvent('article.changed'), on_delete=ModelEvent('article.removed'), on_deactivate=ModelEvent( 'article.deactivate', deactivated__eq=True, ) reverse=model_reverser('article-detail', uuid='uuid'), ) class Article(models.Model): uuid = models.UUIDField() The URL may not actually exist after deletion, so maybe we want to point the reference to something else in that special case, like a category that can be reversed by doing ``reverse('category-detail', args=[article.category.name])``. We can do that by having the ``on_delete`` event override the reverser used for that event only: .. code-block:: python @webhook_model( on_create=ModelEvent('article.created'), on_change=ModelEvent('article.changed'), on_delete=ModelEvent( 'article.removed', reverse=model_reverser('category-detail', 'category.name'), ), on_hipri_delete=ModelEvent( 'article.internal_delete', priority__gte=30.0, ).dispatches_on_delete(), reverse=model_reverser('article-detail', uuid='uuid'), ) class Article(model.Model): uuid = models.UUIDField() category = models.ForeignKey('category') """ def __init__(self, on_create=None, on_change=None, on_delete=None, reverse=None, sender_field=None, **kwargs): self.reverse = reverse self.sender_field = sender_field self.events = {} self.update_events( {k: v for k, v in items(kwargs) if k.startswith('on_')}, on_create=on_create and on_create.dispatches_on_create(), on_change=on_change and on_change.dispatches_on_change(), on_delete=on_delete and on_delete.dispatches_on_delete(), )
[docs] def update_events(self, events, **kwargs): self.events.update(self.connect_events(events, **kwargs))
[docs] def connect_events(self, events, **kwargs): return { k: self.contribute_to_event(v) for k, v in chain(items(events), items(kwargs)) }
[docs] def contribute_to_event(self, event): if event: if event.reverse is None: event.reverse = self.reverse event.sender_field = self.sender_field return event
def __call__(self, model): [event.connect_model(model) for event in values(self.events) if event] model.webhook_events = self return model @property def data(self): # for UserDict return self.events