diff --git a/docs/ref/contrib/formtools/form-wizard.txt b/docs/ref/contrib/formtools/form-wizard.txt index 11a2aed091b..0449bb9cd01 100644 --- a/docs/ref/contrib/formtools/form-wizard.txt +++ b/docs/ref/contrib/formtools/form-wizard.txt @@ -47,36 +47,34 @@ Usage This application handles as much machinery for you as possible. Generally, you just have to do these things: - 1. Define a number of :mod:`django.forms` - :class:`~django.forms.forms.Form` classes -- one per wizard page. - - 2. Create a :class:`~django.contrib.formtools.wizard.FormWizard` class - that specifies what to do once all of your forms have been submitted - and validated. This also lets you override some of the wizard's behavior. - + 1. Define a number of :class:`~django.forms.Form` classes -- one per wizard + page. + + 2. Create a :class:`FormWizard` class that specifies what to do once all of + your forms have been submitted and validated. This also lets you + override some of the wizard's behavior. + 3. Create some templates that render the forms. You can define a single, generic template to handle every one of the forms, or you can define a specific template for each form. - - 4. Point your URLconf at your - :class:`~django.contrib.formtools.wizard.FormWizard` class. + + 4. Point your URLconf at your :class:`FormWizard` class. Defining ``Form`` classes ========================= -The first step in creating a form wizard is to create the :class:`~django.forms.forms.Form` classes. -These should be standard :mod:`django.forms` -:class:`~django.forms.forms.Form` classes, covered in the -:ref:`forms documentation `. - -These classes can live anywhere in your codebase, but convention is to put them -in a file called :file:`forms.py` in your application. +The first step in creating a form wizard is to create the +:class:`~django.forms.Form` classes. These should be standard +:class:`django.forms.Form` classes, covered in the :ref:`forms documentation +`. These classes can live anywhere in your codebase, but +convention is to put them in a file called :file:`forms.py` in your +application. For example, let's write a "contact form" wizard, where the first page's form collects the sender's e-mail address and subject, and the second page collects the message itself. Here's what the :file:`forms.py` might look like:: - from django import forms + from django import forms class ContactForm1(forms.Form): subject = forms.CharField(max_length=100) @@ -86,27 +84,27 @@ the message itself. Here's what the :file:`forms.py` might look like:: message = forms.CharField(widget=forms.Textarea) **Important limitation:** Because the wizard uses HTML hidden fields to store -data between pages, you may not include a :class:`~django.forms.fields.FileField` +data between pages, you may not include a :class:`~django.forms.FileField` in any form except the last one. Creating a ``FormWizard`` class =============================== -The next step is to create a :class:`~django.contrib.formtools.wizard.FormWizard` -class, which should be a subclass of ``django.contrib.formtools.wizard.FormWizard``. - -As with your :class:`~django.forms.forms.Form` classes, this -:class:`~django.contrib.formtools.wizard.FormWizard` class can live anywhere -in your codebase, but convention is to put it in :file:`forms.py`. +The next step is to create a +:class:`django.contrib.formtools.wizard.FormWizard` subclass. As with your +:class:`~django.forms.Form` classes, this :class:`FormWizard` class can live +anywhere in your codebase, but convention is to put it in :file:`forms.py`. The only requirement on this subclass is that it implement a -:meth:`~django.contrib.formtools.wizard.FormWizard.done()` method, -which specifies what should happen when the data for *every* form is submitted -and validated. This method is passed two arguments: +:meth:`~FormWizard.done()` method. - * ``request`` -- an :class:`~django.http.HttpRequest` object - * ``form_list`` -- a list of :mod:`django.forms` - :class:`~django.forms.forms.Form` classes +.. method:: FormWizard.done + + This method specifies what should happen when the data for *every* form is + submitted and validated. This method is passed two arguments: + + * ``request`` -- an :class:`~django.http.HttpRequest` object + * ``form_list`` -- a list of :class:`~django.forms.Form` classes In this simplistic example, rather than perform any database operation, the method simply renders a template of the validated data:: @@ -133,16 +131,16 @@ example:: return HttpResponseRedirect('/page-to-redirect-to-when-done/') See the section `Advanced FormWizard methods`_ below to learn about more -:class:`~django.contrib.formtools.wizard.FormWizard` hooks. +:class:`FormWizard` hooks. Creating templates for the forms ================================ Next, you'll need to create a template that renders the wizard's forms. By default, every form uses a template called :file:`forms/wizard.html`. (You can -change this template name by overriding -:meth:`~django.contrib.formtools.wizard..get_template()`, which is documented -below. This hook also allows you to use a different template for each form.) +change this template name by overriding :meth:`~FormWizard.get_template()`, +which is documented below. This hook also allows you to use a different +template for each form.) This template expects the following context: @@ -150,24 +148,20 @@ This template expects the following context: * ``step0`` -- The current step (zero-based). * ``step`` -- The current step (one-based). * ``step_count`` -- The total number of steps. - * ``form`` -- The :class:`~django.forms.forms.Form` instance for the - current step (either empty or with errors). + * ``form`` -- The :class:`~django.forms.Form` instance for the current step + (either empty or with errors). * ``previous_fields`` -- A string representing every previous data field, plus hashes for completed forms, all in the form of hidden fields. Note - that you'll need to run this through the - :meth:`~django.template.defaultfilters.safe` template filter, to prevent - auto-escaping, because it's raw HTML. + that you'll need to run this through the :tfilter:`safe` template filter, + to prevent auto-escaping, because it's raw HTML. -It will also be passed any objects in :data:`extra_context`, which is a -dictionary you can specify that contains extra values to add to the context. -You can specify it in two ways: +You can supply extra context to this template in two ways: - * Set the :attr:`~django.contrib.formtools.wizard.FormWizard.extra_context` - attribute on your :class:`~django.contrib.formtools.wizard.FormWizard` - subclass to a dictionary. + * Set the :attr:`~FormWizard.extra_context` attribute on your + :class:`FormWizard` subclass to a dictionary. - * Pass :attr:`~django.contrib.formtools.wizard.FormWizard.extra_context` - as extra parameters in the URLconf. + * Pass a dictionary as a parameter named ``extra_context`` to your wizard's + URL pattern in your URLconf. See :ref:`hooking-wizard-into-urlconf`. Here's a full example template: @@ -190,12 +184,13 @@ Here's a full example template: Note that ``previous_fields``, ``step_field`` and ``step0`` are all required for the wizard to work properly. +.. _hooking-wizard-into-urlconf: + Hooking the wizard into a URLconf ================================= -Finally, give your new :class:`~django.contrib.formtools.wizard.FormWizard` -object a URL in ``urls.py``. The wizard takes a list of your form objects as -arguments:: +Finally, give your new :class:`FormWizard` object a URL in ``urls.py``. The +wizard takes a list of your :class:`~django.forms.Form` objects as arguments:: from django.conf.urls.defaults import * from mysite.testapp.forms import ContactForm1, ContactForm2, ContactWizard @@ -209,19 +204,18 @@ Advanced FormWizard methods .. class:: FormWizard - Aside from the :meth:`~django.contrib.formtools.wizard.FormWizard.done()` - method, :class:`~django.contrib.formtools.wizard.FormWizard` offers a few + Aside from the :meth:`~done()` method, :class:`FormWizard` offers a few advanced method hooks that let you customize how your wizard works. - Some of these methods take an argument ``step``, which is a zero-based counter - representing the current step of the wizard. (E.g., the first form is ``0`` and - the second form is ``1``.) + Some of these methods take an argument ``step``, which is a zero-based + counter representing the current step of the wizard. (E.g., the first form + is ``0`` and the second form is ``1``.) .. method:: FormWizard.prefix_for_step - Given the step, returns a :class:`~django.forms.forms.Form` prefix to - use. By default, this simply uses the step itself. For more, see the - :ref:`form prefix documentation `. + Given the step, returns a form prefix to use. By default, this simply uses + the step itself. For more, see the :ref:`form prefix documentation + `. Default implementation:: @@ -237,15 +231,18 @@ Advanced FormWizard methods def render_hash_failure(self, request, step): return self.render(self.get_form(step), request, step, - context={'wizard_error': 'We apologize, but your form has expired. Please continue filling out the form from this page.'}) + context={'wizard_error': + 'We apologize, but your form has expired. Please' + ' continue filling out the form from this page.'}) .. method:: FormWizard.security_hash - Calculates the security hash for the given request object and :class:`~django.forms.forms.Form` instance. + Calculates the security hash for the given request object and + :class:`~django.forms.Form` instance. By default, this uses an MD5 hash of the form data and your - :setting:`SECRET_KEY` setting. It's rare that somebody would need to override - this. + :setting:`SECRET_KEY` setting. It's rare that somebody would need to + override this. Example:: @@ -254,8 +251,8 @@ Advanced FormWizard methods .. method:: FormWizard.parse_params - A hook for saving state from the request object and ``args`` / ``kwargs`` that - were captured from the URL by your URLconf. + A hook for saving state from the request object and ``args`` / ``kwargs`` + that were captured from the URL by your URLconf. By default, this does nothing. @@ -275,26 +272,23 @@ Advanced FormWizard methods def get_template(self, step): return 'myapp/wizard_%s.html' % step - If :meth:`~FormWizard.get_template` returns a list of strings, then the wizard will use the - template system's :func:`~django.template.loader.select_template()` - function, - :ref:`explained in the template docs `. + If :meth:`~FormWizard.get_template` returns a list of strings, then the + wizard will use the template system's + :func:`~django.template.loader.select_template` function. This means the system will use the first template that exists on the filesystem. For example:: def get_template(self, step): return ['myapp/wizard_%s.html' % step, 'myapp/wizard.html'] - .. _explained in the template docs: ../templates_python/#the-python-api - .. method:: FormWizard.render_template Renders the template for the given step, returning an :class:`~django.http.HttpResponse` object. - Override this method if you want to add a custom context, return a different - MIME type, etc. If you only need to override the template name, use - :meth:`~FormWizard.get_template` instead. + Override this method if you want to add a custom context, return a + different MIME type, etc. If you only need to override the template name, + use :meth:`~FormWizard.get_template` instead. The template will be rendered with the context documented in the "Creating templates for the forms" section above. @@ -302,12 +296,12 @@ Advanced FormWizard methods .. method:: FormWizard.process_step Hook for modifying the wizard's internal state, given a fully validated - :class:`~django.forms.forms.Form` object. The Form is guaranteed to - have clean, valid data. + :class:`~django.forms.Form` object. The Form is guaranteed to have clean, + valid data. - This method should *not* modify any of that data. Rather, it might want to set - ``self.extra_context`` or dynamically alter ``self.form_list``, based on - previously submitted forms. + This method should *not* modify any of that data. Rather, it might want to + set ``self.extra_context`` or dynamically alter ``self.form_list``, based + on previously submitted forms. Note that this method is called every time a page is rendered for *all* submitted steps.