|
1 =========== |
|
2 Form wizard |
|
3 =========== |
|
4 |
|
5 .. module:: django.contrib.formtools.wizard |
|
6 :synopsis: Splits forms across multiple Web pages. |
|
7 |
|
8 .. versionadded:: 1.0 |
|
9 |
|
10 Django comes with an optional "form wizard" application that splits |
|
11 :doc:`forms </topics/forms/index>` across multiple Web pages. It maintains |
|
12 state in hashed HTML :samp:`<input type="hidden">` fields, and the data isn't |
|
13 processed server-side until the final form is submitted. |
|
14 |
|
15 You might want to use this if you have a lengthy form that would be too |
|
16 unwieldy for display on a single page. The first page might ask the user for |
|
17 core information, the second page might ask for less important information, |
|
18 etc. |
|
19 |
|
20 The term "wizard," in this context, is `explained on Wikipedia`_. |
|
21 |
|
22 .. _explained on Wikipedia: http://en.wikipedia.org/wiki/Wizard_%28software%29 |
|
23 .. _forms: ../forms/ |
|
24 |
|
25 How it works |
|
26 ============ |
|
27 |
|
28 Here's the basic workflow for how a user would use a wizard: |
|
29 |
|
30 1. The user visits the first page of the wizard, fills in the form and |
|
31 submits it. |
|
32 2. The server validates the data. If it's invalid, the form is displayed |
|
33 again, with error messages. If it's valid, the server calculates a |
|
34 secure hash of the data and presents the user with the next form, |
|
35 saving the validated data and hash in :samp:`<input type="hidden">` |
|
36 fields. |
|
37 3. Step 1 and 2 repeat, for every subsequent form in the wizard. |
|
38 4. Once the user has submitted all the forms and all the data has been |
|
39 validated, the wizard processes the data -- saving it to the database, |
|
40 sending an e-mail, or whatever the application needs to do. |
|
41 |
|
42 Usage |
|
43 ===== |
|
44 |
|
45 This application handles as much machinery for you as possible. Generally, you |
|
46 just have to do these things: |
|
47 |
|
48 1. Define a number of :class:`~django.forms.Form` classes -- one per wizard |
|
49 page. |
|
50 |
|
51 2. Create a :class:`FormWizard` class that specifies what to do once all of |
|
52 your forms have been submitted and validated. This also lets you |
|
53 override some of the wizard's behavior. |
|
54 |
|
55 3. Create some templates that render the forms. You can define a single, |
|
56 generic template to handle every one of the forms, or you can define a |
|
57 specific template for each form. |
|
58 |
|
59 4. Point your URLconf at your :class:`FormWizard` class. |
|
60 |
|
61 Defining ``Form`` classes |
|
62 ========================= |
|
63 |
|
64 The first step in creating a form wizard is to create the |
|
65 :class:`~django.forms.Form` classes. These should be standard |
|
66 :class:`django.forms.Form` classes, covered in the :doc:`forms documentation |
|
67 </topics/forms/index>`. These classes can live anywhere in your codebase, but |
|
68 convention is to put them in a file called :file:`forms.py` in your |
|
69 application. |
|
70 |
|
71 For example, let's write a "contact form" wizard, where the first page's form |
|
72 collects the sender's e-mail address and subject, and the second page collects |
|
73 the message itself. Here's what the :file:`forms.py` might look like:: |
|
74 |
|
75 from django import forms |
|
76 |
|
77 class ContactForm1(forms.Form): |
|
78 subject = forms.CharField(max_length=100) |
|
79 sender = forms.EmailField() |
|
80 |
|
81 class ContactForm2(forms.Form): |
|
82 message = forms.CharField(widget=forms.Textarea) |
|
83 |
|
84 **Important limitation:** Because the wizard uses HTML hidden fields to store |
|
85 data between pages, you may not include a :class:`~django.forms.FileField` |
|
86 in any form except the last one. |
|
87 |
|
88 Creating a ``FormWizard`` class |
|
89 =============================== |
|
90 |
|
91 The next step is to create a |
|
92 :class:`django.contrib.formtools.wizard.FormWizard` subclass. As with your |
|
93 :class:`~django.forms.Form` classes, this :class:`FormWizard` class can live |
|
94 anywhere in your codebase, but convention is to put it in :file:`forms.py`. |
|
95 |
|
96 The only requirement on this subclass is that it implement a |
|
97 :meth:`~FormWizard.done()` method. |
|
98 |
|
99 .. method:: FormWizard.done |
|
100 |
|
101 This method specifies what should happen when the data for *every* form is |
|
102 submitted and validated. This method is passed two arguments: |
|
103 |
|
104 * ``request`` -- an :class:`~django.http.HttpRequest` object |
|
105 * ``form_list`` -- a list of :class:`~django.forms.Form` classes |
|
106 |
|
107 In this simplistic example, rather than perform any database operation, the |
|
108 method simply renders a template of the validated data:: |
|
109 |
|
110 from django.shortcuts import render_to_response |
|
111 from django.contrib.formtools.wizard import FormWizard |
|
112 |
|
113 class ContactWizard(FormWizard): |
|
114 def done(self, request, form_list): |
|
115 return render_to_response('done.html', { |
|
116 'form_data': [form.cleaned_data for form in form_list], |
|
117 }) |
|
118 |
|
119 Note that this method will be called via ``POST``, so it really ought to be a |
|
120 good Web citizen and redirect after processing the data. Here's another |
|
121 example:: |
|
122 |
|
123 from django.http import HttpResponseRedirect |
|
124 from django.contrib.formtools.wizard import FormWizard |
|
125 |
|
126 class ContactWizard(FormWizard): |
|
127 def done(self, request, form_list): |
|
128 do_something_with_the_form_data(form_list) |
|
129 return HttpResponseRedirect('/page-to-redirect-to-when-done/') |
|
130 |
|
131 See the section `Advanced FormWizard methods`_ below to learn about more |
|
132 :class:`FormWizard` hooks. |
|
133 |
|
134 Creating templates for the forms |
|
135 ================================ |
|
136 |
|
137 Next, you'll need to create a template that renders the wizard's forms. By |
|
138 default, every form uses a template called :file:`forms/wizard.html`. (You can |
|
139 change this template name by overriding :meth:`~FormWizard.get_template()`, |
|
140 which is documented below. This hook also allows you to use a different |
|
141 template for each form.) |
|
142 |
|
143 This template expects the following context: |
|
144 |
|
145 * ``step_field`` -- The name of the hidden field containing the step. |
|
146 * ``step0`` -- The current step (zero-based). |
|
147 * ``step`` -- The current step (one-based). |
|
148 * ``step_count`` -- The total number of steps. |
|
149 * ``form`` -- The :class:`~django.forms.Form` instance for the current step |
|
150 (either empty or with errors). |
|
151 * ``previous_fields`` -- A string representing every previous data field, |
|
152 plus hashes for completed forms, all in the form of hidden fields. Note |
|
153 that you'll need to run this through the :tfilter:`safe` template filter, |
|
154 to prevent auto-escaping, because it's raw HTML. |
|
155 |
|
156 You can supply extra context to this template in two ways: |
|
157 |
|
158 * Set the :attr:`~FormWizard.extra_context` attribute on your |
|
159 :class:`FormWizard` subclass to a dictionary. |
|
160 |
|
161 * Pass a dictionary as a parameter named ``extra_context`` to your wizard's |
|
162 URL pattern in your URLconf. See :ref:`hooking-wizard-into-urlconf`. |
|
163 |
|
164 Here's a full example template: |
|
165 |
|
166 .. code-block:: html+django |
|
167 |
|
168 {% extends "base.html" %} |
|
169 |
|
170 {% block content %} |
|
171 <p>Step {{ step }} of {{ step_count }}</p> |
|
172 <form action="." method="post">{% csrf_token %} |
|
173 <table> |
|
174 {{ form }} |
|
175 </table> |
|
176 <input type="hidden" name="{{ step_field }}" value="{{ step0 }}" /> |
|
177 {{ previous_fields|safe }} |
|
178 <input type="submit"> |
|
179 </form> |
|
180 {% endblock %} |
|
181 |
|
182 Note that ``previous_fields``, ``step_field`` and ``step0`` are all required |
|
183 for the wizard to work properly. |
|
184 |
|
185 .. _hooking-wizard-into-urlconf: |
|
186 |
|
187 Hooking the wizard into a URLconf |
|
188 ================================= |
|
189 |
|
190 Finally, we need to specify which forms to use in the wizard, and then |
|
191 deploy the new :class:`FormWizard` object a URL in ``urls.py``. The |
|
192 wizard takes a list of your :class:`~django.forms.Form` objects as |
|
193 arguments when you instantiate the Wizard:: |
|
194 |
|
195 from django.conf.urls.defaults import * |
|
196 from testapp.forms import ContactForm1, ContactForm2, ContactWizard |
|
197 |
|
198 urlpatterns = patterns('', |
|
199 (r'^contact/$', ContactWizard([ContactForm1, ContactForm2])), |
|
200 ) |
|
201 |
|
202 Advanced ``FormWizard`` methods |
|
203 =============================== |
|
204 |
|
205 .. class:: FormWizard |
|
206 |
|
207 Aside from the :meth:`~done()` method, :class:`FormWizard` offers a few |
|
208 advanced method hooks that let you customize how your wizard works. |
|
209 |
|
210 Some of these methods take an argument ``step``, which is a zero-based |
|
211 counter representing the current step of the wizard. (E.g., the first form |
|
212 is ``0`` and the second form is ``1``.) |
|
213 |
|
214 .. method:: FormWizard.prefix_for_step |
|
215 |
|
216 Given the step, returns a form prefix to use. By default, this simply uses |
|
217 the step itself. For more, see the :ref:`form prefix documentation |
|
218 <form-prefix>`. |
|
219 |
|
220 Default implementation:: |
|
221 |
|
222 def prefix_for_step(self, step): |
|
223 return str(step) |
|
224 |
|
225 .. method:: FormWizard.render_hash_failure |
|
226 |
|
227 Renders a template if the hash check fails. It's rare that you'd need to |
|
228 override this. |
|
229 |
|
230 Default implementation:: |
|
231 |
|
232 def render_hash_failure(self, request, step): |
|
233 return self.render(self.get_form(step), request, step, |
|
234 context={'wizard_error': |
|
235 'We apologize, but your form has expired. Please' |
|
236 ' continue filling out the form from this page.'}) |
|
237 |
|
238 .. method:: FormWizard.security_hash |
|
239 |
|
240 Calculates the security hash for the given request object and |
|
241 :class:`~django.forms.Form` instance. |
|
242 |
|
243 By default, this uses an MD5 hash of the form data and your |
|
244 :setting:`SECRET_KEY` setting. It's rare that somebody would need to |
|
245 override this. |
|
246 |
|
247 Example:: |
|
248 |
|
249 def security_hash(self, request, form): |
|
250 return my_hash_function(request, form) |
|
251 |
|
252 .. method:: FormWizard.parse_params |
|
253 |
|
254 A hook for saving state from the request object and ``args`` / ``kwargs`` |
|
255 that were captured from the URL by your URLconf. |
|
256 |
|
257 By default, this does nothing. |
|
258 |
|
259 Example:: |
|
260 |
|
261 def parse_params(self, request, *args, **kwargs): |
|
262 self.my_state = args[0] |
|
263 |
|
264 .. method:: FormWizard.get_template |
|
265 |
|
266 Returns the name of the template that should be used for the given step. |
|
267 |
|
268 By default, this returns :file:`'forms/wizard.html'`, regardless of step. |
|
269 |
|
270 Example:: |
|
271 |
|
272 def get_template(self, step): |
|
273 return 'myapp/wizard_%s.html' % step |
|
274 |
|
275 If :meth:`~FormWizard.get_template` returns a list of strings, then the |
|
276 wizard will use the template system's |
|
277 :func:`~django.template.loader.select_template` function. |
|
278 This means the system will use the first template that exists on the |
|
279 filesystem. For example:: |
|
280 |
|
281 def get_template(self, step): |
|
282 return ['myapp/wizard_%s.html' % step, 'myapp/wizard.html'] |
|
283 |
|
284 .. method:: FormWizard.render_template |
|
285 |
|
286 Renders the template for the given step, returning an |
|
287 :class:`~django.http.HttpResponse` object. |
|
288 |
|
289 Override this method if you want to add a custom context, return a |
|
290 different MIME type, etc. If you only need to override the template name, |
|
291 use :meth:`~FormWizard.get_template` instead. |
|
292 |
|
293 The template will be rendered with the context documented in the |
|
294 "Creating templates for the forms" section above. |
|
295 |
|
296 .. method:: FormWizard.process_step |
|
297 |
|
298 Hook for modifying the wizard's internal state, given a fully validated |
|
299 :class:`~django.forms.Form` object. The Form is guaranteed to have clean, |
|
300 valid data. |
|
301 |
|
302 This method should *not* modify any of that data. Rather, it might want to |
|
303 set ``self.extra_context`` or dynamically alter ``self.form_list``, based |
|
304 on previously submitted forms. |
|
305 |
|
306 Note that this method is called every time a page is rendered for *all* |
|
307 submitted steps. |
|
308 |
|
309 The function signature:: |
|
310 |
|
311 def process_step(self, request, form, step): |
|
312 # ... |