menu

Core concepts

Flow

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.

_images/ViewflowCore.png

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')

Database

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

_images/Models.png

Locking

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

Views

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.

_images/ViewflowView.png

URLs

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)),
 ]

Error handling

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.

_images/ViewflowError.png

Flow migration

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

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):
    ...

Further Options

Any web project with viewflow library is still the django project, you can use any reusable app available for the django web framework or any Python Library

_images/Pony.png

Table of Contents