classLiveStatus(StatusBase):"""Workflow for Lifecycle."""# Define the states as constantsDEVELOP='develop'LIVE='live'MAINTENANCE='maintenance'DELETED='deleted'# Give the states a human readable labelSTATE_CHOICES=((DEVELOP,'Under Development'),(LIVE,'Live'),(MAINTENANCE,'Under Maintenance'),(DELETED,'Deleted'),)# Define the transitions as constantsPUBLISH='publish'MAKE_PRIVATE='make_private'MARK_DELETED='mark_deleted'REVERT_DELETED='revert_delete'# Give the transitions a human readable label and css class# which will be used in the django adminTRANSITION_LABELS={PUBLISH:{'label':'Make live','cssclass':'default'},MAKE_PRIVATE:{'label':'Under maintenance'},MARK_DELETED:{'label':'Mark as deleted','cssclass':'deletelink'},REVERT_DELETED:{'label':'Revert Delete','cssclass':'default'},}# Construct the values to pass to the state machine constructor# The states of the machineSM_STATES=[DEVELOP,LIVE,MAINTENANCE,DELETED,]# The machines initial stateSM_INITIAL_STATE=DEVELOP# The transititions as a list of dictionariesSM_TRANSITIONS=[# trigger, source, destination{'trigger':PUBLISH,'source':[DEVELOP,MAINTENANCE],'dest':LIVE,},{'trigger':MAKE_PRIVATE,'source':LIVE,'dest':MAINTENANCE,},{'trigger':MARK_DELETED,'source':[DEVELOP,LIVE,MAINTENANCE,],'dest':DELETED,},{'trigger':REVERT_DELETED,'source':DELETED,'dest':MAINTENANCE,},]

Next we create a mixin to create a state machine for the django model.

Note

The mixin or the model must provide a state property.
In this implementation state is mapped to the
django model field wf_state

The mixin must override the machine of the StateMachineMixinBase class.
The minimum boilerplate to achieve this is:

machine=Machine(model=None,**status_class.get_kwargs())

In the example we also define a wf_finalize method that will set the
date when the last transition occurred on every transaction.

classLifecycleStateMachineMixin(StateMachineMixinBase):"""Lifecycle workflow state machine."""status_class=LiveStatusmachine=Machine(model=None,finalize_event='wf_finalize',auto_transitions=False,**status_class.get_kwargs()# noqa: C815)@propertydefstate(self):"""Get the items workflowstate or the initial state if none is set."""ifself.wf_state:returnself.wf_statereturnself.machine.initial@state.setterdefstate(self,value):"""Set the items workflow state."""self.wf_state=valuereturnself.wf_statedefwf_finalize(self,*args,**kwargs):"""Run this on all transitions."""self.wf_date=timezone.now()

classLifecycle(LifecycleStateMachineMixin,models.Model):""" A model that provides workflow state and workflow date fields. This is a minimal example implementation. """classMeta:# noqa: D106abstract=Falsewf_state=models.CharField(verbose_name='Workflow Status',null=False,blank=False,default=LiveStatus.SM_INITIAL_STATE,choices=LiveStatus.STATE_CHOICES,max_length=32,help_text='Workflow state',)wf_date=models.DateTimeField(verbose_name='Workflow Date',null=False,blank=False,default=timezone.now,help_text='Indicates when this workflowstate was entered.',)

We can now inspect the behaviour of the model model with
pythonmanage.pyshell