Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow optional extra context to be passed through to lifecycle hooks #159

Open
SupImDos opened this issue May 29, 2024 · 0 comments
Open

Comments

@SupImDos
Copy link

SupImDos commented May 29, 2024

I frequently come across the case where it would be beneficial to be able to pass extra context through a model's .save() method to my lifecycle hooks, i.e.,

# It would be helpful if the following call to `.save()` could (optionally)
# pass `a` and `b` through to the lifecycle hooks if they accept arguments
# in their function signature (in some way).
my_model.save(a=123, b=456)

To give a more concrete example, if we take the code snippet in django-lifecycle's README.md, and say that we now have the new requirement of not sending the email if the editor themself publishes the article.

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, 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, **context):
        # Contrived example!
        # Only send an email if the actioning user is not the editor
        if context.get("actioning_user") != self.editor:
            send_email(self.editor.email, "An article has published!")

Publishing an article may then look something like:

me = User.objects.get(pk=...)
article = Article.objects.get(pk=...)
article.contents = "..."
article.status = "published"
article.save(actioning_user=me)

Obviously this is a bit of a contrived example, as we would probably just have an Article.publish() method that requires an actioning_user, but I do think in general there are cases where allowing extra context makes sense - e.g., when arbitrary updates are made to a record in a given context (such as the request / response cycle), and we want lifecycle hooks to behave differently depending on said context.

I imagine this functionality could be implemented in various (possibly even backwards-compatible) ways, such as:

  • Inspecting lifecycle hooks' function signatures to see if they accept **kwargs?
  • Inspecting lifecycle hooks' function signatures to see specifically what they expect?
  • Require expected context to be specified in @hook() in some way?
    • e.g., @hook(AFTER_UPDATE, condition=..., required_context=["a", "b"])

If a feature like this were to go any further, then there are probably also edge-cases to consider, such as:

  • What if the required context is not passed in?
    • Raise an exception?
    • Skip the hook?
    • Configurable behaviour?

The implementation details above haven't really been fleshed out, but in general I wanted to suggest and gauge the interest for allowing extra context in lifecycle hooks.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant