Viewflow adds a Flow layer to Django’s Model-View-Template pattern. This layer manages dependencies between tasks, so your views only handle CRUD operations.
Each attribute of a Flow class represents a node. A node can be:
Nodes connect with the this object, which creates references before the
target is defined:
from viewflow import this
from viewflow.workflow import flow, lock, act
from viewflow.workflow.flow import views
class SampleFlow(flow.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):
return activation.process.completed
When a node runs, Viewflow creates an activation object and injects it as
request.activation. The activation handles:
All built-in activations have cancel() and undo() methods:
def cancel_task_view(request, **kwargs):
if not request.activation.cancel.can_proceed():
return redirect('index')
if request.method == 'POST':
request.activation.cancel()
return redirect('index')
return render(request, 'cancel_task.html')
Viewflow stores workflow state in two models:
Process - One instance per workflow executionTask - One instance per task in the workflow
When multiple users work on the same process, race conditions can happen. Viewflow uses pessimistic locking to prevent this. The lock is held during view execution and released when the view returns.
Locking is not enabled by default. To enable it:
from viewflow.workflow import lock
class SampleFlow(flow.Flow):
lock_impl = lock.select_for_update_lock
Viewflow works with both class-based and function-based views.
Each view expects process_pk and task_pk in the URL. Viewflow wraps the
view to lock the process, activate the task, and inject request.activation.
Viewflow stores only task names in the database. You can add new tasks or change connections without migrations.
To rename a task, create a data migration:
python manage.py makemigrations --empty your_app_name
operations = [
migrations.RunSQL("""
UPDATE viewflow_task SET flow_task='helloworld/flows.MyFlow.new_name'
WHERE flow_task='helloworld/flows.MyFlow.old_name'
""")
]
To remove a task but keep its history, use the Obsolete node:
from viewflow.workflow import flow
class SampleFlow(flow.Flow):
obsolete = flow.Obsolete()
This shows historical task data and lets admins cancel active obsolete tasks.
A flow class is a viewset. It combines views from all its nodes into URL patterns.
FlowViewset - Exposes a single flow with Inbox, Queue, Archive, and
dashboardWorkflowViewset - Combines multiple flows into shared Inbox, Queue, and
Archive views