Tutorial

This tutorial will show you how to work with django-model-event-actions package and gives you an idea of why using this package over default Django Signals will result in lower codes and a cleaner design and show you the extra functionalities that are not included in the Signals.

Please note that request_started_ and request_finished_ are not supported since they are not a part of django models and we encourage you using Signals for that.

Comparison with Signals

Imagine we have a Customer model and we want to send an email when the customer is created.

# models.py

class Customer(models.Model):
    email = models.EmailField()

The signals way will be:

# email.py

@receiver(post_init, sender=Customer)
def send_creation_email(sender, instance, created, **kwargs):
    if created:
        # sending email logic

The django_model_events will be:

# models.py

class Customer(models.Model):
    email = models.EmailField()

@PreCreateEvent()
def send_creation_email(self):
    # sending email logic

This gives you a idea about how to use this package but we will cover the use cases where this package will come so handy and useful as we go through the document.

Signals Replacement

Event Decorator

Signal

Event Arguments

PreCreateEvent

pre_save (needs explicit check)

[‘field’, ‘new’]

PostCreateEvent

post_save (needs explict check)

[‘field’, ‘new’]

PreSaveEvent

pre_save

[‘fields’, ‘field’, ‘prev’, ‘new’]

PostSaveEvent

post_save

[‘fields’, ‘field’, ‘prev’, ‘new’]

PreDeleteEvent

pre_delete

_

PostDeleteEvent

post_delete

_

FKChangeEvent

_

[‘field’]

M2MChangeEvent

m2m_changed

[‘field’]

Arguments

You can pass some attributes to the Event decorator to determine when the Event should trigger.

Events Arguments

Argument

Trigger Condition

fields

Triggers if the listed ‘fields’ is a subset of the changed fields

field

Triggers if the field is changed

prev

Triggers if the previous value of the field was equal to ‘prev’

field

Triggers if the new value of the field is equal to ‘prev’

You can use a combination of the arguments like this:

  • Event()

  • Event(‘fields’=[‘field_name_1’, ‘field_name_2’, …])

  • Event(‘field’=’field_name’)

  • Event(‘field’=’field_name’, prev=’field_prev_value’)

  • Event(‘field’=’field_name’, new=’field_new_value’)

  • Event(‘field’=’field_name’, prev=’field_prev_value’, new=’field_new_value’)

How to use

Subclass EventActionModel rather than Django’s models.Model for every model that you want to use event actions for. The decorator’s class shows the event’s name and condition to be triggered and the wrapped function is the callback function. To make a function an action, add an Event decorator for that function in the model.

from event_actions.models import EventActionModel

class Comment(EventActionModel):
    message = models.CharField()

    @PostSaveEvent(field='message')
    def log_message_changed(self):
        # logging logic

Decorators

PreCreateEvent

PreCreateEvent will be triggered before the model’s save method is called when a new object is created.

PreCreateEvent accepts field, new arguments.

PostCreateEvent

PostCreateEvent will be triggered after the model’s save method is called when a new object is created.

PreCreateEvent accepts field, new arguments.

PreSaveEvent

PreSaveEvent will be triggered before the model’s save method, this event will not be triggered when the model is created.

PreSaveEvent accepts fields, field, prev and new arguments.

PostSaveEvent

PostSaveEvent will be triggered after the model’s save method, this event will not be triggered when the model is created.

PostSaveEvent accepts fields, field, prev and new arguments.

PreDeleteEvent

PreDeleteEvent will be triggered before the model’s delete method.

PreDeleteEvent does not accept any argument.

PostDeleteEvent

PostDeleteEvent will be triggered after the model’s delete method.

PostDeleteEvent does not accept any argument.

FKChangeEvent

FKChangeEvent will be triggered if any value of the pointed instance by the FK field is changed. The foreign key’s model should subclass EventActionModel also.

PostDeleteEvent should have the field arguments.

class User(EventActionModel):
    # User fields

# This won't be tracked because it's not subclassing EventActionModel
class Post(models.Model):
    # Post fields

class Comment(EventActionModel):
    author = models.ForeignKey(User)
    post = models.ForeignKey(Post)

    @FKChangeEvent(field='author') # this will work
    def author_changed(self):
        # logic

    @FKChangeEvent(field='post') # this won't work
    def post_changed(self):
        # logic

Please note that if you want to track the change of the foreign key field (when the author is changed in the above example), use PreSaveEvent or PostSaveEvent.

Models

EventActionModel

This class uses the EventActionModelMixin and ModelDiffMixin mixin and subclasses Django’s models.Model.