{"id":5613,"library":"django-fsm","title":"Django FSM","description":"Django FSM (Finite State Machine) provides declarative state management for Django models. It allows defining states and transitions using decorators, ensuring state changes adhere to predefined rules. The standalone `django-fsm` library, currently at version 3.0.1, is deprecated; its functionality has been integrated into `viewflow.fsm` since version 3.0.0. The original library will no longer receive updates.","status":"deprecated","version":"3.0.1","language":"en","source_language":"en","source_url":"https://github.com/kmmbvnr/django-fsm","tags":["django","fsm","state machine","workflow","deprecated"],"install":[{"cmd":"pip install django-fsm","lang":"bash","label":"Install `django-fsm` (deprecated)"},{"cmd":"pip install django-viewflow","lang":"bash","label":"Install `viewflow` (for `viewflow.fsm` - the maintained successor)"}],"dependencies":[{"reason":"Core framework dependency for integration.","package":"Django"},{"reason":"The successor library for `django-fsm` functionality (from v3.0.0 onwards).","package":"viewflow","optional":true},{"reason":"Commonly used for logging FSM transitions in Django Admin.","package":"django-fsm-log","optional":true},{"reason":"Required for graph_transition command to visualize state machines.","package":"pygraphviz","optional":true}],"imports":[{"note":"The direct import from `django_fsm` is the most common and recommended way since early versions.","wrong":"from django_fsm.db.fields import FSMField","symbol":"FSMField","correct":"from django_fsm import FSMField"},{"note":"The direct import from `django_fsm` is the most common and recommended way.","wrong":"from django_fsm.db.fields import transition","symbol":"transition","correct":"from django_fsm import transition"},{"note":"Used for integer-based state fields.","symbol":"FSMIntegerField","correct":"from django_fsm import FSMIntegerField"},{"note":"Used for Foreign Key-based state fields.","symbol":"FSMKeyField","correct":"from django_fsm import FSMKeyField"},{"note":"Utility function to check if a transition is allowed.","symbol":"can_proceed","correct":"from django_fsm import can_proceed"},{"note":"Mixin for integrating FSM functionality into Django Admin.","symbol":"FSMAdminMixin","correct":"from django_fsm.admin import FSMAdminMixin"},{"note":"The new way to define states and transitions in `viewflow.fsm` (successor to `django-fsm` v3.0+).","symbol":"State","correct":"from viewflow.fsm import State"}],"quickstart":{"code":"import os\nimport django\nfrom enum import Enum\nfrom django.db import models\nfrom django_fsm import FSMField, transition\n\nos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'your_project_name.settings') # Replace with actual project settings if needed\ndjango.setup()\n\nclass BlogPost(models.Model):\n    class BlogState(models.TextChoices):\n        DRAFT = \"draft\", \"Draft\"\n        REVIEW = \"review\", \"Under Review\"\n        PUBLISHED = \"published\", \"Published\"\n        ARCHIVED = \"archived\", \"Archived\"\n\n    state = FSMField(choices=BlogState.choices, default=BlogState.DRAFT, protected=True)\n    title = models.CharField(max_length=255)\n    content = models.TextField()\n\n    @transition(field=state, source=BlogState.DRAFT, target=BlogState.REVIEW)\n    def submit_for_review(self):\n        print(f\"Blog post '{self.title}' submitted for review.\")\n\n    @transition(field=state, source=BlogState.REVIEW, target=BlogState.PUBLISHED)\n    def publish(self):\n        print(f\"Blog post '{self.title}' published!\")\n\n    @transition(field=state, source=[BlogState.DRAFT, BlogState.REVIEW, BlogState.PUBLISHED], target=BlogState.ARCHIVED)\n    def archive(self):\n        print(f\"Blog post '{self.title}' archived.\")\n\n    def __str__(self):\n        return f\"{self.title} ({self.get_state_display()})\"\n\n# Example Usage (assuming a Django environment is set up and models are synced)\n# For a real application, replace this with actual model creation/retrieval.\n# try:\n#     blog_post = BlogPost.objects.create(title=\"My First Post\", content=\"Lorem ipsum...\")\n#     print(blog_post)\n\n#     if can_proceed(blog_post.submit_for_review):\n#         blog_post.submit_for_review()\n#         blog_post.save()\n#         print(blog_post)\n\n#     if can_proceed(blog_post.publish):\n#         blog_post.publish()\n#         blog_post.save()\n#         print(blog_post)\n\n#     if can_proceed(blog_post.archive):\n#         blog_post.archive()\n#         blog_post.save()\n#         print(blog_post)\n\n#     # This would fail if protected=True and not using a transition method\n#     # blog_post.state = BlogPost.BlogState.DRAFT\n#     # blog_post.save()\n\n# except Exception as e:\n#     print(f\"An error occurred: {e}\")","lang":"python","description":"This quickstart demonstrates defining states with `FSMField` and transitions with the `@transition` decorator for a `BlogPost` model. It highlights basic state transitions and the use of `protected=True` to enforce transitions via methods only. Note that for versions 3.0.0 and above, the functionality has migrated to `viewflow.fsm`."},"warnings":[{"fix":"Migrate existing code to use `viewflow.fsm` classes and decorators. Refer to the `viewflow` documentation for the new API, particularly the migration guide if available. The core concepts of FSM remain, but import paths and class structures have changed significantly.","message":"The standalone `django-fsm` library (version 3.0.0+) is no longer maintained. Its functionality has been fully integrated into `viewflow` as the `viewflow.fsm` package. Users are strongly encouraged to migrate to `viewflow.fsm` for new features and ongoing maintenance.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"Always follow a successful transition method call with `model_instance.save()`.","message":"Calling a transition method (e.g., `model_instance.transition_method()`) changes the state only in memory. You *must* call `model_instance.save()` afterward to persist the state change to the database.","severity":"gotcha","affected_versions":"All"},{"fix":"Ensure all state modifications go through methods decorated with `@transition`. If direct assignment is temporarily needed (e.g., for migrations or testing), set `protected=False` or bypass FSM checks carefully.","message":"If `FSMField(protected=True)` is used, direct assignment to the state field (e.g., `model.state = 'new_state'`) will raise an `AttributeError`. State changes must occur via decorated transition methods. This is often desired for strict state machine enforcement.","severity":"gotcha","affected_versions":"All"},{"fix":"If `db_index` is required for `FSMIntegerField` instances, explicitly set `db_index=True` when defining the field in your model (e.g., `state = FSMIntegerField(db_index=True)`).","message":"The default `db_index=True` for `FSMIntegerField` was removed in version 2.2.0. This could subtly affect database performance if you relied on it for indexing.","severity":"breaking","affected_versions":">=2.2.0"}],"env_vars":null,"last_verified":"2026-04-13T00:00:00.000Z","next_check":"2026-07-12T00:00:00.000Z"}