mirror of https://github.com/django/django.git
Removed the legacy form wizard.
This commit is contained in:
parent
052271168b
commit
d4ea02b992
|
@ -10,7 +10,6 @@ import warnings
|
||||||
from django import http
|
from django import http
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.contrib.formtools import preview, utils
|
from django.contrib.formtools import preview, utils
|
||||||
from django.contrib.formtools.wizard import FormWizard
|
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
from django.test.html import parse_html
|
from django.test.html import parse_html
|
||||||
from django.test.utils import override_settings
|
from django.test.utils import override_settings
|
||||||
|
@ -187,255 +186,3 @@ class FormHmacTests(unittest.TestCase):
|
||||||
hash1 = utils.form_hmac(f1)
|
hash1 = utils.form_hmac(f1)
|
||||||
hash2 = utils.form_hmac(f2)
|
hash2 = utils.form_hmac(f2)
|
||||||
self.assertEqual(hash1, hash2)
|
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 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):
|
class TestForm(forms.Form):
|
||||||
field1 = forms.CharField()
|
field1 = forms.CharField()
|
||||||
|
@ -30,15 +13,3 @@ class HashTestForm(forms.Form):
|
||||||
class HashTestBlankForm(forms.Form):
|
class HashTestBlankForm(forms.Form):
|
||||||
name = forms.CharField(required=False)
|
name = forms.CharField(required=False)
|
||||||
bio = 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 __future__ import absolute_import
|
||||||
|
|
||||||
from django.conf.urls import patterns, url
|
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,
|
from django.contrib.formtools.tests.forms import TestForm
|
||||||
Page3, TestForm, WizardPageOneForm, WizardPageTwoForm, WizardPageThreeForm)
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = patterns('',
|
urlpatterns = patterns('',
|
||||||
url(r'^preview/', TestFormPreview(TestForm)),
|
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__)
|
|
Loading…
Reference in New Issue