Core Concepts

Flow and Nodes

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:

  • A human task (someone fills a form)
  • A Python function (runs synchronously or in background)
  • A gateway (decides which path to take next)

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

Activations

When a node runs, Viewflow creates an activation object and injects it as request.activation. The activation handles:

  • Checking if the task can run
  • Managing task and process state
  • Creating the next tasks

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

Database

Viewflow stores workflow state in two models:

  • Process - One instance per workflow execution
  • Task - One instance per task in the workflow
../_images/Models.png

Locking

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

Views

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.

../_images/Views.png

Flow Migration

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.

Viewsets

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 dashboard
  • WorkflowViewset - Combines multiple flows into shared Inbox, Queue, and Archive views

What’s Next

  • Nodes: Learn about gates, subflows, and other node types
  • Custom views: Write your own task views
  • Templates: Customize the UI
  • Integrations: Connect to Celery for background jobs