Viewflow adds the additional layer to the standard Django Model-View-Template trio.
The Flow Layer is responsible for managing inner-task dependencies, allows extract flow logic out of View, leave in them only CRUD functionality.
Each attribute of the flow represents a flow task or gateway. A task could be a human action, synchronous or asynchronous Python job or even handler. Gateway is a non-interactive node that decides what tasks should be activated next.
To connect flow tasks altogether the special this object can be used to make references to flow nodes or flow methods before they are declared.
from viewflow import flow from viewflow.base import this, Flow class SampleFlow(Flow): start = flow.Start(my_view).Next(this.task) task = flow.Handler(perform_task).Next(this.check_status) check_status = flow.If(this.is_completed) \ .Then(this.end).Else(this.task) end = flow.End() def perform_task(self, activation): activation.process.completed = random.randint(0, 1) def is_completed(self, activation): retun activation.process.completed
At runtime each node is represented as a task activation instance. Activation instance is responsible for precondition check, task and process state transition management, and next tasks instantiation. Each flow node could have own activation implementation, with any interface as you like. All built-in activations are very similar, ex, you can expect that all activations have methods .cancel(), .undo(), that cancels an active task, and revert completed respectively.
@flow.flow_view def cancel_task_view(request, **kwargs): if not activation.cancel.can_proceed(): return redirect('index') if request.method == 'POST': activation.cancel() return redirect('index') return render(request, 'cancel_task.html')
In a database, state of the flows is stored in the two Process/Task models, task and process states are managed by tasks activation classes
To avoid concurrent flow updates viewflow can use short-time pessimistic locking on a process instance. Entire view and handler’s code is executed under lock. For the background jobs, lock acquired only at the start and at the end of a job.
Locking is not enabled by default. You need to choose proper lock implementation and enable it.
from viewflow import lock class SampleFlow(Flow): lock_impl = lock.select_for_update_lock
Viewflow core is independent of a particular view implementation. With viewflow, you can use both class-based views and functional based views. viewflow.flow package provides standard django template based views.
Each viewflow view expects to get flow_task and flow_class from the URL config. Task views also expect that process_pk and task_pk are present in the URL.
To get an activation in a view, you can use one of decorators shortcuts that would proceed URL parameters set the lock and instantiate process, task, and activation instances.
Flow.urls contains all views and task actions URLs and can be used directly in the URL config. viewflow.flow.viewset.FlowViewset and viewflow.rest.view.FlowViewset builds a URL config that contains list views (ex: inbox) besides. If you use the viewflow frontend, no specific URL configuration is required.
ulrpatterns = [ path('sampleflow/', SampleFlow.instance.urls) ]
or, with build-in list views:
from viewflow.flow.viewset import FlowViewSet ulrpatterns = [ path('sampleflow/', FlowViewSet(SampleFlow).urls) ]
or, in the case of viewflow frontend
from material.frontend import urls as frontend_urls urlpatterns = [ path(r'', include(frontend_urls)), ]
Viewflow provides two different strategies for an error handling. For view tasks, any exception in subsequent task activation would rollback the whole transaction, and view task will be available for end user again, for the case if they can change input data to pass.For the jobs, jobs result committed as soon as job ends. If an error happens in a subsequent task, the subsequent task will be saved in error state and available for an administrator for further processing in django admin. Error handling strategy could be customized in activation class.
Viewflow keeps only task names in the database. No action required to add a new task or change task connections.
To rename a task, you can create a django data migration, with simple SQL Update statement
migrations.RunSQL(""" UPDATE viewflow_task SET flow_task='helloworld/flows.MyFlow.new_name' WHERE flow_task='helloworld/flows.MyFlow.old_name' """)
If you would like to delete a task from flow definition but leave
database without changes, you can add a special
Obsolete node to your flow. An Obsolete node
will provide a view to seeing the historical task state, and ability
to admins to cancel active obsolete tasks. No further database content
changes are required.
from viewflow import flow class SampleFlow(Flow): obsolete = flow.Obsolete()
Viewflow frontend speeds up flow UI development as quickly as django admin does for the CRUD. Frontend provide ready you use HTML/CSS theme based on material design, and flow list and action views. To start using it, you need only turn it on and register a flow.
from viewflow import frontend @frontend.register class SampleFlow(Flow): ...