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.
Django Lifecycle Hooks supports:
- Python 3.7, 3.8, 3.9, 3.10, 3.11, and 3.12
- Django 2.2, 3.2, 4.0, 4.1, 4.2, and 5.0
In short, you can write model code like this:
fromdjango_lifecycleimportLifecycleModel, hook, BEFORE_UPDATE, AFTER_UPDATEclassArticle(LifecycleModel):
contents=models.TextField()
updated_at=models.DateTimeField(null=True)
status=models.ChoiceField(choices=['draft', 'published'])
editor=models.ForeignKey(AuthUser)
@hook(BEFORE_UPDATE, WhenFieldHasChanged("contents", has_changed=True))defon_content_change(self):
self.updated_at=timezone.now()
@hook(AFTER_UPDATE, condition=(WhenFieldValueWas("status", value="draft")&WhenFieldValueIs("status", value="published") ) )defon_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.contentsself._orig_status=self.statusdefsave(self, *args, **kwargs):
ifself.pkisnotNoneandself.contents!=self._orig_contents:
self.updated_at=timezone.now()
super().save(*args, **kwargs)
ifself.status!=self._orig_status:
send_email(self.editor.email, "An article has published!")Documentation: https://rsinger86.github.io/django-lifecycle
Source Code: https://github.com/rsinger86/django-lifecycle
See Changelog
Tests are found in a simplified Django project in the /tests folder. Install the project requirements and do ./manage.py test to run them.
See License.