Package: django-states2
DescriptionState engine for django models. Define a state graph for a model and
remember the state of each object. State transitions can be logged for
objects.Installationpipinstalldjango-states2Usage exampleTo use a state machine, you should add a state field to the modelfromdjango_states.fieldsimportStateFieldfromdjango_states.machineimportStateMachine,StateDefinition,StateTransitionclassPurchaseStateMachine(StateMachine):log_transitions=True# possible statesclassinitiated(StateDefinition):description=_('Purchase initiated')initial=Trueclasspaid(StateDefinition):description=_('Purchase paid')defhandler(self,instance):code_to_execute_when_arriving_in_this_state()classshipped(StateDefinition):description=_('Purchase shipped')# state transitionsclassmark_paid(StateTransition):from_state='initiated'to_state='paid'description='Mark this purchase as paid'classship(StateTransition):from_state='paid'to_state='shipped'description='Ship purchase'defhandler(transition,instance,user):code_to_execute_during_this_transition()defhas_permission(transition,instance,user):returntrue_when_user_can_make_this_transition()classPurchase(StateModel):purchase_state=StateField(machine=PurchaseStateMachine,default='initiated')...(otherfieldsforapurchase)Iflog_transitionsis enabled, another model is created. Everything
should be compatible with South_ for migrations.Note: If you’re creating aDataMigrationinSouth, remember to useobj.save(no_state_validation=True)Usage example:p=Purchase()# Will automatically create state object for this purchase, in the# initial state.p.save()p.get_purchase_state_info().make_transition('mark_paid',request.user)# User parameter is optionalp.state# Will return 'paid'p.get_purchase_state_info().description# Will return 'Purchase paid'# Returns an iterator of possible transitions for this purchase.p.get_purchase_state_info().possible_transitions()# Which can be used like this..[x.get_name()forxinp.possible_transitions]For better transition control, override:has_permission(self, instance, user): Check whether this user is
allowed to make this transition.handler(self, instance, user): Code to run during this
transition. When an exception has been raised in here, the transition
will not be made.Get all objects in a certain state:Purchase.objects.filter(state='initiated')ValidationYou can add a test that needs to pass before a state transition can be
executed. Well, you can add 2: one based on the current user
(has_permission) and one generic (validate).So on aStateTransition-object you need to specify an extravalidatefunction (signature isvalidate(cls, instance)). This
should yieldTransitionValidationError, this way you can return
multiple errors on that need to pass before the transition can happen.Thehas_permissionfunction (signaturehas_permission(transition, instance, user)) should check whether the
given user is allowed to make the transition. E.g. a super user can
moderate all comments while other users can only moderate comments on
their blog-posts.GroupsSometimes you want to group several states together, since for a certain
view (or other content) it doesn’t really matter which of the states it
is. We support 2 different state groups, inclusive (only these) or
exclusive (everything but these):classis_paid(StateGroup):states=['paid','shipped']classis_paid(StateGroup):exclude_states=['initiated']State graphYou can get a graph of your states by running thegraph_statesmanagement command.pythonmanage.pygraph_statesmyapp.Purchase.stateThis requiresgraphvizand python bindings for
graphviz:pygraphvizandyapgvb.