Specify one or multiple source states:
@state_field.transition(source={States.NEW, States.DONE}, target=States.CANCELED)
def cancel(self):
pass
Use State.ANY to allow transition from any state except the target:
@state_field.transition(source=State.ANY, target=States.CANCELED)
def cancel(self):
pass
The state changes before the method runs. This lets you chain transitions:
@state_field.transition(source=States.NEW, target=States.IN_PROCESS)
def pay(self):
try:
self.perform_payment()
except:
self.error()
@state_field.transition(source=States.IN_PROCESS, target=States.DONE)
def perform_payment(self):
pass
@state_field.transition(source=States.IN_PROCESS, target=States.ERROR)
def payment_error(self):
pass
If you omit the target, no state change happens:
@state_field.transition(source=States.NEW)
def notify(self):
mail_admins('New flow is waiting', self.text)
Stack multiple decorators for different source/target combinations:
@state_field.transition(source=States.NEW, target=States.DONE)
@state_field.transition(source=States.DONE, target=States.NEW)
def toggle(self):
pass
Add a human-readable label:
@state_field.transition(source=States.NEW, target=States.DONE)
@state_field.transition(source=States.DONE, target=States.NEW, label=_("Toggle back to New"))
def toggle(self):
pass
toggle.label = _("Toggle report state")
Require conditions to be met before a transition can happen. Conditions are functions that return True or False. They should not have side effects.
def can_publish(instance):
# No publishing after 17 hours
if datetime.datetime.now().hour > 17:
return False
return True
Or use a method on the flow class:
def can_destroy(self):
return self.is_under_investigation()
Apply conditions:
@state_field.transition(States.NEW, target=States.DONE, conditions=[this.can_publish])
def publish(self):
pass
Attach permission checks to transitions. Use a callable that takes the flow instance and user:
@state_field.transition(
source=States.NEW,
target=States.DONE,
permission=lambda flow, user: user.has_perm('myapp.delete_review', obj=flow.report)
)
def remove(self):
pass
@state_field.transition(source=States.NEW, target=States.DONE, permission=this.is_owner)
def hide(self):
pass
def is_owner(self, user):
return self.author == user
Check permissions in your code:
if not flow.remove.has_perm(request.user):
raise PermissionDenied
Add custom metadata to transitions:
@state.transition(
field=state,
source=STATE.ANY,
target=States.ON_HOLD,
custom=dict(verbose='Hold for legal reasons'))
def legal_hold(self):
"""
Side effects galore
"""