{"id":635,"date":"2017-05-15T18:00:00","date_gmt":"2017-05-15T18:00:00","guid":{"rendered":"https:\/\/geoadmin.com.br\/?p=635"},"modified":"2021-06-09T10:24:16","modified_gmt":"2021-06-09T13:24:16","slug":"django-workflow","status":"publish","type":"post","link":"https:\/\/geoadmin.com.br\/?p=635","title":{"rendered":"Django Workflow"},"content":{"rendered":"<p>Voc\u00ea, desenvolvedor, j\u00e1 se deparou com cen\u00e1rios espec\u00edficos em que voc\u00ea precisa controlar um fluxo de informa\u00e7\u00f5es, mas, suportando diversos status, com diversos efeitos colaterais diferentes, entre cada mudan\u00e7a desses status?<\/p>\n<p>Criar e manter este tipo de estrutura, manualmente, \u00e9 bem complicado. Formalmente, este tipo de estrutura \u00e9 chamada de m\u00e1quina de estado finita ou <strong>finite state machines<\/strong> e para os \u00edntimos, <strong>FSM<\/strong>.<\/p>\n<p>Inspirados em outros pacotes dispon\u00edveis para o Django, constru\u00edmos nosso pr\u00f3prio gerenciador de m\u00e1quinas de estados. Este \u00e9 mais um pacote open-source que disponibilizamos para a comunidade.<\/p>\n<h2>Aplica\u00e7\u00f5es das FSM<\/h2>\n<p>As aplica\u00e7\u00f5es das <strong>FSMs<\/strong> s\u00e3o diversas. Elas podem controlar como compoenentes internos de uma aplica\u00e7\u00e3o reagem a est\u00edmulos externos (usu\u00e1rio informa determinada informa\u00e7\u00e3o) ou mesmo serem editadas pelo pr\u00f3prio usu\u00e1rio. Um exemplo disso s\u00e3o os famigerados <strong>BPM<\/strong> (Bussiness Process Management**) onde os pr\u00f3prios usu\u00e1rios definem at\u00e9 certo ponto, o fluxo da informa\u00e7\u00e3o e por quais verifica\u00e7\u00f5es esta deve passar antes de permitir a troca de um estado para outro.<\/p>\n<p>Outro exemplo interessante \u00e9 que algumas AIs s\u00e3o escritas usando os conceitos de FSM para determinar comportamentos de seus agentes, portanto, te garanto, FSMs s\u00e3o bastante flex\u00edveis.<\/p>\n<p>Na realidade, a ideia desta aplica\u00e7\u00e3o Django surgiu para atender uma demanda, onde os usu\u00e1rios mais graduados, deveriam poder escolher como a informa\u00e7\u00e3o fluiria numa aplica\u00e7\u00e3o geogr\u00e1fica.<\/p>\n<h2>Instala\u00e7\u00e3o<\/h2>\n<p>Para instalar este pacote \u00e9 bem simples e voc\u00ea pode usar o pip:<\/p>\n<pre><code>pip install django-workflow-fsm\n<\/code><\/pre>\n<p>Agora adicione este pacote ao seu <code>INSTALLED_APPS<\/code>:<\/p>\n<pre><code>INSTALLED_APPS = (\n    # outras apps,\n    'workflow',\n    # outras apps,\n)\n<\/code><\/pre>\n<p>Execute suas migra\u00e7\u00f5es com <code>.\/manage.py migrate<\/code><\/p>\n<h2>Getting Started<\/h2>\n<p>Terminado isto, voc\u00ea precisa definir qual \u00e9 o modelo que voc\u00ea deseja controlar o status. Basta voc\u00ea herdar de um <strong>MixIn<\/strong> para obter a funcionalidade de m\u00e1quina de status:<\/p>\n<pre><code># models.py\nclass Projeto(StateControllerMixIn):\n    nome = models.CharField(max_length=128)\n<\/code><\/pre>\n<p>Pronto. A configura\u00e7\u00e3o b\u00e1sica est\u00e1 pronta!<\/p>\n<p>Depois disso, com toda a certeza, voc\u00ea deve querer editar quais status e como a informa\u00e7\u00e3o flui entre estes status. Isto \u00e9 bem f\u00e1cil.<\/p>\n<p>N\u00f3s controlamos o fluxo de informa\u00e7\u00f5es utilizando tr\u00eas modelos:<\/p>\n<ul>\n<li>StateMachine;<\/li>\n<li>State<\/li>\n<li>Transition;<\/li>\n<\/ul>\n<p>Vamos construir isso em um passo a passo r\u00e1pido:<\/p>\n<pre><code># shell ou fixture ou migra\u00e7\u00e3o de dados\nstatus_aberto = State.objects.create(code='aberto')\nstatus_em_andamento = State.objects.create(code='em-andamento')\nstatus_fechado = State.objects.create(code='fechado')\n\n# nossos status est\u00e3o criados. Vamos criar nossa m\u00e1quina de estado\n\nfsm = StateMachine.objects.create(name='projetos-simples', initial_state=status_aberto)\n# na linha acima definimos nossa m\u00e1quina e o status inicial dela, aberto.\n\n# agora vamos definir as transicoes\naberto_andamento = Transition.objects.create(name='iniciando-projeto',\n    machine=fsm,\n    from_state=status_aberto,\n    to_state=status_em_andamento)\n\nandamento_fechado = Transition.objects.create(name='finalizando-projeto',\n    machine=fsm,\n    from_state=status_em_andamento,\n    to_state=status_fechado)\n<\/code><\/pre>\n<p>Agora que nossas transi\u00e7\u00f5es est\u00e3o prontas, voc\u00ea pode definir qualquer projeto, com diferentes tipos de m\u00e1quinas de estado, basta escolher a m\u00e1quina de estado apropriada, veja s\u00f3:<\/p>\n<pre><code>projeto = Projeto()\nprojeto.nome = 'projeto legal'\nprojeto.save(state_machine=fsm)\n<\/code><\/pre>\n<p>Neste momento, todas as a\u00e7\u00f5es da m\u00e1quina de estado est\u00e3o dispon\u00edveis atrav\u00e9s do mixin que voc\u00ea herdou na constru\u00e7\u00e3o do modelo Projeto.<\/p>\n<p>Exemplo:<\/p>\n<pre><code>projeto.current_state\n# imprime \"aberto\"\nprojeto.next\n# imprime \"em andamento\"\nprojeto.change_to(state_em_andamento)\n# projeto ser\u00e1 enviado para o estado \"em andamento\"\nprojeto.next\n# imprime 'fechado'\n<\/code><\/pre>\n<p>N\u00e3o \u00e9 s\u00f3 isso, existem diversos ganchos que voc\u00ea pode usar, como tarefas espec\u00edficas a serem disparadas e a\u00e7\u00f5es, nas quais voc\u00ea pode associar a um estado, para indicar que esta a\u00e7\u00e3o est\u00e1 dispon\u00edvel. Exemplo: no status em andamento do projeto, voc\u1ebd pode criar coment\u00e1rios e apenas neste estado. Portanto, voc\u00ea pode criar uma <code>Action<\/code> e associ\u00e1-la ao estado <code>em-andamento<\/code>.<\/p>\n<p>No seu c\u00f3digo, voc\u1ebd pode checar qual <code>action<\/code> est\u00e1 dispon\u00edvel e renderizar o template como voc\u00ea achar melhor.<\/p>\n<h2>Outras coisas legais:<\/h2>\n<ul>\n<li>Voc\u00ea pode associar permiss\u00f5es a cada estado, ou seja, apenas usu\u00e1rios com determinadas permiss\u00f5es podem trocar o estado da m\u00e1quina;<\/li>\n<li>Voc\u00ea pode associar <code>tasks<\/code> (ou tarefas) que ser\u00e3o executadas quando um estado da m\u00e1quina \u00e9 alterado. Por exemplo, quando a m\u00e1quina de estado mudar de estado, quero disparar um email para um usu\u00e1rio, informando do ocorrido. Voc\u00ea pode criar isso como uma <code>Task<\/code> e associar esta task a transi\u00e7\u00e3o espec\u00edfica.<\/li>\n<li>Suporte para tarefas ass\u00edncronas. Por padr\u00e3o, as tarefas s\u00e3o executadas usando o Celery, que \u00e9 uma depend\u00eancia do projeto. Caso voc\u00ea n\u00e3o queira executar estas tarefas de forma ass\u00edcrona, basta desabilitar o <code>Celery<\/code>.<\/li>\n<li>Suporte completo para API REST, usando <code>django-rest-framework<\/code><\/li>\n<\/ul>\n<h2>Reposit\u00f3rio<\/h2>\n<p>Dispon\u00edvel no Github: https:\/\/github.com\/sigma-geosistemas\/django-workflow<\/p>\n<h2>Roadmap<\/h2>\n<ul>\n<li>Suporte completo para tarefas ass\u00edncronas e s\u00edncronas (hoje s\u00f3 suportamos um modo, queriamos suportar os dois);<\/li>\n<li>Melhorar a infraestrutura de testes;<\/li>\n<li>Melhor\/criar um help\/ajuda\/getting started;<\/li>\n<li>Outras coisitas;<\/li>\n<\/ul>\n<p>Este \u00e9 um pacote bem completo para gest\u00e3o de m\u00e1quinas de estado. Caso voc\u00ea tenha interesse, d\u00ea uma conferida. Estamos a disposi\u00e7\u00e3o!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Voc\u00ea, desenvolvedor, j\u00e1 se deparou com cen\u00e1rios espec\u00edficos em que voc\u00ea precisa controlar um fluxo de informa\u00e7\u00f5es, mas, suportando diversos status, com diversos efeitos colaterais diferentes, entre cada mudan\u00e7a desses status? Criar e manter este tipo de estrutura, manualmente, \u00e9 bem complicado. Formalmente, este tipo de estrutura \u00e9 chamada de m\u00e1quina de estado finita ou &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/geoadmin.com.br\/?p=635\" class=\"more-link\">Leia mais<span class=\"screen-reader-text\"> &#8220;Django Workflow&#8221;<\/span><\/a><\/p>\n","protected":false},"author":4,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_post_was_ever_published":false},"categories":[1],"tags":[51,30,29,56,53,45,52],"class_list":["post-635","post","type-post","status-publish","format-standard","hentry","category-sem-categoria","tag-agil","tag-desenvolvimento-de-sistemas","tag-dev","tag-devops","tag-django","tag-python","tag-scrum"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=\/wp\/v2\/posts\/635","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=\/wp\/v2\/users\/4"}],"replies":[{"embeddable":true,"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=635"}],"version-history":[{"count":1,"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=\/wp\/v2\/posts\/635\/revisions"}],"predecessor-version":[{"id":1313,"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=\/wp\/v2\/posts\/635\/revisions\/1313"}],"wp:attachment":[{"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=635"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=635"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/geoadmin.com.br\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=635"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}