Migration Guide¶
This guide covers migrating to django-fsm-rx from other FSM packages.
django-fsm-rx provides full backwards compatibility with django-fsm, django-fsm-2, django-fsm-admin, and django-fsm-log. Your existing code will work with deprecation warnings guiding you to update imports.
Quick Migration Check¶
Management Command¶
Run the built-in migration check command to find deprecated imports in your project:
python manage.py check_fsm_migration
This scans your codebase and shows exactly what imports need updating:
Files affected: 3
Deprecated imports found: 5
myapp/models.py:
Line 1:
- from django_fsm import FSMField, transition
+ from django_fsm_rx import FSMField, transition
myapp/admin.py:
Line 2:
- from django_fsm_admin.mixins import FSMTransitionMixin
+ from django_fsm_rx.admin import FSMAdminMixin
Command Options¶
Option |
Description |
|---|---|
|
Scan a specific directory (default: BASE_DIR) |
|
Comma-separated patterns to exclude |
|
Show detailed migration notes |
|
Output as JSON for CI/automation |
JSON Output for CI¶
For CI integration, use JSON output:
python manage.py check_fsm_migration --json > migration_report.json
Example JSON output:
{
"is_fully_migrated": false,
"files_affected": ["myapp/models.py", "myapp/admin.py"],
"deprecated_imports": [
{
"file": "myapp/models.py",
"line": 1,
"old": "from django_fsm import FSMField",
"new": "from django_fsm_rx import FSMField",
"notes": "Direct replacement, API identical"
}
],
"warnings": [],
"suggested_changes": {
"from django_fsm import FSMField": "from django_fsm_rx import FSMField"
}
}
From django-fsm-2¶
django-fsm-2 is a maintained fork of django-fsm under Django Commons. Migration is straightforward since django-fsm-rx maintains API compatibility.
Step 1: Install the new package¶
pip uninstall django-fsm-2
pip install django-fsm-rx
Step 2: Update INSTALLED_APPS¶
# settings.py
INSTALLED_APPS = [
...,
'django_fsm_rx',
...,
]
Step 3: Run migrations¶
python manage.py migrate django_fsm_rx
This creates the FSMTransitionLog table for audit logging.
Step 4: Update imports (recommended)¶
Your existing imports will continue to work with a deprecation warning:
# Old (still works, shows deprecation warning)
from django_fsm_2 import FSMField, transition
# New (recommended)
from django_fsm_rx import FSMField, transition
API Compatibility¶
All core APIs from django-fsm-2 are fully compatible:
Feature |
Status |
Notes |
|---|---|---|
|
Identical |
|
|
Compatible |
New optional params: |
|
Identical |
|
|
Identical |
|
|
Identical |
|
|
Identical |
|
Wildcard sources ( |
Identical |
|
Prefix wildcards ( |
New |
Matches |
New Features¶
django-fsm-rx adds these optional features:
Automatic audit logging - All transitions logged to
FSMTransitionLogon_successcallback - Runs inside transaction, rolls back togetheron_commitcallback - Runs after commit (for emails, external APIs)atomic=Truedefault - Transitions wrapped intransaction.atomic()
Opting out of new defaults¶
To get behavior identical to django-fsm-2:
# settings.py
DJANGO_FSM_RX = {
'AUDIT_LOG': False, # Disable audit logging
'ATOMIC': False, # Disable transaction wrapping (not recommended)
}
From django-fsm¶
django-fsm is the original FSM package by Mikhail Podgurskiy (now archived).
pip uninstall django-fsm
pip install django-fsm-rx
Follow the same steps as “From django-fsm-2” above. Your from django_fsm import ... imports will also continue to work with a deprecation warning.
From django-fsm-log¶
django-fsm-log provides transition logging for django-fsm. django-fsm-rx includes built-in audit logging that replaces django-fsm-log.
Step 1: Install django-fsm-rx¶
pip uninstall django-fsm-log
pip install django-fsm-rx
Step 2: Update INSTALLED_APPS¶
# settings.py
INSTALLED_APPS = [
...,
'django_fsm_rx', # This is all you need
# 'django_fsm_log', # Remove - no longer needed
...,
]
Important
You do NOT need to add django_fsm_log to INSTALLED_APPS. The compatibility shim is built into django-fsm-rx - just add django_fsm_rx and your existing from django_fsm_log.models import StateLog imports will continue to work automatically.
Step 3: Run migrations¶
python manage.py migrate django_fsm_rx
Step 4: Update imports¶
Your existing imports will continue to work via the compatibility shim:
# Old (still works via compatibility shim)
from django_fsm_log.models import StateLog
# New (recommended)
from django_fsm_rx import FSMTransitionLog
StateLog is an alias to FSMTransitionLog - they are the same model.
Step 5: Migrate existing data (if needed)¶
If you have existing transition logs in django-fsm-log’s StateLog table, you can migrate them:
# One-time migration script
# Note: This assumes you still have the original django-fsm-log installed temporarily
from django.contrib.contenttypes.models import ContentType
from django.db import connection
# Check if old table exists
with connection.cursor() as cursor:
cursor.execute("""
SELECT COUNT(*) FROM information_schema.tables
WHERE table_name = 'django_fsm_log_statelog'
""")
if cursor.fetchone()[0] == 0:
print("No old StateLog table found, skipping migration")
else:
# Migrate data
cursor.execute("""
INSERT INTO django_fsm_rx_fsmtransitionlog
(content_type_id, object_id, transition_name, source_state, target_state, timestamp, by_id, description)
SELECT
content_type_id,
object_id::text,
transition,
source_state,
state,
timestamp,
by_id,
COALESCE(description, '')
FROM django_fsm_log_statelog
""")
print(f"Migrated {cursor.rowcount} records")
Or using Django ORM (if django-fsm-log is still installed):
# If you still have django-fsm-log installed temporarily
from django_fsm_log.models import StateLog as OldStateLog
from django_fsm_rx import FSMTransitionLog
for old_log in OldStateLog.objects.all().iterator():
FSMTransitionLog.objects.create(
content_type=old_log.content_type,
object_id=str(old_log.object_id),
transition_name=old_log.transition,
source_state=old_log.source_state,
target_state=old_log.state,
timestamp=old_log.timestamp,
by=old_log.by,
description=getattr(old_log, 'description', ''),
)
Field Mapping¶
django-fsm-log |
django-fsm-rx |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Decorators¶
The @fsm_log_by and @fsm_log_description decorators are available for compatibility:
from django_fsm_rx.log import fsm_log_by, fsm_log_description
@fsm_log_by
@fsm_log_description
@transition(field=state, source='draft', target='published')
def publish(self, by=None, description=None):
pass
However, with audit logging enabled (default), you may not need these decorators - transitions are automatically logged.
Differences from django-fsm-log¶
Feature |
django-fsm-log |
django-fsm-rx |
|---|---|---|
Model name |
|
|
App label |
|
|
Requires INSTALLED_APPS |
Yes |
No (for shim), Yes (for model) |
Automatic logging |
Via signal |
Via |
Log modes |
Signal only |
|
From django-fsm-admin / django-fsm-2-admin¶
django-fsm-admin and django-fsm-2-admin provide Django admin integration for django-fsm and django-fsm-2 respectively. django-fsm-rx includes built-in admin support.
Step 1: Install django-fsm-rx¶
pip uninstall django-fsm-admin # or django-fsm-2-admin
pip install django-fsm-rx
Step 2: Update admin imports¶
# Old (django-fsm-admin or django-fsm-2-admin)
from fsm_admin.mixins import FSMTransitionMixin
# New
from django_fsm_rx.admin import FSMAdminMixin
# Or use the compatibility alias:
from django_fsm_rx.admin import FSMTransitionMixin # Alias for FSMAdminMixin
Step 3: Update admin classes¶
from django.contrib import admin
from django_fsm_rx.admin import FSMAdminMixin
@admin.register(BlogPost)
class BlogPostAdmin(FSMAdminMixin, admin.ModelAdmin):
fsm_fields = ['state'] # List your FSM fields
list_display = ['title', 'state']
Admin Features¶
django-fsm-rx’s admin integration provides:
Transition buttons - Execute transitions from the change form
Custom labels - Use
custom={'label': 'Publish'}on transitionsForm support - Transitions with forms via
custom={'form': MyForm}Visibility control - Hide transitions with
custom={'admin': False}FSM_ADMIN_FORCE_PERMIT - Require explicit
custom={'admin': True}
See the Admin Integration guide for full documentation.
Compatibility Shims¶
django-fsm-rx provides backwards compatibility shims for seamless migration:
Import Path |
Status |
Notes |
|---|---|---|
|
Works with deprecation warning |
Shim for django-fsm |
|
Works with deprecation warning |
Shim for django-fsm-2 |
|
Works (alias to FSMTransitionLog) |
Shim for django-fsm-log |
|
Works (alias to FSMAdminMixin) |
Shim for django-fsm-admin |
These shims allow gradual migration - update imports at your own pace.
Note: The shims for django_fsm and django_fsm_2 are full package shims included in the django-fsm-rx distribution. The django_fsm_log shim provides StateLog as an alias. The FSMTransitionMixin alias is for django-fsm-admin/django-fsm-2-admin compatibility (these were separate packages, not part of django-fsm-2 core).
Programmatic Migration Utilities¶
For automated migration, CI integration, or custom tooling, use the migration utilities programmatically:
Scanning for Deprecated Imports¶
from django_fsm_rx.migration import (
scan_imports_in_file,
scan_imports_in_directory,
MigrationReport,
)
# Scan a single file
report = scan_imports_in_file('myapp/models.py')
# Scan a directory (recursive)
report = scan_imports_in_directory(
'/path/to/project',
exclude_patterns=['migrations', '__pycache__', 'venv']
)
# Check results
if report.is_fully_migrated:
print("No deprecated imports found!")
else:
print(f"Files to update: {len(report.files_affected)}")
for item in report.deprecated_imports:
print(f" {item['file']}:{item['line']}")
print(f" OLD: {item['old']}")
print(f" NEW: {item['new']}")
if item['notes']:
print(f" NOTE: {item['notes']}")
Model Validation¶
Validate that a model’s FSM configuration is compatible:
from django_fsm_rx.migration import validate_model_fsm_compatibility
from myapp.models import Order
warnings = validate_model_fsm_compatibility(Order)
for warning in warnings:
print(f"Warning: {warning}")
This checks for:
FSM fields are properly configured
Transitions have the correct decorator metadata
Protected fields have
FSMModelMixinif needed
Import Mappings¶
Get all import replacements as a dictionary:
from django_fsm_rx.migration import get_import_replacements, IMPORT_MAPPINGS
# Get simple old->new mapping
replacements = get_import_replacements()
print(replacements['from django_fsm import FSMField'])
# Output: 'from django_fsm_rx import FSMField'
# Access detailed mappings with notes
for mapping in IMPORT_MAPPINGS:
print(f"{mapping['old_module']}.{mapping['old_name']}")
print(f" -> {mapping['new_module']}.{mapping['new_name']}")
print(f" Note: {mapping['notes']}")
Check Migration Status from Django Settings¶
from django_fsm_rx.migration import check_migration_status
# Automatically finds BASE_DIR from Django settings
report = check_migration_status()
print(report) # Human-readable report
Using in CI/CD¶
Create a custom management command or script:
# scripts/check_migration.py
import sys
from django_fsm_rx.migration import run_migration_check_command
if __name__ == '__main__':
# Returns 0 if fully migrated, 1 if migration needed
sys.exit(run_migration_check_command())
Or use the management command in CI:
# .github/workflows/test.yml
- name: Check FSM migration
run: python manage.py check_fsm_migration --json
Complete Import Reference¶
Core FSM (django-fsm / django-fsm-2)¶
Old Import |
New Import |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Admin (django-fsm-admin)¶
Old Import |
New Import |
|---|---|
|
|
|
|
Logging (django-fsm-log)¶
Old Import |
New Import |
|---|---|
|
|
|
|
|
|
|
|