mirror of
				https://github.com/django/django.git
				synced 2025-10-31 09:41:08 +00:00 
			
		
		
		
	Removed the legacy form wizard.
This commit is contained in:
		| @@ -10,7 +10,6 @@ import warnings | ||||
| from django import http | ||||
| from django.conf import settings | ||||
| from django.contrib.formtools import preview, utils | ||||
| from django.contrib.formtools.wizard import FormWizard | ||||
| from django.test import TestCase | ||||
| from django.test.html import parse_html | ||||
| from django.test.utils import override_settings | ||||
| @@ -187,255 +186,3 @@ class FormHmacTests(unittest.TestCase): | ||||
|         hash1 = utils.form_hmac(f1) | ||||
|         hash2 = utils.form_hmac(f2) | ||||
|         self.assertEqual(hash1, hash2) | ||||
|  | ||||
|  | ||||
| # | ||||
| # FormWizard tests | ||||
| # | ||||
|  | ||||
| class TestWizardClass(FormWizard): | ||||
|  | ||||
|     def get_template(self, step): | ||||
|         return 'forms/wizard.html' | ||||
|  | ||||
|     def done(self, request, cleaned_data): | ||||
|         return http.HttpResponse(success_string) | ||||
|  | ||||
|  | ||||
| class DummyRequest(http.HttpRequest): | ||||
|  | ||||
|     def __init__(self, POST=None): | ||||
|         super(DummyRequest, self).__init__() | ||||
|         self.method = POST and "POST" or "GET" | ||||
|         if POST is not None: | ||||
|             self.POST.update(POST) | ||||
|         self._dont_enforce_csrf_checks = True | ||||
|  | ||||
|  | ||||
| @override_settings( | ||||
|     SECRET_KEY="123", | ||||
|     TEMPLATE_DIRS=( | ||||
|         os.path.join(os.path.dirname(upath(__file__)), 'templates'), | ||||
|     ), | ||||
| ) | ||||
| class WizardTests(TestCase): | ||||
|     urls = 'django.contrib.formtools.tests.urls' | ||||
|     wizard_step_data = ( | ||||
|         { | ||||
|             '0-name': 'Pony', | ||||
|             '0-thirsty': '2', | ||||
|         }, | ||||
|         { | ||||
|             '1-address1': '123 Main St', | ||||
|             '1-address2': 'Djangoland', | ||||
|         }, | ||||
|         { | ||||
|             '2-random_crap': 'blah blah', | ||||
|         } | ||||
|     ) | ||||
|  | ||||
|     def setUp(self): | ||||
|         super(WizardTests, self).setUp() | ||||
|         self.save_warnings_state() | ||||
|         warnings.filterwarnings('ignore', category=DeprecationWarning, | ||||
|                                 module='django.contrib.formtools.wizard') | ||||
|  | ||||
|     def tearDown(self): | ||||
|         super(WizardTests, self).tearDown() | ||||
|         self.restore_warnings_state() | ||||
|  | ||||
|     def test_step_starts_at_zero(self): | ||||
|         """ | ||||
|         step should be zero for the first form | ||||
|         """ | ||||
|         response = self.client.get('/wizard1/') | ||||
|         self.assertEqual(0, response.context['step0']) | ||||
|  | ||||
|     def test_step_increments(self): | ||||
|         """ | ||||
|         step should be incremented when we go to the next page | ||||
|         """ | ||||
|         response = self.client.post('/wizard1/', {"0-field":"test", "wizard_step":"0"}) | ||||
|         self.assertEqual(1, response.context['step0']) | ||||
|  | ||||
|     def test_bad_hash(self): | ||||
|         """ | ||||
|         Form should not advance if the hash is missing or bad | ||||
|         """ | ||||
|         response = self.client.post('/wizard1/', | ||||
|                                     {"0-field":"test", | ||||
|                                      "1-field":"test2", | ||||
|                                      "wizard_step": "1"}) | ||||
|         self.assertEqual(0, response.context['step0']) | ||||
|  | ||||
|     def test_good_hash(self): | ||||
|         """ | ||||
|         Form should advance if the hash is present and good, as calculated using | ||||
|         current method. | ||||
|         """ | ||||
|         data = {"0-field": "test", | ||||
|                 "1-field": "test2", | ||||
|                 "hash_0": { | ||||
|                     2: "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", | ||||
|                     3: "9355d5dff22d49dbad58e46189982cec649f9f5b", | ||||
|                 }[pickle.HIGHEST_PROTOCOL], | ||||
|                 "wizard_step": "1"} | ||||
|         response = self.client.post('/wizard1/', data) | ||||
|         self.assertEqual(2, response.context['step0']) | ||||
|  | ||||
|     def test_11726(self): | ||||
|         """ | ||||
|         Regression test for ticket #11726. | ||||
|         Wizard should not raise Http404 when steps are added dynamically. | ||||
|         """ | ||||
|         reached = [False] | ||||
|         that = self | ||||
|  | ||||
|         class WizardWithProcessStep(TestWizardClass): | ||||
|             def process_step(self, request, form, step): | ||||
|                 if step == 0: | ||||
|                     if self.num_steps() < 2: | ||||
|                         self.form_list.append(WizardPageTwoForm) | ||||
|                 if step == 1: | ||||
|                     that.assertTrue(isinstance(form, WizardPageTwoForm)) | ||||
|                     reached[0] = True | ||||
|  | ||||
|         wizard = WizardWithProcessStep([WizardPageOneForm]) | ||||
|         data = {"0-field": "test", | ||||
|                 "1-field": "test2", | ||||
|                 "hash_0": { | ||||
|                     2: "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", | ||||
|                     3: "9355d5dff22d49dbad58e46189982cec649f9f5b", | ||||
|                 }[pickle.HIGHEST_PROTOCOL], | ||||
|                 "wizard_step": "1"} | ||||
|         wizard(DummyRequest(POST=data)) | ||||
|         self.assertTrue(reached[0]) | ||||
|  | ||||
|         data = {"0-field": "test", | ||||
|                 "1-field": "test2", | ||||
|                 "hash_0": { | ||||
|                     2: "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", | ||||
|                     3: "9355d5dff22d49dbad58e46189982cec649f9f5b", | ||||
|                 }[pickle.HIGHEST_PROTOCOL], | ||||
|                 "hash_1": { | ||||
|                     2: "1e6f6315da42e62f33a30640ec7e007ad3fbf1a1", | ||||
|                     3: "c33142ef9d01b1beae238adf22c3c6c57328f51a", | ||||
|                 }[pickle.HIGHEST_PROTOCOL], | ||||
|                 "wizard_step": "2"} | ||||
|         self.assertRaises(http.Http404, wizard, DummyRequest(POST=data)) | ||||
|  | ||||
|     def test_14498(self): | ||||
|         """ | ||||
|         Regression test for ticket #14498.  All previous steps' forms should be | ||||
|         validated. | ||||
|         """ | ||||
|         reached = [False] | ||||
|         that = self | ||||
|  | ||||
|         class WizardWithProcessStep(TestWizardClass): | ||||
|             def process_step(self, request, form, step): | ||||
|                 that.assertTrue(form.is_valid()) | ||||
|                 reached[0] = True | ||||
|  | ||||
|         wizard = WizardWithProcessStep([WizardPageOneForm, | ||||
|                                         WizardPageTwoForm, | ||||
|                                         WizardPageThreeForm]) | ||||
|         data = {"0-field": "test", | ||||
|                 "1-field": "test2", | ||||
|                 "hash_0": { | ||||
|                     2: "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", | ||||
|                     3: "9355d5dff22d49dbad58e46189982cec649f9f5b", | ||||
|                 }[pickle.HIGHEST_PROTOCOL], | ||||
|                 "wizard_step": "1"} | ||||
|         wizard(DummyRequest(POST=data)) | ||||
|         self.assertTrue(reached[0]) | ||||
|  | ||||
|     def test_14576(self): | ||||
|         """ | ||||
|         Regression test for ticket #14576. | ||||
|  | ||||
|         The form of the last step is not passed to the done method. | ||||
|         """ | ||||
|         reached = [False] | ||||
|         that = self | ||||
|  | ||||
|         class Wizard(TestWizardClass): | ||||
|             def done(self, request, form_list): | ||||
|                 reached[0] = True | ||||
|                 that.assertTrue(len(form_list) == 2) | ||||
|  | ||||
|         wizard = Wizard([WizardPageOneForm, | ||||
|                          WizardPageTwoForm]) | ||||
|  | ||||
|         data = {"0-field": "test", | ||||
|                 "1-field": "test2", | ||||
|                 "hash_0": { | ||||
|                     2: "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", | ||||
|                     3: "9355d5dff22d49dbad58e46189982cec649f9f5b", | ||||
|                 }[pickle.HIGHEST_PROTOCOL], | ||||
|                 "wizard_step": "1"} | ||||
|         wizard(DummyRequest(POST=data)) | ||||
|         self.assertTrue(reached[0]) | ||||
|  | ||||
|     def test_15075(self): | ||||
|         """ | ||||
|         Regression test for ticket #15075.  Allow modifying wizard's form_list | ||||
|         in process_step. | ||||
|         """ | ||||
|         reached = [False] | ||||
|         that = self | ||||
|  | ||||
|         class WizardWithProcessStep(TestWizardClass): | ||||
|             def process_step(self, request, form, step): | ||||
|                 if step == 0: | ||||
|                     self.form_list[1] = WizardPageTwoAlternativeForm | ||||
|                 if step == 1: | ||||
|                     that.assertTrue(isinstance(form, WizardPageTwoAlternativeForm)) | ||||
|                     reached[0] = True | ||||
|  | ||||
|         wizard = WizardWithProcessStep([WizardPageOneForm, | ||||
|                                         WizardPageTwoForm, | ||||
|                                         WizardPageThreeForm]) | ||||
|         data = {"0-field": "test", | ||||
|                 "1-field": "test2", | ||||
|                 "hash_0": { | ||||
|                     2: "cd13b1db3e8f55174bc5745a1b1a53408d4fd1ca", | ||||
|                     3: "9355d5dff22d49dbad58e46189982cec649f9f5b", | ||||
|                 }[pickle.HIGHEST_PROTOCOL], | ||||
|                 "wizard_step": "1"} | ||||
|         wizard(DummyRequest(POST=data)) | ||||
|         self.assertTrue(reached[0]) | ||||
|  | ||||
|     def grab_field_data(self, response): | ||||
|         """ | ||||
|         Pull the appropriate field data from the context to pass to the next wizard step | ||||
|         """ | ||||
|         previous_fields = parse_html(response.context['previous_fields']) | ||||
|         fields = {'wizard_step': response.context['step0']} | ||||
|  | ||||
|         for input_field in previous_fields: | ||||
|             input_attrs = dict(input_field.attributes) | ||||
|             fields[input_attrs["name"]] = input_attrs["value"] | ||||
|  | ||||
|         return fields | ||||
|  | ||||
|     def check_wizard_step(self, response, step_no): | ||||
|         """ | ||||
|         Helper function to test each step of the wizard | ||||
|         - Make sure the call succeeded | ||||
|         - Make sure response is the proper step number | ||||
|         - return the result from the post for the next step | ||||
|         """ | ||||
|         step_count = len(self.wizard_step_data) | ||||
|  | ||||
|         self.assertContains(response, 'Step %d of %d' % (step_no, step_count)) | ||||
|  | ||||
|         data = self.grab_field_data(response) | ||||
|         data.update(self.wizard_step_data[step_no - 1]) | ||||
|  | ||||
|         return self.client.post('/wizard2/', data) | ||||
|  | ||||
|     def test_9473(self): | ||||
|         response = self.client.get('/wizard2/') | ||||
|         for step_no in range(1, len(self.wizard_step_data) + 1): | ||||
|             response = self.check_wizard_step(response, step_no) | ||||
|   | ||||
| @@ -1,21 +1,4 @@ | ||||
| from django import forms | ||||
| from django.contrib.formtools.wizard import FormWizard | ||||
| from django.http import HttpResponse | ||||
|  | ||||
| class Page1(forms.Form): | ||||
|     name = forms.CharField(max_length=100) | ||||
|     thirsty = forms.NullBooleanField() | ||||
|  | ||||
| class Page2(forms.Form): | ||||
|     address1 = forms.CharField(max_length=100) | ||||
|     address2 = forms.CharField(max_length=100) | ||||
|  | ||||
| class Page3(forms.Form): | ||||
|     random_crap = forms.CharField(max_length=100) | ||||
|  | ||||
| class ContactWizard(FormWizard): | ||||
|     def done(self, request, form_list): | ||||
|         return HttpResponse("") | ||||
|  | ||||
| class TestForm(forms.Form): | ||||
|     field1 = forms.CharField() | ||||
| @@ -30,15 +13,3 @@ class HashTestForm(forms.Form): | ||||
| class HashTestBlankForm(forms.Form): | ||||
|     name = forms.CharField(required=False) | ||||
|     bio = forms.CharField(required=False) | ||||
|  | ||||
| class WizardPageOneForm(forms.Form): | ||||
|     field = forms.CharField() | ||||
|  | ||||
| class WizardPageTwoForm(forms.Form): | ||||
|     field = forms.CharField() | ||||
|  | ||||
| class WizardPageTwoAlternativeForm(forms.Form): | ||||
|     field = forms.CharField() | ||||
|  | ||||
| class WizardPageThreeForm(forms.Form): | ||||
|     field = forms.CharField() | ||||
|   | ||||
| @@ -5,15 +5,11 @@ This is a URLconf to be loaded by tests.py. Add any URLs needed for tests only. | ||||
| from __future__ import absolute_import | ||||
|  | ||||
| from django.conf.urls import patterns, url | ||||
| from django.contrib.formtools.tests import TestFormPreview, TestWizardClass | ||||
| from django.contrib.formtools.tests import TestFormPreview | ||||
|  | ||||
| from django.contrib.formtools.tests.forms import (ContactWizard, Page1, Page2, | ||||
|     Page3, TestForm, WizardPageOneForm, WizardPageTwoForm, WizardPageThreeForm) | ||||
| from django.contrib.formtools.tests.forms import TestForm | ||||
|  | ||||
|  | ||||
| urlpatterns = patterns('', | ||||
|     url(r'^preview/', TestFormPreview(TestForm)), | ||||
|     url(r'^wizard1/$', TestWizardClass( | ||||
|         [WizardPageOneForm, WizardPageTwoForm, WizardPageThreeForm])), | ||||
|     url(r'^wizard2/$', ContactWizard([Page1, Page2, Page3])), | ||||
| ) | ||||
|   | ||||
| @@ -1 +0,0 @@ | ||||
| from django.contrib.formtools.wizard.legacy import FormWizard | ||||
|   | ||||
| @@ -1,270 +0,0 @@ | ||||
| """ | ||||
| FormWizard class -- implements a multi-page form, validating between each | ||||
| step and storing the form's state as HTML hidden fields so that no state is | ||||
| stored on the server side. | ||||
| """ | ||||
| from django.forms import HiddenInput | ||||
| from django.http import Http404 | ||||
| from django.shortcuts import render_to_response | ||||
| from django.template.context import RequestContext | ||||
| from django.utils.crypto import constant_time_compare | ||||
| from django.utils.translation import ugettext_lazy as _ | ||||
| from django.utils.decorators import method_decorator | ||||
| from django.views.decorators.csrf import csrf_protect | ||||
|  | ||||
| from django.contrib.formtools.utils import form_hmac | ||||
|  | ||||
| class FormWizard(object): | ||||
|     # The HTML (and POST data) field name for the "step" variable. | ||||
|     step_field_name="wizard_step" | ||||
|  | ||||
|     # METHODS SUBCLASSES SHOULDN'T OVERRIDE ################################### | ||||
|  | ||||
|     def __init__(self, form_list, initial=None): | ||||
|         """ | ||||
|         Start a new wizard with a list of forms. | ||||
|  | ||||
|         form_list should be a list of Form classes (not instances). | ||||
|         """ | ||||
|         self.form_list = form_list[:] | ||||
|         self.initial = initial or {} | ||||
|  | ||||
|         # Dictionary of extra template context variables. | ||||
|         self.extra_context = {} | ||||
|  | ||||
|         # A zero-based counter keeping track of which step we're in. | ||||
|         self.step = 0 | ||||
|  | ||||
|         import warnings | ||||
|         warnings.warn( | ||||
|             'Old-style form wizards have been deprecated; use the class-based ' | ||||
|             'views in django.contrib.formtools.wizard.views instead.', | ||||
|             DeprecationWarning) | ||||
|  | ||||
|     def __repr__(self): | ||||
|         return "step: %d\nform_list: %s\ninitial_data: %s" % (self.step, self.form_list, self.initial) | ||||
|  | ||||
|     def get_form(self, step, data=None): | ||||
|         "Helper method that returns the Form instance for the given step." | ||||
|         # Sanity check. | ||||
|         if step >= self.num_steps(): | ||||
|             raise Http404('Step %s does not exist' % step) | ||||
|         return self.form_list[step](data, prefix=self.prefix_for_step(step), initial=self.initial.get(step, None)) | ||||
|  | ||||
|     def num_steps(self): | ||||
|         "Helper method that returns the number of steps." | ||||
|         # You might think we should just set "self.num_steps = len(form_list)" | ||||
|         # in __init__(), but this calculation needs to be dynamic, because some | ||||
|         # hook methods might alter self.form_list. | ||||
|         return len(self.form_list) | ||||
|  | ||||
|     def _check_security_hash(self, token, request, form): | ||||
|         expected = self.security_hash(request, form) | ||||
|         return constant_time_compare(token, expected) | ||||
|  | ||||
|     @method_decorator(csrf_protect) | ||||
|     def __call__(self, request, *args, **kwargs): | ||||
|         """ | ||||
|         Main method that does all the hard work, conforming to the Django view | ||||
|         interface. | ||||
|         """ | ||||
|         if 'extra_context' in kwargs: | ||||
|             self.extra_context.update(kwargs['extra_context']) | ||||
|         current_step = self.get_current_or_first_step(request, *args, **kwargs) | ||||
|         self.parse_params(request, *args, **kwargs) | ||||
|  | ||||
|         # Validate and process all the previous forms before instantiating the | ||||
|         # current step's form in case self.process_step makes changes to | ||||
|         # self.form_list. | ||||
|  | ||||
|         # If any of them fails validation, that must mean the validator relied | ||||
|         # on some other input, such as an external Web site. | ||||
|  | ||||
|         # It is also possible that alidation might fail under certain attack | ||||
|         # situations: an attacker might be able to bypass previous stages, and | ||||
|         # generate correct security hashes for all the skipped stages by virtue | ||||
|         # of: | ||||
|         #  1) having filled out an identical form which doesn't have the | ||||
|         #     validation (and does something different at the end), | ||||
|         #  2) or having filled out a previous version of the same form which | ||||
|         #     had some validation missing, | ||||
|         #  3) or previously having filled out the form when they had more | ||||
|         #     privileges than they do now. | ||||
|         # | ||||
|         # Since the hashes only take into account values, and not other other | ||||
|         # validation the form might do, we must re-do validation now for | ||||
|         # security reasons. | ||||
|         previous_form_list = [] | ||||
|         for i in range(current_step): | ||||
|             f = self.get_form(i, request.POST) | ||||
|             if not self._check_security_hash(request.POST.get("hash_%d" % i, ''), | ||||
|                                              request, f): | ||||
|                 return self.render_hash_failure(request, i) | ||||
|  | ||||
|             if not f.is_valid(): | ||||
|                 return self.render_revalidation_failure(request, i, f) | ||||
|             else: | ||||
|                 self.process_step(request, f, i) | ||||
|                 previous_form_list.append(f) | ||||
|  | ||||
|         # Process the current step. If it's valid, go to the next step or call | ||||
|         # done(), depending on whether any steps remain. | ||||
|         if request.method == 'POST': | ||||
|             form = self.get_form(current_step, request.POST) | ||||
|         else: | ||||
|             form = self.get_form(current_step) | ||||
|  | ||||
|         if form.is_valid(): | ||||
|             self.process_step(request, form, current_step) | ||||
|             next_step = current_step + 1 | ||||
|  | ||||
|             if next_step == self.num_steps(): | ||||
|                 return self.done(request, previous_form_list + [form]) | ||||
|             else: | ||||
|                 form = self.get_form(next_step) | ||||
|                 self.step = current_step = next_step | ||||
|  | ||||
|         return self.render(form, request, current_step) | ||||
|  | ||||
|     def render(self, form, request, step, context=None): | ||||
|         "Renders the given Form object, returning an HttpResponse." | ||||
|         old_data = request.POST | ||||
|         prev_fields = [] | ||||
|         if old_data: | ||||
|             hidden = HiddenInput() | ||||
|             # Collect all data from previous steps and render it as HTML hidden fields. | ||||
|             for i in range(step): | ||||
|                 old_form = self.get_form(i, old_data) | ||||
|                 hash_name = 'hash_%s' % i | ||||
|                 prev_fields.extend([bf.as_hidden() for bf in old_form]) | ||||
|                 prev_fields.append(hidden.render(hash_name, old_data.get(hash_name, self.security_hash(request, old_form)))) | ||||
|         return self.render_template(request, form, ''.join(prev_fields), step, context) | ||||
|  | ||||
|     # METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ######################## | ||||
|  | ||||
|     def prefix_for_step(self, step): | ||||
|         "Given the step, returns a Form prefix to use." | ||||
|         return str(step) | ||||
|  | ||||
|     def render_hash_failure(self, request, step): | ||||
|         """ | ||||
|         Hook for rendering a template if a hash check failed. | ||||
|  | ||||
|         step is the step that failed. Any previous step is guaranteed to be | ||||
|         valid. | ||||
|  | ||||
|         This default implementation simply renders the form for the given step, | ||||
|         but subclasses may want to display an error message, etc. | ||||
|         """ | ||||
|         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.')}) | ||||
|  | ||||
|     def render_revalidation_failure(self, request, step, form): | ||||
|         """ | ||||
|         Hook for rendering a template if final revalidation failed. | ||||
|  | ||||
|         It is highly unlikely that this point would ever be reached, but See | ||||
|         the comment in __call__() for an explanation. | ||||
|         """ | ||||
|         return self.render(form, request, step) | ||||
|  | ||||
|     def security_hash(self, request, form): | ||||
|         """ | ||||
|         Calculates the security hash for the given HttpRequest and Form instances. | ||||
|  | ||||
|         Subclasses may want to take into account request-specific information, | ||||
|         such as the IP address. | ||||
|         """ | ||||
|         return form_hmac(form) | ||||
|  | ||||
|     def get_current_or_first_step(self, request, *args, **kwargs): | ||||
|         """ | ||||
|         Given the request object and whatever *args and **kwargs were passed to | ||||
|         __call__(), returns the current step (which is zero-based). | ||||
|  | ||||
|         Note that the result should not be trusted. It may even be a completely | ||||
|         invalid number. It's not the job of this method to validate it. | ||||
|         """ | ||||
|         if not request.POST: | ||||
|             return 0 | ||||
|         try: | ||||
|             step = int(request.POST.get(self.step_field_name, 0)) | ||||
|         except ValueError: | ||||
|             return 0 | ||||
|         return step | ||||
|  | ||||
|     def parse_params(self, request, *args, **kwargs): | ||||
|         """ | ||||
|         Hook for setting some state, given the request object and whatever | ||||
|         *args and **kwargs were passed to __call__(), sets some state. | ||||
|  | ||||
|         This is called at the beginning of __call__(). | ||||
|         """ | ||||
|         pass | ||||
|  | ||||
|     def get_template(self, step): | ||||
|         """ | ||||
|         Hook for specifying the name of the template to use for a given step. | ||||
|  | ||||
|         Note that this can return a tuple of template names if you'd like to | ||||
|         use the template system's select_template() hook. | ||||
|         """ | ||||
|         return 'forms/wizard.html' | ||||
|  | ||||
|     def render_template(self, request, form, previous_fields, step, context=None): | ||||
|         """ | ||||
|         Renders the template for the given step, returning an 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 get_template() instead. | ||||
|  | ||||
|         The template will be rendered with 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 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 "safe" template filter, to prevent | ||||
|                           auto-escaping, because it's raw HTML. | ||||
|         """ | ||||
|         context = context or {} | ||||
|         context.update(self.extra_context) | ||||
|         return render_to_response(self.get_template(step), dict(context, | ||||
|             step_field=self.step_field_name, | ||||
|             step0=step, | ||||
|             step=step + 1, | ||||
|             step_count=self.num_steps(), | ||||
|             form=form, | ||||
|             previous_fields=previous_fields | ||||
|         ), context_instance=RequestContext(request)) | ||||
|  | ||||
|     def process_step(self, request, form, step): | ||||
|         """ | ||||
|         Hook for modifying the FormWizard's internal state, given a fully | ||||
|         validated 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. | ||||
|         """ | ||||
|         pass | ||||
|  | ||||
|     # METHODS SUBCLASSES MUST OVERRIDE ######################################## | ||||
|  | ||||
|     def done(self, request, form_list): | ||||
|         """ | ||||
|         Hook for doing something with the validated data. This is responsible | ||||
|         for the final processing. | ||||
|  | ||||
|         form_list is a list of Form instances, each containing clean, valid | ||||
|         data. | ||||
|         """ | ||||
|         raise NotImplementedError("Your %s class has not defined a done() method, which is required." % self.__class__.__name__) | ||||
		Reference in New Issue
	
	Block a user