A finite state machine defines a set of states and the transitions between them.
viewflow.fsm enforces these rules at runtime — a method only runs when the
current state allows it. It is the maintained successor to the original
django-fsm library.
FSM fits simple, sequential workflows. For parallel execution or complex branching, use the BPMN workflow engine instead.
The State class holds a value from a Python enum or Django choices class.
You can’t change it with direct assignment—only through transitions.
from enum import Enum
from viewflow.fsm import State
class States(Enum):
NEW = 1
DONE = 2
HIDDEN = 3
class MyFlow(object):
state_field = State(States, default=States.NEW)
@state_field.transition(source=States.NEW, target=States.DONE)
def complete():
pass
@state_field.transition(source=State.ANY, target=States.HIDDEN)
def hide():
pass
flow = MyFlow()
flow.state_field == States.NEW # True
flow.state_field = States.DONE # Raises AttributeError
flow.complete()
flow.state_field == States.DONE # True
flow.complete() # Raises TransitionNotAllowed
The @transition decorator adds runtime checks. Methods can only run when
the object is in the right state.
viewflow.fsm is the maintained successor to the original django-fsm
library. The idea is the same — a state value plus @transition-guarded
methods — with one structural change: the state stays a plain model field, and
the transitions live in a separate flow class instead of on the model.
# Before — django-fsm: state and transitions live on the model
from django_fsm import FSMField, transition
class Report(models.Model):
state = FSMField(default="NEW")
@transition(field=state, source="NEW", target="APPROVED")
def approve(self):
...
# After — viewflow.fsm: plain field on the model, transitions in a flow class
class Report(models.Model):
state_field = models.CharField(
max_length=150, choices=ReportState.choices, default=ReportState.NEW)
class ReportFlow(object):
state_field = fsm.State(ReportState, default=ReportState.NEW)
def __init__(self, report):
self.report = report
@state_field.getter()
def _get(self):
return self.report.state_field
@state_field.setter()
def _set(self, value):
self.report.state_field = value
@state_field.transition(source=ReportState.NEW, target=ReportState.APPROVED)
def approve(self):
...
Keeping the machine in its own class separates transition rules from the model
definition and lets you reuse plain TextChoices. See Wrapping Django Models for the
full pattern.
The original django-fsm is in maintenance mode. viewflow.fsm is its
actively developed successor and the recommended choice for new Django projects.
Store the state in a normal CharField with TextChoices, then wrap the
model in a flow class whose fsm.State descriptor declares the allowed
transitions. See Wrapping Django Models.
FSM models one object moving through sequential states. BPMN models a whole process with parallel branches and multiple participants. Use FSM for simple status fields and the BPMN workflow engine for multi-step processes.