Package: django-fsm-log
Django Finite State Machine LogProvides persistence of the transitions of your fsm's models. Backed by the excellentDjango FSMpackage.Logs can be accessed before a transition occurs and before they are persisted to the database
by enabling a cached backend. SeeAdvanced UsageChangelog4.0.0 (not released)3.1.0 (2023-03-23)fsm_log_descriptionnow accepts a default description parameterDocumentfsm_log_descriptiondecoratorAdd support for Django 4.1Add compatibility for python 3.113.0.0 (2022-01-14)Switch to github actions (from travis-ci)Test against django 3.2 and 4.0, then python 3.9 and 3.10Drop support for django 1.11, 2.0, 2.1, 3.0, 3.1Drop support for python 3.4, 3.5, 3.6allow using StateLogManager in migrations#952.0.1 (2020-03-26)Add support for django3.0Drop support for python21.6.2 (2019-01-06)Address Migration history breakage added in 1.6.11.6.1 (2018-12-02)Make StateLog.description field nullable1.6.0 (2018-11-14)Add source state on transitionsFixedget_state_displaywith FSMIntegerField (#63)Fixed handling of transitions if target is None (#71)Addedfsm_log_descriptiondecorator (#1, #67)Dropped support for Django 1.10 (#64)1.5.0 (2017-11-29)cleanup deprecated code.add codecov support.switch to pytest.add Admin integration to visualize past transitions.1.4.0 (2017-11-09)Bring compatibility with Django 2.0 and drop support of unsupported versions
of Django:1.6,1.7,1.9.CompatibilityPython 2.7 and 3.4+Django 1.8+Django-FSM 2+InstallationFirst, install the package with pip. This will automatically install any
dependencies you may be missingpipinstalldjango-fsm-logRegister django_fsm_log in your list of Django applications:INSTALLED_APPS=(...,'django_fsm_log',...,)Then migrate the app to create the database tablepythonmanage.pymigratedjango_fsm_logUsageThe app listens for thedjango_fsm.signals.post_transitionsignal and
creates a new record for each transition.To query the log:fromdjango_fsm_log.modelsimportStateLogStateLog.objects.all()# ...all recorded logs...Disabling logging for specific modelsBy default transitions get recorded for all models. Logging can be disabled for
specific models by adding their fully qualified name toDJANGO_FSM_LOG_IGNORED_MODELS.DJANGO_FSM_LOG_IGNORED_MODELS=('poll.models.Vote',)for_Manager MethodFor convenience there is a customfor_manager method to easily filter on the generic foreign key:frommy_app.modelsimportArticlefromdjango_fsm_log.modelsimportStateLogarticle=Article.objects.all()[0]StateLog.objects.for_(article)# ...logs for article...byDecoratorWe found that our transitions are commonly called by a user, so we've added a
decorator to make logging this easy:fromdjango.dbimportmodelsfromdjango_fsmimportFSMField,transitionfromdjango_fsm_log.decoratorsimportfsm_log_byclassArticle(models.Model):state=FSMField(default='draft',protected=True)@fsm_log_by@transition(field=state,source='draft',target='submitted')defsubmit(self,by=None):passWith this the transition gets logged when thebykwarg is present.article=Article.objects.create()article.submit(by=some_user)# StateLog.by will be some_userdescriptionDecoratorDecorator that allows to set a custom description (saved on database) to a transitions.fromdjango.dbimportmodelsfromdjango_fsmimportFSMField,transitionfromdjango_fsm_log.decoratorsimportfsm_log_descriptionclassArticle(models.Model):state=FSMField(default='draft',protected=True)@fsm_log_description(description='Article submitted')# description param is NOT required@transition(field=state,source='draft',target='submitted')defsubmit(self,description=None):passarticle=Article.objects.create()article.submit()# logged with "Article submitted" descriptionarticle.submit(description="Article reviewed and submitted")# logged with "Article reviewed and submitted" description.. TIP::
The "description" argument passed when calling ".submit" has precedence over the default description set in the decoratorThe decorator also accepts aallow_inlineboolean argument that allows to set the description inside the transition method.fromdjango.dbimportmodelsfromdjango_fsmimportFSMField,transitionfromdjango_fsm_log.decoratorsimportfsm_log_descriptionclassArticle(models.Model):state=FSMField(default='draft',protected=True)@fsm_log_description(allow_inline=True)@transition(field=state,source='draft',target='submitted')defsubmit(self,description=None):description.set("Article submitted")article=Article.objects.create()article.submit()# logged with "Article submitted" descriptionAdmin integrationThere is an InlineForm available that can be used to display the history of changes.To use it expand your ownAdminModelby addingStateLogInlineto its inlines:fromdjango.contribimportadminfromdjango_fsm_log.adminimportStateLogInline@admin.register(FSMModel)classFSMModelAdmin(admin.ModelAdmin):inlines=[StateLogInline]Advanced UsageYou can change the behaviour of this app by turning on caching for StateLog records.
Simply addDJANGO_FSM_LOG_STORAGE_METHOD = 'django_fsm_log.backends.CachedBackend'to your project's settings file.
It will use your project's default cache backend by default. If you wish to use a specific cache backend, you can add to
your project's settings:DJANGO_FSM_LOG_CACHE_BACKEND='some_other_cache_backend'The StateLog object is now available after thedjango_fsm.signals.pre_transitionsignal is fired, but is deleted from the cache and persisted to the database afterdjango_fsm.signals.post_transitionis fired.This is useful if:you need immediate access to StateLog details, and cannot wait untildjango_fsm.signals.post_transitionhas been firedat any stage, you need to verify whether or not the StateLog has been written to the databaseAccess to the pending StateLog record is available via thepending_objectsmanagerfromdjango_fsm_log.modelsimportStateLogarticle=Article.objects.get(...)pending_state_log=StateLog.pending_objects.get_for_object(article)ContributingRunning testspipinstalltox
toxLinting with pre-commitWe use ruff, black and more, all configured and check viapre-commit.
Before committing, run the following:pipinstallpre-commit
pre-commitinstall