Django Lifecycle Hooks¶
This project provides a @hook
decorator as well as a base model and mixin to add lifecycle hooks to your Django models. Django's built-in approach to offering lifecycle hooks is Signals. However, my team often finds that Signals introduce unnecessary indirection and are at odds with Django's "fat models" approach.
In short, you can write model code like this:
from django_lifecycle import LifecycleModel, hook, BEFORE_UPDATE, AFTER_UPDATE
class Article(LifecycleModel):
contents = models.TextField()
updated_at = models.DateTimeField(null=True)
status = models.ChoiceField(choices=['draft', 'published'])
editor = models.ForeignKey(AuthUser)
@hook(
BEFORE_UPDATE,
condition=WhenFieldHasChanged('contents', has_changed=True),
)
def on_content_change(self):
self.updated_at = timezone.now()
@hook(
AFTER_UPDATE,
condition=(
WhenFieldValueWas("status", value="draft")
& WhenFieldValueIs("status", value="published")
)
)
def on_publish(self):
send_email(self.editor.email, "An article has published!")
Instead of overriding save
and __init__
in a clunky way that hurts readability:
# same class and field declarations as above ...
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._orig_contents = self.contents
self._orig_status = self.status
def save(self, *args, **kwargs):
if self.pk is not None and self.contents != self._orig_contents):
self.updated_at = timezone.now()
super().save(*args, **kwargs)
if self.status != self._orig_status:
send_email(self.editor.email, "An article has published!")
Requirements¶
- Python (3.7+)
- Django (2.2+)
Installation¶
Getting Started¶
Either extend the provided abstract base model class:
from django_lifecycle import LifecycleModel, hook
class YourModel(LifecycleModel):
name = models.CharField(max_length=50)
Or add the mixin to your Django model definition:
from django.db import models
from django_lifecycle import LifecycleModelMixin, hook
class YourModel(LifecycleModelMixin, models.Model):
name = models.CharField(max_length=50)
(Optional) Add django_lifecycle_checks
to your INSTALLED_APPS
¶
This will raise an exception if you forget to add the mixin or extend the base model class.
Read on to see more examples of how to use lifecycle hooks.