summaryrefslogtreecommitdiff
path: root/parts/django/docs/ref/contrib/formtools/form-wizard.txt
diff options
context:
space:
mode:
authorNishanth Amuluru2011-01-08 11:20:57 +0530
committerNishanth Amuluru2011-01-08 11:20:57 +0530
commit65411d01d448ff0cd4abd14eee14cf60b5f8fc20 (patch)
treeb4c404363c4c63a61d6e2f8bd26c5b057c1fb09d /parts/django/docs/ref/contrib/formtools/form-wizard.txt
parent2e35094d43b4cc6974172e1febf76abb50f086ec (diff)
downloadpytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.tar.gz
pytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.tar.bz2
pytask-65411d01d448ff0cd4abd14eee14cf60b5f8fc20.zip
Added buildout stuff and made changes accordingly
--HG-- rename : profile/management/__init__.py => eggs/djangorecipe-0.20-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/djangorecipe-0.20-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/infrae.subversion-1.4.5-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/infrae.subversion-1.4.5-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/mercurial-1.7.3-py2.6-linux-x86_64.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/py-1.4.0-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/py-1.4.0-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/zc.buildout-1.5.2-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/zc.buildout-1.5.2-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => eggs/zc.recipe.egg-1.3.2-py2.6.egg/EGG-INFO/dependency_links.txt rename : profile/management/__init__.py => eggs/zc.recipe.egg-1.3.2-py2.6.egg/EGG-INFO/not-zip-safe rename : profile/management/__init__.py => parts/django/Django.egg-info/dependency_links.txt rename : taskapp/models.py => parts/django/django/conf/app_template/models.py rename : taskapp/tests.py => parts/django/django/conf/app_template/tests.py rename : taskapp/views.py => parts/django/django/conf/app_template/views.py rename : taskapp/views.py => parts/django/django/contrib/gis/tests/geo3d/views.py rename : profile/management/__init__.py => parts/django/tests/modeltests/delete/__init__.py rename : profile/management/__init__.py => parts/django/tests/modeltests/files/__init__.py rename : profile/management/__init__.py => parts/django/tests/modeltests/invalid_models/__init__.py rename : profile/management/__init__.py => parts/django/tests/modeltests/m2m_signals/__init__.py rename : profile/management/__init__.py => parts/django/tests/modeltests/model_package/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/management/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/management/commands/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/bash_completion/models.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/delete_regress/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/file_storage/__init__.py rename : profile/management/__init__.py => parts/django/tests/regressiontests/max_lengths/__init__.py rename : profile/forms.py => pytask/profile/forms.py rename : profile/management/__init__.py => pytask/profile/management/__init__.py rename : profile/management/commands/seed_db.py => pytask/profile/management/commands/seed_db.py rename : profile/models.py => pytask/profile/models.py rename : profile/templatetags/user_tags.py => pytask/profile/templatetags/user_tags.py rename : taskapp/tests.py => pytask/profile/tests.py rename : profile/urls.py => pytask/profile/urls.py rename : profile/utils.py => pytask/profile/utils.py rename : profile/views.py => pytask/profile/views.py rename : static/css/base.css => pytask/static/css/base.css rename : taskapp/tests.py => pytask/taskapp/tests.py rename : taskapp/views.py => pytask/taskapp/views.py rename : templates/base.html => pytask/templates/base.html rename : templates/profile/browse_notifications.html => pytask/templates/profile/browse_notifications.html rename : templates/profile/edit.html => pytask/templates/profile/edit.html rename : templates/profile/view.html => pytask/templates/profile/view.html rename : templates/profile/view_notification.html => pytask/templates/profile/view_notification.html rename : templates/registration/activate.html => pytask/templates/registration/activate.html rename : templates/registration/activation_email.txt => pytask/templates/registration/activation_email.txt rename : templates/registration/activation_email_subject.txt => pytask/templates/registration/activation_email_subject.txt rename : templates/registration/logged_out.html => pytask/templates/registration/logged_out.html rename : templates/registration/login.html => pytask/templates/registration/login.html rename : templates/registration/logout.html => pytask/templates/registration/logout.html rename : templates/registration/password_change_done.html => pytask/templates/registration/password_change_done.html rename : templates/registration/password_change_form.html => pytask/templates/registration/password_change_form.html rename : templates/registration/password_reset_complete.html => pytask/templates/registration/password_reset_complete.html rename : templates/registration/password_reset_confirm.html => pytask/templates/registration/password_reset_confirm.html rename : templates/registration/password_reset_done.html => pytask/templates/registration/password_reset_done.html rename : templates/registration/password_reset_email.html => pytask/templates/registration/password_reset_email.html rename : templates/registration/password_reset_form.html => pytask/templates/registration/password_reset_form.html rename : templates/registration/registration_complete.html => pytask/templates/registration/registration_complete.html rename : templates/registration/registration_form.html => pytask/templates/registration/registration_form.html rename : utils.py => pytask/utils.py
Diffstat (limited to 'parts/django/docs/ref/contrib/formtools/form-wizard.txt')
-rw-r--r--parts/django/docs/ref/contrib/formtools/form-wizard.txt312
1 files changed, 312 insertions, 0 deletions
diff --git a/parts/django/docs/ref/contrib/formtools/form-wizard.txt b/parts/django/docs/ref/contrib/formtools/form-wizard.txt
new file mode 100644
index 0000000..390d575
--- /dev/null
+++ b/parts/django/docs/ref/contrib/formtools/form-wizard.txt
@@ -0,0 +1,312 @@
+===========
+Form wizard
+===========
+
+.. module:: django.contrib.formtools.wizard
+ :synopsis: Splits forms across multiple Web pages.
+
+.. versionadded:: 1.0
+
+Django comes with an optional "form wizard" application that splits
+:doc:`forms </topics/forms/index>` across multiple Web pages. It maintains
+state in hashed HTML :samp:`<input type="hidden">` fields, and the data isn't
+processed server-side until the final form is submitted.
+
+You might want to use this if you have a lengthy form that would be too
+unwieldy for display on a single page. The first page might ask the user for
+core information, the second page might ask for less important information,
+etc.
+
+The term "wizard," in this context, is `explained on Wikipedia`_.
+
+.. _explained on Wikipedia: http://en.wikipedia.org/wiki/Wizard_%28software%29
+.. _forms: ../forms/
+
+How it works
+============
+
+Here's the basic workflow for how a user would use a wizard:
+
+ 1. The user visits the first page of the wizard, fills in the form and
+ submits it.
+ 2. The server validates the data. If it's invalid, the form is displayed
+ again, with error messages. If it's valid, the server calculates a
+ secure hash of the data and presents the user with the next form,
+ saving the validated data and hash in :samp:`<input type="hidden">`
+ fields.
+ 3. Step 1 and 2 repeat, for every subsequent form in the wizard.
+ 4. Once the user has submitted all the forms and all the data has been
+ validated, the wizard processes the data -- saving it to the database,
+ sending an e-mail, or whatever the application needs to do.
+
+Usage
+=====
+
+This application handles as much machinery for you as possible. Generally, you
+just have to do these things:
+
+ 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:`FormWizard` class.
+
+Defining ``Form`` classes
+=========================
+
+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 :doc:`forms documentation
+</topics/forms/index>`. 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
+
+ class ContactForm1(forms.Form):
+ subject = forms.CharField(max_length=100)
+ sender = forms.EmailField()
+
+ class ContactForm2(forms.Form):
+ 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.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` 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:`~FormWizard.done()` method.
+
+.. 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::
+
+ from django.shortcuts import render_to_response
+ from django.contrib.formtools.wizard import FormWizard
+
+ class ContactWizard(FormWizard):
+ def done(self, request, form_list):
+ return render_to_response('done.html', {
+ 'form_data': [form.cleaned_data for form in form_list],
+ })
+
+Note that this method will be called via ``POST``, so it really ought to be a
+good Web citizen and redirect after processing the data. Here's another
+example::
+
+ from django.http import HttpResponseRedirect
+ from django.contrib.formtools.wizard import FormWizard
+
+ class ContactWizard(FormWizard):
+ def done(self, request, form_list):
+ do_something_with_the_form_data(form_list)
+ return HttpResponseRedirect('/page-to-redirect-to-when-done/')
+
+See the section `Advanced FormWizard methods`_ below to learn about more
+: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:`~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:
+
+ * ``step_field`` -- The name of the hidden field containing the step.
+ * ``step0`` -- The current step (zero-based).
+ * ``step`` -- The current step (one-based).
+ * ``step_count`` -- The total number of steps.
+ * ``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 :tfilter:`safe` template filter,
+ to prevent auto-escaping, because it's raw HTML.
+
+You can supply extra context to this template in two ways:
+
+ * Set the :attr:`~FormWizard.extra_context` attribute on your
+ :class:`FormWizard` subclass to a dictionary.
+
+ * 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:
+
+.. code-block:: html+django
+
+ {% extends "base.html" %}
+
+ {% block content %}
+ <p>Step {{ step }} of {{ step_count }}</p>
+ <form action="." method="post">{% csrf_token %}
+ <table>
+ {{ form }}
+ </table>
+ <input type="hidden" name="{{ step_field }}" value="{{ step0 }}" />
+ {{ previous_fields|safe }}
+ <input type="submit">
+ </form>
+ {% endblock %}
+
+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, we need to specify which forms to use in the wizard, and then
+deploy the new :class:`FormWizard` object a URL in ``urls.py``. The
+wizard takes a list of your :class:`~django.forms.Form` objects as
+arguments when you instantiate the Wizard::
+
+ from django.conf.urls.defaults import *
+ from testapp.forms import ContactForm1, ContactForm2, ContactWizard
+
+ urlpatterns = patterns('',
+ (r'^contact/$', ContactWizard([ContactForm1, ContactForm2])),
+ )
+
+Advanced ``FormWizard`` methods
+===============================
+
+.. class:: FormWizard
+
+ 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``.)
+
+.. method:: FormWizard.prefix_for_step
+
+ 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
+ <form-prefix>`.
+
+ Default implementation::
+
+ def prefix_for_step(self, step):
+ return str(step)
+
+.. method:: FormWizard.render_hash_failure
+
+ Renders a template if the hash check fails. It's rare that you'd need to
+ override this.
+
+ Default implementation::
+
+ 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.'})
+
+.. method:: FormWizard.security_hash
+
+ 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.
+
+ Example::
+
+ def security_hash(self, request, form):
+ return my_hash_function(request, form)
+
+.. 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.
+
+ By default, this does nothing.
+
+ Example::
+
+ def parse_params(self, request, *args, **kwargs):
+ self.my_state = args[0]
+
+.. method:: FormWizard.get_template
+
+ Returns the name of the template that should be used for the given step.
+
+ By default, this returns :file:`'forms/wizard.html'`, regardless of step.
+
+ Example::
+
+ 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.
+ 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']
+
+.. 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.
+
+ The template will be rendered with the context documented in the
+ "Creating templates for the forms" section above.
+
+.. method:: FormWizard.process_step
+
+ Hook for modifying the wizard's internal state, given a fully validated
+ :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.
+
+ Note that this method is called every time a page is rendered for *all*
+ submitted steps.
+
+ The function signature::
+
+ def process_step(self, request, form, step):
+ # ...