Skip to main content

django-fsm

Django-friendly finite state machine support.

django-fsm adds simple, declarative state management for Django models and plain Python objects.

Define states and transitions explicitly instead of spreading state assignments across views and models. Transitions check source states and business conditions before running a method. Permissions are checked explicitly with has_transition_perm() or by UI/REST integrations.

Installation

python -m pip install django-fsm

For plain Python classes, no other dependencies are required. To use with Django models, make sure Django is installed or specify the extra:

python -m pip install 'django-fsm[django]'

Quick start

Add an FSMField to your Django model and mark transition methods with the @transition decorator:

from django.db import models
from django_fsm import FSMField, transition, can_proceed

class BlogPost(models.Model):
    state = FSMField(default='draft', protected=True)

    @transition(field=state, source='draft', target='published')
    def publish(self):
        """Side-effects when publishing a post (e.g. notify users, update cache)."""

The protected=True option prevents direct state modification:

post = BlogPost()
post.state = 'published'  # Raises AttributeError

To invoke a transition and persist the change:

post = BlogPost.objects.get(pk=post_id)

if can_proceed(post.publish):
    post.publish()
    post.save()

Conditions and Permissions

Conditions

Use the conditions argument to enforce rules before a transition is allowed. Conditions are callables that take the model instance and return a boolean:

def can_publish(instance):
    return bool(instance.title and instance.body)

class BlogPost(models.Model):
    state = FSMField(default='draft')

    @transition(field=state, source='draft', target='published', conditions=[can_publish])
    def publish(self):
        pass

Permissions

You can guard transitions with permissions and verify them using has_transition_perm:

from django_fsm import transition, has_transition_perm

class BlogPost(models.Model):
    state = FSMField(default='draft')

    @transition(field=state, source='draft', target='published', permission='blog.can_publish')
    def publish(self):
        pass

A direct call such as post.publish() does not check user permissions. Use has_transition_perm() before calling it, as below, or use an integration that performs the check.

In your view:

if not has_transition_perm(post.publish, request.user):
    raise PermissionDenied

post.publish()
post.save()

Dynamic Targets

When the target state depends on runtime logic or method results:

  • RETURN_VALUE(*allowed_states) selects the target from the method’s return value:

from django_fsm import FSMField, RETURN_VALUE, transition

class Task(models.Model):
    status = FSMField(default='pending')

    @transition(field=status, source='pending', target=RETURN_VALUE('approved', 'rejected'))
    def review(self, score):
        return 'approved' if score >= 80 else 'rejected'
  • GET_STATE(func, states=[...]) calculates the target state before the transition runs, based on the instance and method arguments.

Plain Python State Machines

State also works with plain Python classes without Django database models or settings:

from django_fsm import State

class Review:
    state = State(states=None, default='draft')

    @state.transition(source='draft', target='published')
    def publish(self):
        pass

review = Review()
review.publish()
assert review.state == 'published'

Upgrading from django-fsm 3.x

Version 5.0 uses the Viewflow transition engine. Ordinary targets are now applied before the method body runs; django-fsm 3.x applied them after the method returned. Code that reads self.state inside a transition therefore observes the target state in 5.0.

GET_STATE also computes and applies its target before the method runs. RETURN_VALUE applies the target after the method returns.

If the method raises, the adapter restores the source state in memory, or applies on_error when specified. This does not undo database writes or external side effects performed by the method. Use transaction.atomic() around the transition and save() when those database writes must roll back together.

Viewflow Pro Integrations

django-fsm integrates out of the box with Viewflow Pro and Django REST Framework:

  • django_fsm.admin.FlowAdminMixin: Viewflow Admin integration for state workflows.

  • django_fsm.viewset.FlowViewsMixin: Viewflow Viewset workflows.

  • django_fsm.rest.FlowRESTMixin: Optional Django REST Framework viewset integration (requires django-fsm[rest]).

Documentation & Contributing

License

Django FSM is licensed under AGPL-3.0-or-later with the viewflow-library-exception. See LICENSE and LICENSE_EXCEPTION for details.

Metadata

Release files for django-fsm 5.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for django-fsm 5.0.1
File Size Uploaded Provenance Actions
django_fsm-5.0.1.tar.gz 74.3 kB Without provenance Details for django_fsm-5.0.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for django-fsm 5.0.1
File Interpreter ABI Platform Actions
django_fsm-5.0.1-py3-none-any.whl Python 3 none any Details for django_fsm-5.0.1-py3-none-any.whl

Total release size: 120.5 kB

Release files / django_fsm-5.0.1.tar.gz

Download URL django_fsm-5.0.1.tar.gz
Size 74.3 kB
Tags Source
SHA-256 checksum
How to use checksums
afffa8aa8dcd5cb234451e56562392b3bded2838b04913f038230cd15c2c9b76
BLAKE2b-256 checksum
How to use checksums
b7ad8766c42fa281b433d5ae8de5445a693796a5dc285d724047ea63003d431c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Release files / django_fsm-5.0.1-py3-none-any.whl

Download URL django_fsm-5.0.1-py3-none-any.whl
Size 46.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
5e8673f9ac50db2df8e2234608541641ea1bf6364f4296269ef36b88a78a1daf
BLAKE2b-256 checksum
How to use checksums
eb0b91b9da5e98de5617d273352070f2d031c2691c24ae51386057ba54a0c41b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.12.13

Security for django-fsm 5.0.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page