One of the most challenging aspects of building a compelling user experience is crafting complex multi-part workflows. Horizon’s workflows module aims to bring that capability within everyday reach.
See also
For a detailed API information check out the Workflows Reference Guide.
Workflows are complex forms with tabs, each workflow must consist of classes extending the Workflow, Step and Action
The following is a complex example of how data are exchanged between urls, views, workflows and templates:
In urls.py, we have the named parameter. E.g. resource_class_id.
RESOURCE_CLASS = r'^(?P<resource_class_id>[^/]+)/%s$'
urlpatterns = patterns(
'',
url(RESOURCE_CLASS % 'update', UpdateView.as_view(), name='update'))
In views.py, we pass data to the template and to the action(form) (action can also pass data to the get_context_data method and to the template).
class UpdateView(workflows.WorkflowView):
workflow_class = UpdateResourceClass
def get_context_data(self, **kwargs):
context = super(UpdateView, self).get_context_data(**kwargs)
# Data from URL are always in self.kwargs, here we pass the data
# to the template.
context["resource_class_id"] = self.kwargs['resource_class_id']
# Data contributed by Workflow's Steps are in the
# context['workflow'].context list. We can use that in the
# template too.
return context
def _get_object(self, *args, **kwargs):
# Data from URL are always in self.kwargs, we can use them here
# to load our object of interest.
resource_class_id = self.kwargs['resource_class_id']
# Code omitted, this method should return some object obtained
# from API.
def get_initial(self):
resource_class = self._get_object()
# This data will be available in the Action's methods and
# Workflow's handle method.
# But only if the steps will depend on them.
return {'resource_class_id': resource_class.id,
'name': resource_class.name,
'service_type': resource_class.service_type}
In workflows.py we process the data, it is just more complex django form.
class ResourcesAction(workflows.Action):
# The name field will be automatically available in all action's
# methods.
# If we want this field to be used in the another Step or Workflow,
# it has to be contributed by this step, then depend on in another
# step.
name = forms.CharField(max_length=255,
label=_("Testing Name"),
help_text="",
required=True)
def handle(self, request, data):
pass
# If we want to use some data from the URL, the Action's step
# has to depend on them. It's then available in
# self.initial['resource_class_id'] or data['resource_class_id'].
# In other words, resource_class_id has to be passed by view's
# get_initial and listed in step's depends_on list.
# We can also use here the data from the other steps. If we want
# the data from the other step, the step needs to contribute the
# data and the steps needs to be ordered properly.
class UpdateResources(workflows.Step):
# This passes data from Workflow context to action methods
# (handle, clean). Workflow context consists of URL data and data
# contributed by other steps.
depends_on = ("resource_class_id",)
# By contributing, the data on these indexes will become available to
# Workflow and to other Steps (if they will depend on them). Notice,
# that the resources_object_ids key has to be manually added in
# contribute method first.
contributes = ("resources_object_ids", "name")
def contribute(self, data, context):
# We can obtain the http request from workflow.
request = self.workflow.request
if data:
# Only fields defined in Action are automatically
# available for contribution. If we want to contribute
# something else, We need to override the contribute method
# and manually add it to the dictionary.
context["resources_object_ids"] =\
request.POST.getlist("resources_object_ids")
# We have to merge new context with the passed data or let
# the superclass do this.
context.update(data)
return context
class UpdateResourceClass(workflows.Workflow):
default_steps = (UpdateResources,)
def handle(self, request, data):
pass
# This method is called as last (after all Action's handle
# methods). All data that are listed in step's 'contributes='
# and 'depends_on=' are available here.
# It can be easier to have the saving logic only here if steps
# are heavily connected or complex.
# data["resources_object_ids"], data["name"] and
# data["resources_class_id"] are available here.