Django friendly finite state machine support for models through FSMField and the @transition decorator.
npx @tessl/cli install tessl/pypi-django-fsm@2.8.0Django FSM provides declarative finite state machine support for Django models through FSMField and the @transition decorator. It enables developers to manage model state transitions in a structured way by decorating methods that can contain side-effects of state changes, supports conditional transitions with permission checking, optimistic locking to prevent concurrent state modifications, and state-based proxy model switching.
pip install django-fsmfrom django_fsm import FSMField, transitionCommon additional imports:
from django_fsm import (
FSMField, FSMIntegerField, FSMKeyField,
transition, can_proceed, has_transition_perm,
TransitionNotAllowed, ConcurrentTransition, InvalidResultState,
FSMModelMixin, ConcurrentTransitionMixin,
GET_STATE, RETURN_VALUE
)For signals:
from django_fsm.signals import pre_transition, post_transitionfrom django.db import models
from django_fsm import FSMField, transition
class BlogPost(models.Model):
state = FSMField(default='new')
title = models.CharField(max_length=200)
content = models.TextField()
@transition(field=state, source='new', target='published')
def publish(self):
"""
This method may contain side-effects like
updating caches, notifying users, etc.
"""
pass
@transition(field=state, source='published', target='archived')
def archive(self):
pass
# Usage example
blog_post = BlogPost.objects.create(title="My Post", content="Content")
print(blog_post.state) # 'new'
blog_post.publish()
blog_post.save()
print(blog_post.state) # 'published'
# Check if transition is possible before calling
from django_fsm import can_proceed
if can_proceed(blog_post.archive):
blog_post.archive()
blog_post.save()Django FSM uses a decorator-based approach to define state machines:
The library integrates seamlessly with Django's ORM and provides dynamic methods for each FSM field to query available transitions.
Core FSM field types for different data storage needs: CharField-based FSMField, IntegerField-based FSMIntegerField, and ForeignKey-based FSMKeyField. Includes configuration options for default states, protection against direct modification, and state-based proxy model switching.
class FSMField(FSMFieldMixin, models.CharField):
def __init__(self, default=None, protected=False, state_choices=None, max_length=50, **kwargs): ...
class FSMIntegerField(FSMFieldMixin, models.IntegerField):
def __init__(self, default=None, protected=False, state_choices=None, **kwargs): ...
class FSMKeyField(FSMFieldMixin, models.ForeignKey):
def __init__(self, to, default=None, protected=False, state_choices=None, **kwargs): ...Decorator for marking methods as state transitions with comprehensive configuration options including source/target states, error handling, conditions, permissions, and custom metadata. Also includes utility functions to check transition availability and permissions.
def transition(field, source="*", target=None, on_error=None, conditions=[], permission=None, custom={}): ...
def can_proceed(bound_method, check_conditions=True): ...
def has_transition_perm(bound_method, user): ...Model mixins for enhanced FSM functionality including refresh_from_db support for protected fields and optimistic locking protection against concurrent transitions. These mixins provide additional safety and reliability features for production applications.
class FSMModelMixin(object):
def refresh_from_db(self, *args, **kwargs): ...
class ConcurrentTransitionMixin(object):
def save(self, *args, **kwargs): ...Exception classes for managing state transition errors including general transition failures, concurrent modification conflicts, and invalid result states. Proper exception handling is essential for robust state machine implementations.
class TransitionNotAllowed(Exception):
def __init__(self, *args, object=None, method=None, **kwargs): ...
class ConcurrentTransition(Exception): ...
class InvalidResultState(Exception): ...Django signals for hooking into state transition lifecycle events, enabling custom logic before and after transitions. Signals provide a clean way to implement cross-cutting concerns like logging, notifications, and audit trails.
# From django_fsm.signals
pre_transition = Signal()
post_transition = Signal()Classes for dynamic state resolution allowing transition targets to be determined at runtime based on method return values or custom functions. This enables flexible state machines that can adapt to different business logic scenarios.
class RETURN_VALUE(State):
def __init__(self, *allowed_states): ...
class GET_STATE(State):
def __init__(self, func, states=None): ...Django management command for creating GraphViz visualizations of state machine transitions, helping developers understand and document complex state workflows. The command generates dot files that can be rendered as images or interactive graphs.
# Management command
python manage.py graph_transitions [appname[.model[.field]]] --output file.png --layout dot