# Django Finite State Machine Log
[](https://github.com/jazzband/django-fsm-log/actions/workflows/test_suite.yml)
[](https://codecov.io/gh/jazzband/django-fsm-log)
[](https://jazzband.co/)
[](https://results.pre-commit.ci/latest/github/jazzband/django-fsm-log/master)
[](https://django-fsm-log.readthedocs.io/en/latest/?badge=latest)
Provides persistence of the transitions of your fsm's models. Backed by the excellent [Django FSM](https://github.com/viewflow/django-fsm)
package.
Logs can be accessed before a transition occurs and before they are persisted to the database
by enabling a cached backend. See [Advanced Usage](#advanced-usage)
## Changelog
## 4.0.0 (not released)
## 3.1.0 (2023-03-23)
- `fsm_log_description` now accepts a default description parameter
- Document `fsm_log_description` decorator
- Add support for Django 4.1
- Add compatibility for python 3.11
### 3.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.10
- Drop support for django 1.11, 2.0, 2.1, 3.0, 3.1
- Drop support for python 3.4, 3.5, 3.6
- allow using StateLogManager in migrations [#95](https://github.com/jazzband/django-fsm-log/pull/95)
### 2.0.1 (2020-03-26)
- Add support for django3.0
- Drop support for python2
### 1.6.2 (2019-01-06)
- Address Migration history breakage added in 1.6.1
### 1.6.1 (2018-12-02)
- Make StateLog.description field nullable
### 1.6.0 (2018-11-14)
- Add source state on transitions
- Fixed `get_state_display` with FSMIntegerField (#63)
- Fixed handling of transitions if target is None (#71)
- Added `fsm_log_description` decorator (#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`.
### Compatibility
- Python 2.7 and 3.4+
- Django 1.8+
- Django-FSM 2+
## Installation
First, install the package with pip. This will automatically install any
dependencies you may be missing
```bash
pip install django-fsm-log
```
Register django_fsm_log in your list of Django applications:
```python
INSTALLED_APPS = (
...,
'django_fsm_log',
...,
)
```
Then migrate the app to create the database table
```bash
python manage.py migrate django_fsm_log
```
## Usage
The app listens for the `django_fsm.signals.post_transition` signal and
creates a new record for each transition.
To query the log:
```python
from django_fsm_log.models import StateLog
StateLog.objects.all()
# ...all recorded logs...
```
### Disabling logging for specific models
By default transitions get recorded for all models. Logging can be disabled for
specific models by adding their fully qualified name to `DJANGO_FSM_LOG_IGNORED_MODELS`.
```python
DJANGO_FSM_LOG_IGNORED_MODELS = ('poll.models.Vote',)
```
### `for_` Manager Method
For convenience there is a custom `for_` manager method to easily filter on the generic foreign key:
```python
from my_app.models import Article
from django_fsm_log.models import StateLog
article = Article.objects.all()[0]
StateLog.objects.for_(article)
# ...logs for article...
```
### `by` Decorator
We found that our transitions are commonly called by a user, so we've added a
decorator to make logging this easy:
```python
from django.db import models
from django_fsm import FSMField, transition
from django_fsm_log.decorators import fsm_log_by
class Article(models.Model):
state = FSMField(default='draft', protected=True)
@fsm_log_by
@transition(field=state, source='draft', target='submitted')
def submit(self, by=None):
pass
```
With this the transition gets logged when the `by` kwarg is present.
```python
article = Article.objects.create()
article.submit(by=some_user) # StateLog.by will be some_user
```
### `description` Decorator
Decorator that allows to set a custom description (saved on database) to a transitions.
```python
from django.db import models
from django_fsm import FSMField, transition
from django_fsm_log.decorators import fsm_log_description
class Article(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')
def submit(self, description=None):
pass
article = Article.objects.create()
article.submit() # logged with "Article submitted" description
article.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 decorator
The decorator also accepts a `allow_inline` boolean argument that allows to set the description inside the transition method.
```python
from django.db import models
from django_fsm import FSMField, transition
from django_fsm_log.decorators import fsm_log_description
class Article(models.Model):
state = FSMField(default='draft', protected=True)
@fsm_log_description(allow_inline=True)
@transition(field=state, source='draft', target='submitted')
def submit(self, description=None):
description.set("Article submitted")
article = Article.objects.create()
article.submit() # logged with "Article submitted" description
```
### Admin integration
There is an InlineForm available that can be used to display the history of changes.
To use it expand your own `AdminModel` by adding `StateLogInline` to its inlines:
```python
from django.contrib import admin
from django_fsm_log.admin import StateLogInline
@admin.register(FSMModel)
class FSMModelAdmin(admin.ModelAdmin):
inlines = [StateLogInline]
```
### Advanced Usage
You can change the behaviour of this app by turning on caching for StateLog records.
Simply add `DJANGO_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:
```python
DJANGO_FSM_LOG_CACHE_BACKEND = 'some_other_cache_backend'
```
The StateLog object is now available after the `django_fsm.signals.pre_transition`
signal is fired, but is deleted from the cache and persisted to the database after `django_fsm.signals.post_transition`
is fired.
This is useful if:
- you need immediate access to StateLog details, and cannot wait until `django_fsm.signals.post_transition`
has been fired
- at any stage, you need to verify whether or not the StateLog has been written to the database
Access to the pending StateLog record is available via the `pending_objects` manager
```python
from django_fsm_log.models import StateLog
article = Article.objects.get(...)
pending_state_log = StateLog.pending_objects.get_for_object(article)
```
## Contributing
### Running tests
```bash
pip install tox
tox
```
### Linting with pre-commit
We use ruff, black and more, all configured and check via [pre-commit](https://pre-commit.com/).
Before committing, run the following:
```bash
pip install pre-commit
pre-commit install
```
Raw data
{
"_id": null,
"home_page": "https://github.com/jazzband/django-fsm-log",
"name": "django-fsm-log",
"maintainer": "",
"docs_url": null,
"requires_python": "",
"maintainer_email": "",
"keywords": "",
"author": "Gizmag",
"author_email": "tech@gizmag.com",
"download_url": "https://files.pythonhosted.org/packages/bd/25/25296d04f9d4bb3717049a4f758f8b3ce5c6614ffea3b9504d1f6e79121f/django-fsm-log-3.1.0.tar.gz",
"platform": null,
"description": "# Django Finite State Machine Log\n\n[](https://github.com/jazzband/django-fsm-log/actions/workflows/test_suite.yml)\n[](https://codecov.io/gh/jazzband/django-fsm-log)\n[](https://jazzband.co/)\n[](https://results.pre-commit.ci/latest/github/jazzband/django-fsm-log/master)\n[](https://django-fsm-log.readthedocs.io/en/latest/?badge=latest)\n\nProvides persistence of the transitions of your fsm's models. Backed by the excellent [Django FSM](https://github.com/viewflow/django-fsm)\npackage.\n\nLogs can be accessed before a transition occurs and before they are persisted to the database\nby enabling a cached backend. See [Advanced Usage](#advanced-usage)\n\n## Changelog\n\n## 4.0.0 (not released)\n\n## 3.1.0 (2023-03-23)\n\n- `fsm_log_description` now accepts a default description parameter\n- Document `fsm_log_description` decorator\n- Add support for Django 4.1\n- Add compatibility for python 3.11\n\n### 3.0.0 (2022-01-14)\n\n- Switch to github actions (from travis-ci)\n- Test against django 3.2 and 4.0, then python 3.9 and 3.10\n- Drop support for django 1.11, 2.0, 2.1, 3.0, 3.1\n- Drop support for python 3.4, 3.5, 3.6\n- allow using StateLogManager in migrations [#95](https://github.com/jazzband/django-fsm-log/pull/95)\n\n### 2.0.1 (2020-03-26)\n\n- Add support for django3.0\n- Drop support for python2\n\n### 1.6.2 (2019-01-06)\n\n- Address Migration history breakage added in 1.6.1\n\n### 1.6.1 (2018-12-02)\n\n- Make StateLog.description field nullable\n\n### 1.6.0 (2018-11-14)\n\n- Add source state on transitions\n- Fixed `get_state_display` with FSMIntegerField (#63)\n- Fixed handling of transitions if target is None (#71)\n- Added `fsm_log_description` decorator (#1, #67)\n- Dropped support for Django 1.10 (#64)\n\n### 1.5.0 (2017-11-29)\n\n- cleanup deprecated code.\n- add codecov support.\n- switch to pytest.\n- add Admin integration to visualize past transitions.\n\n### 1.4.0 (2017-11-09)\n\n- Bring compatibility with Django 2.0 and drop support of unsupported versions\n of Django: `1.6`, `1.7`, `1.9`.\n\n### Compatibility\n\n- Python 2.7 and 3.4+\n- Django 1.8+\n- Django-FSM 2+\n\n## Installation\n\nFirst, install the package with pip. This will automatically install any\ndependencies you may be missing\n\n```bash\npip install django-fsm-log\n```\n\nRegister django_fsm_log in your list of Django applications:\n\n```python\nINSTALLED_APPS = (\n ...,\n 'django_fsm_log',\n ...,\n)\n```\n\nThen migrate the app to create the database table\n\n```bash\npython manage.py migrate django_fsm_log\n```\n\n## Usage\n\nThe app listens for the `django_fsm.signals.post_transition` signal and\ncreates a new record for each transition.\n\nTo query the log:\n\n```python\nfrom django_fsm_log.models import StateLog\nStateLog.objects.all()\n# ...all recorded logs...\n```\n\n### Disabling logging for specific models\n\nBy default transitions get recorded for all models. Logging can be disabled for\nspecific models by adding their fully qualified name to `DJANGO_FSM_LOG_IGNORED_MODELS`.\n\n```python\nDJANGO_FSM_LOG_IGNORED_MODELS = ('poll.models.Vote',)\n```\n\n### `for_` Manager Method\n\nFor convenience there is a custom `for_` manager method to easily filter on the generic foreign key:\n\n```python\nfrom my_app.models import Article\nfrom django_fsm_log.models import StateLog\n\narticle = Article.objects.all()[0]\n\nStateLog.objects.for_(article)\n# ...logs for article...\n```\n\n### `by` Decorator\n\nWe found that our transitions are commonly called by a user, so we've added a\ndecorator to make logging this easy:\n\n```python\nfrom django.db import models\nfrom django_fsm import FSMField, transition\nfrom django_fsm_log.decorators import fsm_log_by\n\nclass Article(models.Model):\n\n state = FSMField(default='draft', protected=True)\n\n @fsm_log_by\n @transition(field=state, source='draft', target='submitted')\n def submit(self, by=None):\n pass\n```\n\nWith this the transition gets logged when the `by` kwarg is present.\n\n```python\narticle = Article.objects.create()\narticle.submit(by=some_user) # StateLog.by will be some_user\n```\n\n### `description` Decorator\n\nDecorator that allows to set a custom description (saved on database) to a transitions.\n\n```python\nfrom django.db import models\nfrom django_fsm import FSMField, transition\nfrom django_fsm_log.decorators import fsm_log_description\n\nclass Article(models.Model):\n\n state = FSMField(default='draft', protected=True)\n\n @fsm_log_description(description='Article submitted') # description param is NOT required\n @transition(field=state, source='draft', target='submitted')\n def submit(self, description=None):\n pass\n\narticle = Article.objects.create()\narticle.submit() # logged with \"Article submitted\" description\narticle.submit(description=\"Article reviewed and submitted\") # logged with \"Article reviewed and submitted\" description\n```\n\n.. TIP::\n The \"description\" argument passed when calling \".submit\" has precedence over the default description set in the decorator\n\nThe decorator also accepts a `allow_inline` boolean argument that allows to set the description inside the transition method.\n\n```python\nfrom django.db import models\nfrom django_fsm import FSMField, transition\nfrom django_fsm_log.decorators import fsm_log_description\n\nclass Article(models.Model):\n\n state = FSMField(default='draft', protected=True)\n\n @fsm_log_description(allow_inline=True)\n @transition(field=state, source='draft', target='submitted')\n def submit(self, description=None):\n description.set(\"Article submitted\")\n\narticle = Article.objects.create()\narticle.submit() # logged with \"Article submitted\" description\n```\n\n### Admin integration\n\nThere is an InlineForm available that can be used to display the history of changes.\n\nTo use it expand your own `AdminModel` by adding `StateLogInline` to its inlines:\n\n```python\nfrom django.contrib import admin\nfrom django_fsm_log.admin import StateLogInline\n\n\n@admin.register(FSMModel)\nclass FSMModelAdmin(admin.ModelAdmin):\n inlines = [StateLogInline]\n```\n\n### Advanced Usage\n\nYou can change the behaviour of this app by turning on caching for StateLog records.\nSimply add `DJANGO_FSM_LOG_STORAGE_METHOD = 'django_fsm_log.backends.CachedBackend'` to your project's settings file.\nIt will use your project's default cache backend by default. If you wish to use a specific cache backend, you can add to\nyour project's settings:\n\n```python\nDJANGO_FSM_LOG_CACHE_BACKEND = 'some_other_cache_backend'\n```\n\nThe StateLog object is now available after the `django_fsm.signals.pre_transition`\nsignal is fired, but is deleted from the cache and persisted to the database after `django_fsm.signals.post_transition`\nis fired.\n\nThis is useful if:\n\n- you need immediate access to StateLog details, and cannot wait until `django_fsm.signals.post_transition`\nhas been fired\n- at any stage, you need to verify whether or not the StateLog has been written to the database\n\nAccess to the pending StateLog record is available via the `pending_objects` manager\n\n```python\nfrom django_fsm_log.models import StateLog\narticle = Article.objects.get(...)\npending_state_log = StateLog.pending_objects.get_for_object(article)\n```\n\n## Contributing\n\n### Running tests\n\n```bash\npip install tox\ntox\n```\n\n### Linting with pre-commit\n\nWe use ruff, black and more, all configured and check via [pre-commit](https://pre-commit.com/).\nBefore committing, run the following:\n\n```bash\npip install pre-commit\npre-commit install\n```\n\n\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Transition's persistence for django-fsm",
"version": "3.1.0",
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "960d45936561ddef8a714d08acfa74a72d5e0b0d4f4775cec78b4cea8b33fd24",
"md5": "e11323a20b61ad90b294134797df87f9",
"sha256": "ac4394f22659e7fb8e5ac42d1cc075490cd5a2af37202377ab2a1cb221c5f3db"
},
"downloads": -1,
"filename": "django_fsm_log-3.1.0-py3-none-any.whl",
"has_sig": false,
"md5_digest": "e11323a20b61ad90b294134797df87f9",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 14087,
"upload_time": "2023-03-23T16:49:31",
"upload_time_iso_8601": "2023-03-23T16:49:31.647469Z",
"url": "https://files.pythonhosted.org/packages/96/0d/45936561ddef8a714d08acfa74a72d5e0b0d4f4775cec78b4cea8b33fd24/django_fsm_log-3.1.0-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "bd2525296d04f9d4bb3717049a4f758f8b3ce5c6614ffea3b9504d1f6e79121f",
"md5": "67f22f42f9cee708b367d0a2413bd165",
"sha256": "9ef766f5e6d7c573d1953cf91df73538a611373cc1ef97488eff19a3f71d6ed6"
},
"downloads": -1,
"filename": "django-fsm-log-3.1.0.tar.gz",
"has_sig": false,
"md5_digest": "67f22f42f9cee708b367d0a2413bd165",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 211700,
"upload_time": "2023-03-23T16:49:35",
"upload_time_iso_8601": "2023-03-23T16:49:35.939280Z",
"url": "https://files.pythonhosted.org/packages/bd/25/25296d04f9d4bb3717049a4f758f8b3ce5c6614ffea3b9504d1f6e79121f/django-fsm-log-3.1.0.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2023-03-23 16:49:35",
"github": true,
"gitlab": false,
"bitbucket": false,
"github_user": "jazzband",
"github_project": "django-fsm-log",
"travis_ci": false,
"coveralls": false,
"github_actions": true,
"tox": true,
"lcname": "django-fsm-log"
}