Государственные Машины И Джанго

При работе над проектом Django необходимо иметь ряд сторонних библиотек, если вы не хотите бесконечно изобретать велосипед. Инструмент для отладки sql-запросов (debug-toolbar, Silk, --print-sql из django-extensions), что-то для хранения древовидных структур, периодических/отложенных задач (кстати, cron-подобный интерфейс в uswgi есть это .

ЭАВ по-прежнему необходим, хотя его часто можно заменить jsonfield. И одна из таких чрезвычайно полезных вещей, но почему-то реже обсуждаемых в Интернете, — это FSM. Почему-то я не часто встречаю их в чужом коде.

Почти каждая запись в базе данных имеет какое-то состояние.

Например, для комментария это может быть - опубликовано/удалено/удалено модератором.

Для заказа в магазине - оформлен/оплачен/доставлен/возвращен и т.д. Причем переход из одного состояния в другое часто разложен в коде и в нем есть бизнес-логика, которую нужно обильно покрывать тестами( вам все равно придется, но вы можете избежать тестирования базовых вещей, например того, что заказ может перейти в состояние «возврат» только после того, как он перешел в состояние «оплачено».

Было бы вполне логично описать подобные переходы более декларативно и в одном месте.

Вместе с необходимой логикой и проверками доступа.

Вот пример кода из тестов библиотеки django-fsm.

   

class BlogPost(models.Model): """ Test workflow """ state = FSMField(default='new', protected=True) def can_restore(self, user): return user.is_superuser or user.is_staff @transition(field=state, source='new', target='published', on_error='failed', permission='testapp.can_publish_post') def publish(self): pass @transition(field=state, source='published') def notify_all(self): pass @transition(field=state, source='published', target='hidden', on_error='failed',) def hide(self): pass @transition( field=state, source='new', target='removed', on_error='failed', permission=lambda self, u: u.has_perm('testapp.can_remove_post')) def remove(self): raise Exception('No rights to delete %s' % self) @transition(field=state, source='new', target='restored', on_error='failed', permission=can_restore) def restore(self): pass @transition(field=state, source=['published', 'hidden'], target='stolen') def steal(self): pass @transition(field=state, source='*', target='moderated') def moderate(self): pass class Meta: permissions = [ ('can_publish_post', 'Can publish post'), ('can_remove_post', 'Can remove post'), ]

Помимо прочего, это идеально подходит для остального API. Мы можем автоматически создавать конечные точки для переходов между состояниями.

Например, запрос /orders/id/cancel кажется логичным действием для набора представлений.

И у нас уже есть необходимая информация для проверки доступа! А ещё за кнопки в админке, и возможность рисовать красивые диаграммы с рабочим процессом :) Есть даже визуальные редакторы рабочего процесса, т.е.

непрограммисты могут описывать бизнес-процессы Чем более декларативный и обобщенный код мы пишем, тем он надежнее.

Меньше кода, меньше дублирования, меньше ошибок.

Тестирование частично перекладывается на автора библиотеки, и вы можете сосредоточиться на уникальной для проекта бизнес-логике.

Теги: #python #django #fsm #django rest framework

Вместе с данным постом часто просматривают: