# django-lifecycle
**Repository Path**: mirrors_rsinger86/django-lifecycle
## Basic Information
- **Project Name**: django-lifecycle
- **Description**: Declarative model lifecycle hooks, an alternative to Signals.
- **Primary Language**: Unknown
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-01-06
- **Last Updated**: 2026-05-10
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Django Lifecycle Hooks
[](https://pypi.python.org/pypi/django-lifecycle)
[](https://img.shields.io/pypi/status/django-lifecycle.svg/)
[](https://pypi.org/project/django-lifecycle/)

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](https://docs.djangoproject.com/en/dev/topics/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.8, 3.9, 3.10, 3.11, 3.12, 3.13 and 3.14
* Django 4.2, 5.0, 5.2 and 6.0
In short, you can write model code like this:
```python
from django_lifecycle import LifecycleModel, hook, BEFORE_UPDATE, AFTER_UPDATE
from django_lifecycle.conditions import WhenFieldValueIs, WhenFieldValueWas, WhenFieldHasChanged
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, 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:
```python
# 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!")
```
---
**Documentation**: https://rsinger86.github.io/django-lifecycle
**Source Code**: https://github.com/rsinger86/django-lifecycle
---
# Changelog
See [Changelog](CHANGELOG.md)
# Testing
Tests are found in a simplified Django project in the `/tests` folder. Install the project requirements and do `./manage.py test` to run them.
# License
See [License](LICENSE.md).