e8524a70f9
removed a before detailed API information - unnecessary changed are to is after data Change-Id: Iffc0a15d7f24923e7faf99b17e3ce4c769c9ee8f Closes-Bug: #1401194
135 lines
5.5 KiB
ReStructuredText
135 lines
5.5 KiB
ReStructuredText
======================
|
|
Workflows Topic Guide
|
|
======================
|
|
|
|
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.
|
|
|
|
.. seealso::
|
|
|
|
For detailed API information refer to the :doc:`Workflows Reference
|
|
Guide </ref/workflows>`.
|
|
|
|
Workflows
|
|
=========
|
|
|
|
Workflows are complex forms with tabs, each workflow must consist of classes
|
|
extending the :class:`~horizon.workflows.Workflow`,
|
|
:class:`~horizon.workflows.Step` and :class:`~horizon.workflows.Action`
|
|
|
|
Complex example of a workflow
|
|
------------------------------
|
|
|
|
The following is a complex example of how data is 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.
|