GitHub - rsinger86/django-lifecycle: Declarative model lifecycle hooks, an alternative to Signals. (original) (raw)

Django Lifecycle Hooks

Package version Python versions Python versions PyPI - Django Version

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:

In short, you can write model code like this:

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:

# 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

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.