Управление конечным автоматом с использованием Django FSM
Представьте себе приложение типа JIRA со сложным рабочим процессом. Для создания такого приложения требуется поддержка управления конечным автоматом. Если вы создаете свое приложение с помощью Django, Django FSM предоставляет вам готовую поддержку для управления конечным автоматом.
Предположим, что наше приложение имеет следующий рабочий процесс выполнение Task.
Основная модель для Task будет следующая:
from django.db import models class Task(models.Model): title = models.CharField(max_length=100, null=False) description = models.TextField()
Теперь давайте посмотрим, как мы можем выстроить этот рабочий процесс с помощью Django FSM.
Установка django-fsm
$ pip install django-fsm
Или вы можете установить последнюю версию с git
$ pip install -e git://github.com/kmmbvnr/django-fsm.git#egg=django-fsm
Определение состояния и поле состояния в модели
Определим поле в модели как FSMState. Через это поле Django FSM будет выполнят переходы.
from django.db import models from django_fsm import FSMField class Task(models.Model): title = models.CharField(max_length=100, null=False) description = models.TextField() state = FSMField()
Мы определили поле FSMState, но пока еще мы не связывали с ним никаких состояний с этим полем. Мы можем сделать это следующим образом:
from django.db import models from django_fsm import FSMField STATES = ('Open', 'In Progress', 'Resolved', 'Re Opened', 'Closed') STATES = list(zip(STATES, STATES)) class Task(models.Model): title = models.CharField(max_length=100, null=False) description = models.TextField() state = FSMField(default=STATES[0], choices=STATES)
Создание переходов (Transitions)
Django FSM предоставляет декоратор transition. После того, как вы декорируете метод модели с помощью этого декоратора, вы можете вызвать метод модели для выполнения перехода на каждом объекте модели.
from django.db import models from django_fsm import FSMField, transition STATES = ('Open', 'In Progress', 'Resolved', 'Re Opened', 'Closed') STATES = list(zip(STATES, STATES)) class Task(models.Model): ... state = FSMField(default=STATES[0], choices=STATES) @transition(field=state, source=['Open', 'Re Opened'], target='In Progress') def start(self): """ Этот метод будет содержать действие, которое необходимо предпринять после того как измениться state. Например, уведомление пользователей. """ pass @transition(field=state, source='In Progress', target='Resolved') def resolve(self): pass
Параметр source принимает список состояний или отдельное состояние из которого возможен переход. Вы можете использовать * для source, чтобы разрешить переключение на цель (target) из любого состояния. Параметр field принимает как имя строкового атрибута, так и фактический экземпляр поля.
Изменение состояния объекта
Чтобы изменить состояние объекта, вызовите метод модели, который декорирован декоратором transition.
task = Task.objects.get(pk=task_id) task.start()
Если start() успешно выполнено без каких-либо исключений, состояние объекта задачи будет обновлено, но не будет сохранено в базе данных. Вызовите метод save(), чтобы сохранить объект в базе данных.
task.save()
Добавление условий на переходы (Transitions)
Могут быть ситуации, когда вы захотите проверить определенное условие перед выполнением перехода. Для этого в Django FSM, используется параметр conditions декоратора transition. Conditions должен быть списком функций, принимающих один аргумент (экземпляр модели). Функция/метод должны возвращать значение True или False.
Метод conditions может быть обычной функцией Python.
def can_close(instance): """ Return True or False, depending upon some condition """ pass
или это может быть методом модели.
def can_close(self): """ Return True or False, depending upon some conditions """ pass
conditions используется таким образом:
@transition(field=state, source='Resolved', target='Closed', conditions=[can_close]) def close(self): """ This method will contain the action that needs to be taken once the state is changed. """ pass
Обработка исключений в методе перехода
Django FSM предоставляет способ установить резервное состояние в случае, если метод перехода вызывает исключение.
@transition(field=state, source='Resolved', target='Closed', on_error='failed') def close(self): """ Some exception could happen here """ pass
Переход на основе разрешений
Декоратор transition имеет параметр permission, который можно использовать для проверки разрешения перед выполнением перехода. Permission принимает строку разрешения или вызываемую функцию, которая ожидает аргументы экземпляра и пользователя и возвращает True, если пользователь может выполнить переход.
@transition(field=state, source='Resolved', target='Closed', permission='myapp.can_change_task') def close(self): """ This method will contain the action that needs to be taken once state is changed. """ pass
или
@transition(field=state, source='Resolved', target='Closed', permission=lambda instance, user: not user.has_perm('myapp.can_change_task')) def close(self): """ This method will contain the action that needs to be taken once the state is changed. """ pass
Вы можете проверить разрешение с помощью метода has_transition_permission.
from django_fsm import has_transition_perm def publish_view(request, task_id): task = get_object_or_404(Task, pk=task_id) if not has_transition_perm(task.close, request.user): raise PermissionDenied task.close()
Диаграмма переходного состояния
Вы можете сгенерировать диаграмму перехода состояний на основе методов перехода.
Вам понадобится pip install graphviz> = 0.4 а так же не забудьте добавьте django_fsm в вашу INSTALLED_APPS
INSTALLED_APPS = ( ... 'django_fsm', ... )
Теперь выполните следующую команду.
$ ./manage.py graph_transitions -o task_transitions.png myapp.Task
Заключение
Django FSM имеет все, что вам нужно для управления конечным автоматом в вашем приложении. В дополнение к этому, если вы хотите выполнять переходы с панель администратора, используйте Django-FSM-admin. Также вы можете вести журналы переходов, используя Django-FSM-log.
Оригинальная статья: A Guide to Managing Finite State Machine Using Django FSM