Viewflow adds an additional layer to the standard Django Model-View-Template trio - the Flow Layer. This layer is responsible for managing inner-task dependencies, allowing you to extract flow logic out of Views and leaving in them only CRUD functionality.
In Viewflow, each attribute of the Flow class represents a flow task or gateway. A task could be a human action, synchronous or asynchronous Python job, or an event handler. A Gateway is a non-interactive node that decides what tasks should be activated next.
To connect flow tasks together, the special this object can be used to make references to flow nodes or flow methods before they are declared.
Here is an example of a SampleFlow class:
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
At runtime, for each node, a task activation instance is created and injected as request.activation. The Activation instance is responsible for precondition check, task and process state transition management, and next tasks instantiation. Each flow node could have its own activation implementation, with any interface you like. All built-in activations are very similar; for example, you can expect that all activations have methods like cancel() and undo() that cancel an active task and revert completed tasks, respectively.
Here is an example of a View for canceling a task:
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')
This view checks if the current task activation can be canceled and cancels it if so.
The way to link flow nodes all-together
Viewflow uses a database to store the state of flows. The state of a flow is represented by two models: Process and Task. Each Process instance corresponds to a single instance of a flow, while each Task instance represents a specific task within that flow.
Concurrency issues can occur when multiple users try to update the same fqlow instance at the same time. Viewflow uses short-term pessimistic locking on a process instance to prevent such issues. The lock is acquired before executing the entire view and handler code and is released after the code execution is complete. For background jobs, the lock is acquired only at the start and end of the job.
Note that locking is not enabled by default in Viewflow. You need to choose an appropriate lock implementation and enable it.
Here is an example of enabling locking using the select_for_update_lock implementation:
from viewflow.workflow import lock
class SampleFlow(flow.Flow):
lock_impl = lock.select_for_update_lock
Viewflow is designed to be independent of a particular view implementation, which means that both class-based views and function-based views can be used.
In Viewflow, each view expects that the process_pk and task_pk are present in the URL. Viewflow wraps each view to lock a process, activate a task, and inject the request.activation instance. This instance is used to manage precondition checks, task and process state transitions, and next task instantiations.
##TODO sample of view
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
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'
""")
]
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.workflow import flow
class SampleFlow(flow.Flow):
obsolete = flow.Obsolete()
Viewsets in Viewflow are a way to combine multiple Django views into a single class-based URL configuration.
In Viewflow, a flow class is a viewset itself, and flow nodes are also viewsets. The flow class combines views from nodes to create a comprehensive viewset.
The FlowViewset is used to expose a single flow, along with Inbox, Queue, Archive task list views, and a flow dashboard. The WorkflowViewset, on the other hand, combines tasks from multiple flows into a single Inbox, Task, and Archive list view.
Congratulations on learning the basics of Viewflow! Here are some next steps you can take to deepen your understanding and use of the library:
Explore the available nodes: Viewflow provides a variety of nodes that you can use in your flows, including gateways, subflows, and signals. Take a look at the documentation to learn more about what’s available and how to use these nodes in your own flows.
Write custom views: While Viewflow provides some default views for common tasks like task lists and task actions, you may want to write your own views to handle more complex workflows. Read the documentation on custom views to learn how to do this.
Customize templates: Viewflow provides default templates for its views, but you can customize these templates to fit your own branding and design needs. Learn more about how to do this in the documentation.
Explore integrations: Viewflow can integrate with other Django apps and services, such as Celery for background processing and Django Rest Framework for building APIs. Check out the documentation to learn more about these integrations and how to use them.