Управление конечным автоматом с использованием Django FSM

Spread the love

Представьте себе приложение типа JIRA со сложным рабочим процессом. Для создания такого приложения требуется поддержка управления конечным автоматом. Если вы создаете свое приложение с помощью Django, Django FSM предоставляет вам готовую поддержку для управления конечным автоматом.

Предположим, что наше приложение имеет следующий рабочий процесс выполнение Task.

workflow

Основная модель для 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


Spread the love

Добавить комментарий

Ваш e-mail не будет опубликован.