### Quick Start Development Setup with uv Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Clones the repository, installs dependencies, and runs tests using uv for a quick development setup. ```bash # Clone the repository git clone https://github.com/django-commons/django-fsm-2.git cd django-fsm # Install dependencies (creates .venv automatically) uv sync # Run tests uv run pytest # Run tests with coverage uv run pytest --cov=django_fsm --cov-report=term-missing ``` -------------------------------- ### Setup pre-commit hooks with prek Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Installs pre-commit hooks using prek and runs them manually on all files. ```bash # Install prek hooks uv run prek install # Run manually on all files uv run prek run --all-files ``` -------------------------------- ### Development Setup with pip and venv Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Clones the repository, sets up a virtual environment, installs dependencies in development mode, and runs tests using pip. ```bash # Clone the repository git clone https://github.com/django-commons/django-fsm-2.git cd django-fsm # Create virtual environment python -m venv .venv source .venv/bin/activate # On Windows: .venv\Scripts\activate # Install in development mode with dev dependencies pip install -e ".[graphviz]" pip install pytest pytest-django pytest-cov django-guardian prek # Run tests pytest ``` -------------------------------- ### Install django-fsm-2 from Git Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Install the django-fsm-2 package directly from its Git repository. ```bash uv pip install -e git://github.com/django-commons/django-fsm-2.git#egg=django-fsm ``` -------------------------------- ### Install Graphviz Support for Django FSM Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Install the necessary package for generating transition graphs. This can be done via pip with the graphviz extra or by installing the graphviz library directly. ```bash uv pip install django-fsm-2[graphviz] ``` ```bash uv pip install "graphviz>=0.4" ``` -------------------------------- ### Install graphviz on Fedora Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Installs the graphviz system package on Fedora using dnf. ```bash # Fedora sudo dnf install graphviz ``` -------------------------------- ### Install graphviz on Windows Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Installs the graphviz system package on Windows using Chocolatey. ```bash # Windows (with chocolatey) choco install graphviz ``` -------------------------------- ### Quick Development Setup for Django FSM Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Clone the repository, set up the environment with uv sync, and run tests or linting commands for development. ```bash # Clone and setup git clone https://github.com/django-commons/django-fsm-2.git cd django-fsm uv sync ``` ```bash # Run tests uv run pytest -v # or uv run tox ``` ```bash # Run linting uv run ruff format . uv run ruff check . ``` -------------------------------- ### Install graphviz on Ubuntu/Debian Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Installs the graphviz system package on Ubuntu/Debian using apt-get. ```bash # Ubuntu/Debian sudo apt-get install graphviz ``` -------------------------------- ### Install graphviz on macOS Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Installs the graphviz system package on macOS using Homebrew. ```bash # macOS brew install graphviz ``` -------------------------------- ### Install django-fsm-2 using uv pip Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Install the django-fsm-2 package using the uv pip command. ```bash uv pip install django-fsm-2 ``` -------------------------------- ### Multi-version testing with tox Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Installs tox and runs tests against multiple Python and Django versions, or a specific environment. ```bash # Install tox pip install tox # Run all environments tox # Run specific environment tox -e py312-dj52 ``` -------------------------------- ### Model Helper Methods for Transitions Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Django FSM provides helper methods on model instances to query transitions. These include getting all declared transitions, available transitions in the current state, and available transitions for a specific user. ```python my_model_instance.get_all_status_transitions() my_model_instance.get_available_status_transitions() my_model_instance.get_available_user_status_transitions() ``` -------------------------------- ### Generate Transition Graph PNG for Specific Model Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Create a PNG image file of the transition graph for a specific model by specifying the output file and model name. ```bash ./manage.py graph_transitions -o blog_transitions.png myapp.Blog ``` -------------------------------- ### Check Transition and Execute Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Check if a transition can be performed using fsm.can_proceed and then execute the transition. Remember to save the model instance to persist the state change. ```python import django_fsm as fsm post = BlogPost.objects.get(pk=1) if fsm.can_proceed(post.publish): post.publish() post.save() ``` -------------------------------- ### Handle Transition in a Django View Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Implement a Django view to handle state transitions. Check for transition permissions using fsm.can_proceed, execute the transition, and save the model instance. ```python import django_fsm as fsm def publish_view(request, post_id, **kwargs): post = get_object_or_404(BlogPost, pk=post_id) if not fsm.can_proceed(post.publish): raise PermissionDenied post.publish() post.save() return redirect('/') ``` -------------------------------- ### Run basic tests with uv Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Executes the test suite using pytest via uv. ```bash uv run pytest ``` -------------------------------- ### Generate Transition Graph Dot File Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Use the manage.py graph_transitions command to create a dot file representing the state machine transitions for all models. ```bash ./manage.py graph_transitions > transitions.dot ``` -------------------------------- ### Format code with ruff Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Uses ruff to format the codebase according to defined style guidelines. ```bash uv run ruff format . ``` -------------------------------- ### Run tests with coverage report using uv Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Executes the test suite and generates coverage reports in both terminal and HTML formats using pytest via uv. ```bash uv run pytest --cov=django_fsm --cov-report=term-missing --cov-report=html ``` -------------------------------- ### Attach Custom Form to FSM Transition Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Specify a custom form for an FSM transition using the 'custom' argument with a dotted import path. This form will be used to collect input before the transition. ```python from django import forms import django_fsm as fsm class RenameForm(forms.Form): new_title = forms.CharField(max_length=255) # it's also possible to declare fsm log description description = forms.CharField(max_length=255) class BlogPost(fsm.FSMModelMixin, models.Model): title = models.CharField(max_length=255) state = fsm.FSMField(default="created") @fsm.transition( field=state, source="*", target="created", custom={ "label": "Rename", "help_text": "Rename blog post", "form": "path.to.RenameForm", }, ) def rename(self, new_title, **kwargs): self.title = new_title ``` -------------------------------- ### Set Default Transition Visibility in Admin Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Configure `fsm_default_disallow_transition = False` in your admin class to make transitions visible by default, requiring `custom={"admin": True}` to explicitly hide them. ```python from django_fsm.admin import FSMAdminMixin @admin.register(AdminBlogPost) class MyAdmin(FSMAdminMixin, admin.ModelAdmin): fsm_default_disallow_transition = False ... ``` -------------------------------- ### Check Transition Permissions Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Use `fsm.has_transition_perm` to check if a user has the necessary permissions to perform a specific transition before attempting it. This is useful in view logic. ```python import django_fsm as fsm def publish_view(request, post_id): post = get_object_or_404(BlogPost, pk=post_id) if not fsm.has_transition_perm(post.publish, request.user): raise PermissionDenied post.publish() post.save() return redirect('/') ``` -------------------------------- ### Apply FSMAdminMixin to ModelAdmin Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Use FSMAdminMixin in your admin.py file to add FSM behavior to your ModelAdmin. Ensure FSMAdminMixin precedes ModelAdmin in the inheritance order. ```python from django_fsm.admin import FSMAdminMixin @admin.register(AdminBlogPost) class MyAdmin(FSMAdminMixin, admin.ModelAdmin): # Declare the fsm fields you want to manage fsm_fields = ['my_fsm_field'] ... ``` -------------------------------- ### Run tests with verbose output using uv Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Executes the test suite with verbose output enabled using pytest via uv. ```bash uv run pytest -v ``` -------------------------------- ### Custom Transition Metadata Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Attach arbitrary data to a transition using the `custom` argument. This data can be accessed later for custom logic or display purposes. ```python @fsm.transition( field=state, source='*', target='onhold', custom=dict(verbose='Hold for legal reasons'), ) def legal_hold(self, **kwargs): pass ``` -------------------------------- ### Django Template for FSM Transitions Source: https://github.com/django-commons/django-fsm-2/blob/main/django_fsm/contrib/unfold/templates/django_fsm/fsm_admin_change_form.html This template code extends Django's admin change form to display available FSM transitions as buttons. It iterates through available transitions and includes a partial template for each button. ```html {% extends 'admin/change_form.html' %} {% load i18n admin_urls static admin_modify unfold %} {% block submit_buttons_bottom %} {% for fsm_object_transition in fsm_object_transitions %} {{ fsm_object_transition.block_label }} {% for transition in fsm_object_transition.available_transitions %} {% include "django_fsm/fsm_transition_button.html" with transition=transition %} {% endfor %} {% endfor %} {{ block.super }} {% endblock %} ``` -------------------------------- ### Update Import Path for FSMAdminMixin Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Replace the old import path with the new one for FSMAdminMixin. ```python - from django_fsm_admin.mixins import FSMTransitionMixin + from django_fsm.admin import FSMAdminMixin ``` -------------------------------- ### Dynamic Target States with RETURN_VALUE Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Use `fsm.RETURN_VALUE` to dynamically determine the target state based on the return value of the transition method. This allows for conditional state changes. ```python import django_fsm as fsm @fsm.transition( field=state, source='*', target=fsm.RETURN_VALUE('for_moderators', 'published'), ) def publish(self, is_public=False, **kwargs): return 'for_moderators' if is_public else 'published' ``` -------------------------------- ### Check code style with ruff Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Uses ruff to check the codebase for linting issues. ```bash uv run ruff check . ``` -------------------------------- ### Add django_fsm to INSTALLED_APPS Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Include 'django_fsm' in your Django project's INSTALLED_APPS setting to enable features like graph transitions and admin integration. ```python INSTALLED_APPS = ( ... 'django_fsm', ... ) ``` -------------------------------- ### Define FSM Forms in ModelAdmin Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Map transition names to form classes or import paths within the ModelAdmin to associate custom forms with transitions without modifying the transition definition. ```python from django_fsm.admin import FSMAdminMixin from .admin_forms import RenameForm @admin.register(AdminBlogPost) class MyAdmin(FSMAdminMixin, admin.ModelAdmin): fsm_fields = ["state"] fsm_forms = { "rename": "path.to.RenameForm", # use import path "rename": RenameForm, # or FormClass } ``` -------------------------------- ### Define BlogPost Model with FSMField Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Define a Django model that includes an FSMField for state management and a transition method. ```python from django.db import models import django_fsm as fsm class BlogPost(fsm.FSMModelMixin, models.Model): state = fsm.FSMField(default='new') @fsm.transition(field=state, source='new', target='published') def publish(self, **kwargs): pass ``` -------------------------------- ### Dynamic Target States with GET_STATE Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Use `fsm.GET_STATE` to define a callable that determines the target state. This callable receives the instance and any keyword arguments, and must return one of the specified states. ```python import django_fsm as fsm @fsm.transition( field=state, source='for_moderators', target=fsm.GET_STATE( lambda self, allowed: 'published' if allowed else 'rejected', states=['published', 'rejected'], ), ) def moderate(self, allowed, **kwargs): pass ``` ```python import django_fsm as fsm @fsm.transition( field=state, source='for_moderators', target=fsm.GET_STATE( lambda self, **kwargs: 'published' if kwargs.get('allowed', True) else 'rejected', states=['published', 'rejected'], ), ) def moderate(self, allowed=True, **kwargs): pass ``` -------------------------------- ### Transition Permissions Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Control access to transitions using the `permission` argument. It can be a permission string or a callable that checks user permissions against the instance. ```python @fsm.transition( field=state, source='*', target='published', permission=lambda instance, user: not user.has_perm('myapp.can_make_mistakes'), ) def publish(self, **kwargs): pass ``` ```python @fsm.transition( field=state, source='*', target='removed', permission='myapp.can_remove_post', ) def remove(self, **kwargs): pass ``` -------------------------------- ### Django Unfold Integration Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Include 'django_fsm.contrib.unfold' in INSTALLED_APPS to enable templates tailored for the Django Unfold admin UI. ```python INSTALLED_APPS = ( ... 'unfold', 'django_fsm.contrib.unfold', ... ) ``` -------------------------------- ### Type Checking with mypy Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Performs static type checking on the django_fsm package using mypy. ```bash uv run mypy django_fsm ``` -------------------------------- ### Define Custom Form for FSM Transition Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Define a Django Form class to be used with an FSM transition. The form fields can be passed as keyword arguments to the transition method. ```python from django import forms import django_fsm as fsm class RenameForm(forms.Form): new_title = forms.CharField(max_length=255) # it's also possible to declare fsm log description description = forms.CharField(max_length=255) ``` -------------------------------- ### Run specific test file with uv Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Executes tests from a specific file with verbose output using pytest via uv. ```bash uv run pytest tests/test_basic_transitions.py -v ``` -------------------------------- ### Override FSMAdminMixin Methods for Custom Labels and Help Text Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Override `get_fsm_label` and `get_help_text` methods in your ModelAdmin class to provide custom labels and help text for transitions. ```python from django_fsm.admin import FSMAdminMixin @admin.register(AdminBlogPost) class MyAdmin(FSMAdminMixin, admin.ModelAdmin): ... def get_fsm_label(self, transition): if transition.name == "do_something": return "My awesome transition" return super().get_fsm_label(transition) def get_help_text(self, transition): if transition.name == "do_something": return "Rename blog post" return super().get_help_text(transition) ``` -------------------------------- ### Run specific test with uv Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Executes a single, specific test case with verbose output using pytest via uv. ```bash uv run pytest tests/test_basic_transitions.py::test_initial_state -v ``` -------------------------------- ### Customize Transition Buttons with Labels and Help Text Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Customize the appearance of transition buttons in the admin interface by adding 'label' and 'help_text' to the 'custom' attribute of the transition decorator. ```python @fsm.transition( field='state', source=['startstate'], target='finalstate', custom={ "label": "My awesome transition", # this "help_text": "Rename blog post", # and this }, ) def do_something(self, **kwargs): ... ``` -------------------------------- ### Define Transition Conditions Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Use condition functions to restrict transitions. These functions receive the instance and must return a truthy or falsy value. Avoid side effects in condition functions. ```python import django_fsm as fsm def can_publish(instance): # No publishing after 17 hours return datetime.datetime.now().hour <= 17 class XXX(fsm.FSMModelMixin, models.Model): @fsm.transition( field=state, source='new', target='published', conditions=[can_publish] ) def publish(self, **kwargs): pass ``` ```python import django_fsm as fsm class XXX(fsm.FSMModelMixin, models.Model): def can_destroy(self): return self.is_under_investigation() @fsm.transition( field=state, source='*', target='destroyed', conditions=[can_destroy] ) def destroy(self, **kwargs): pass ``` -------------------------------- ### Error Target State for Transitions Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Specify an `on_error` state to transition to if an exception occurs during the transition method execution. This helps in managing failed transitions gracefully. ```python @fsm.transition( field=state, source='new', target='published', on_error='failed' ) def publish(self, **kwargs): """ Some exception could happen here """ pass ``` -------------------------------- ### Declare State Transition Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Use the @fsm.transition decorator to declare a state transition. Specify the target field, source state(s), and the target state. The decorated method can contain business logic and side effects. ```python import django_fsm as fsm @fsm.transition(field=state, source='new', target='published') def publish(self, **kwargs): """ This function may contain side effects, like updating caches, notifying users, etc. The return value will be discarded. """ ``` -------------------------------- ### FSMKeyField with FK Support Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Use FSMKeyField to store state values in a separate table and maintain foreign key integrity. Source and target states use PK values from the referenced model. ```python import django_fsm as fsm from django.db import models class DbState(fsm.FSMModelMixin, models.Model): id = models.CharField(primary_key=True) label = models.CharField() def __str__(self): return self.label class BlogPost(fsm.FSMModelMixin, models.Model): state = fsm.FSMKeyField(DbState, default='new') @fsm.transition(field=state, source='new', target='published') def publish(self, **kwargs): pass ``` ```json [ { "pk": "_NEW_", "model": "myapp.dbstate", "fields": { "label": "New" } }, { "pk": "_PUBLISHED_", "model": "myapp.dbstate", "fields": { "label": "Published" } } ] ``` -------------------------------- ### FSMIntegerField with Enum-Style States Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Utilize FSMIntegerField for state management using integer choices, providing a clear and type-safe way to define states. Transitions can reference these choices directly. ```python import django_fsm as fsm from django.db import models class BlogPostStateChoices(models.IntegerChoices): NEW = 10, "New" PUBLISHED = 20, "Published" HIDDEN = 30, "Hidden" class BlogPostWithIntegerField(fsm.FSMModelMixin, models.Model): state = fsm.FSMIntegerField(default=BlogPostStateChoices.NEW) @fsm.transition( field=state, source=BlogPostStateChoices.NEW, target=BlogPostStateChoices.PUBLISHED, ) def publish(self, **kwargs): pass ``` -------------------------------- ### Auto-fix code style issues with ruff Source: https://github.com/django-commons/django-fsm-2/blob/main/CONTRIBUTING.md Uses ruff to automatically fix linting issues in the codebase. ```bash uv run ruff check --fix . ``` -------------------------------- ### Optimistic Locking with ConcurrentTransitionMixin Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Implement ConcurrentTransitionMixin to prevent concurrent state modifications. A ConcurrentTransition error is raised on save() if the state has changed in the database. ```python import django_fsm as fsm from django.db import models class BlogPost(fsm.ConcurrentTransitionMixin, models.Model): state = fsm.FSMField(default='new') ``` -------------------------------- ### Extend Django Admin Change Form for FSM Transitions Source: https://github.com/django-commons/django-fsm-2/blob/main/django_fsm/templates/django_fsm/fsm_admin_change_form.html Override the 'submit_buttons_bottom' block in your Django admin change form template to display FSM transition buttons. This requires iterating through available transitions and including a specific button template for each. ```html {% extends 'admin/change_form.html' %} {% block submit_buttons_bottom %} {% for fsm_object_transition in fsm_object_transitions %} {{ fsm_object_transition.block_label }} {% for transition in fsm_object_transition.available_transitions %} {% include "django_fsm/fsm_transition_button.html" with transition=transition %} {% endfor %} {% endfor %} {{ block.super }} {% endblock %} ``` -------------------------------- ### Hide Transitions in Admin Interface by Overriding Method Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Hide a transition button in the admin interface by overriding the `is_fsm_transition_visible` method in your ModelAdmin class. ```python from django_fsm.admin import FSMAdminMixin @admin.register(AdminBlogPost) class MyAdmin(FSMAdminMixin, admin.ModelAdmin): ... def is_fsm_transition_visible(self, transition: fsm.Transition) -> bool: if transition.name == "do_something": return False return super().is_fsm_transition_visible(transition) ``` -------------------------------- ### Protected State Fields Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Set `protected=True` on FSMField to prevent direct assignment. Only transitions can change the state. FSMModelMixin allows `refresh_from_db` to work without issues. ```python import django_fsm as fsm class BlogPost(fsm.FSMModelMixin, models.Model): state = fsm.FSMField(default='new', protected=True) model = BlogPost() model.state = 'invalid' # Raises AttributeError model.refresh_from_db() # Works ``` -------------------------------- ### Exclude Transitions from Graph Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Use the -e flag with the graph_transitions command to exclude specific transitions from the generated graph. ```bash ./manage.py graph_transitions -e transition_1,transition_2 myapp.Blog ``` -------------------------------- ### Hide Transitions in Admin Interface using Custom Attribute Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Hide a transition button in the admin interface by setting `custom={"admin": False}` in the transition decorator. ```python @fsm.transition( field='state', source=['startstate'], target='finalstate', custom={ "admin": False, # this }, ) def do_something(self, **kwargs): # will not add a button "Do Something" to your admin model interface ``` -------------------------------- ### Add FSMField to Model Source: https://github.com/django-commons/django-fsm-2/blob/main/README.md Add an FSMField to your Django model to store the state. This field will manage the state transitions. ```python import django_fsm as fsm class BlogPost(fsm.FSMModelMixin, models.Model): state = fsm.FSMField(default='new') ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.