viewflow/django-fsm

auto-save workflow state

Closed this issue · 8 comments

An earlier version (django-fsm 1.6.0) allowed the transition decorator to automatically save the workflow state to the db by setting argument save=True

@transition(source='new', target='published', save=True)
def publish(self):
    ...

It appears this feature was removed? Is overriding django_fsm.signals.post_transition the new recommended approach?

Yep, it was removed in 2014 with django-fsm 2.0 release.

The recommended way for today is to switch to django-viewflow==2.0.0a and explicitly define desired behavior with .on_success decorator - https://docs-next.viewflow.io/fsm/models.html

What is the recommandation if we want to stick with django-fsm package?

Our solution was a post transition signal to save the new state upon successful state change:

from django_fsm.signals import post_transition

@receiver(post_transition, sender=models.MyModel)
def save_new_workflow_state(sender, instance, name, source, target, **kwargs):
    """ Save the new workflow state following successful django_fsm transition. """

    if source != target:
        instance.save()

@xavella thanks for sharing! Is there a way to write this once for all models?
I can already see a new developer joining our codebase, adding a new Django Model and asking "why my model is not saved"? :)

I wouldn't take that approach as it would also apply to the admin models, which I don't see any reason to restrict track. How many models?

I'm not sure to understand the issue with admin models, as only my custom models have FSMField and transitions configured (except if a package is using django-fsm behind the scene ?).

Lots of models (more than 10) will need transitions. If I can do it, I'd like to avoid the manual step to add a signal for each of them, as it could be error-prone, and it's not DRY.

I would do this long-hand, not sure how to abstract the signal but maybe there is a way.

it seems ok following these steps:

  • removing the sender parameter from within the decorator (means all senders are accepted)
  • moving the signal to signals.py into my app
  • importing it using the ready() method