Ticket #16174: cbv-formpreview0.diff
File cbv-formpreview0.diff, 10.2 KB (added by , 13 years ago) |
---|
-
django/contrib/formtools/preview.py
diff --git a/django/contrib/formtools/preview.py b/django/contrib/formtools/preview.py index b4cdeba..b32d7d0 100644
a b 1 1 """ 2 2 Formtools Preview application. 3 3 """ 4 5 try:6 import cPickle as pickle7 except ImportError:8 import pickle9 10 4 from django.conf import settings 11 from django.http import Http40412 5 from django.shortcuts import render_to_response 13 from django.template.context import RequestContext14 6 from django.utils.crypto import constant_time_compare 15 7 from django.contrib.formtools.utils import form_hmac 8 from django.views.generic import FormView 16 9 17 10 AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter. 18 11 19 class FormPreview( object):12 class FormPreview(FormView): 20 13 preview_template = 'formtools/preview.html' 21 14 form_template = 'formtools/form.html' 22 15 23 16 # METHODS SUBCLASSES SHOULDN'T OVERRIDE ################################### 24 17 25 def __init__(self, form): 18 def __init__(self, form_class, *args, **kwargs): 19 super(FormPreview, self).__init__(*args, **kwargs) 26 20 # form should be a Form class, not an instance. 27 self.form, self.state = form, {} 21 self.form_class = form_class 22 self.state = {} 28 23 29 24 def __call__(self, request, *args, **kwargs): 30 stage = {'1': 'preview', '2': 'post'}.get(request.POST.get(self.unused_name('stage')), 'preview') 25 return self.dispatch(request, *args, **kwargs) 26 27 def dispatch(self, request, *args, **kwargs): 28 self.preview_stage = 'preview' 29 self.post_stage = 'post' 30 stages = {'1': self.preview_stage, '2': self.post_stage} 31 32 posted_stage = request.POST.get(self.unused_name('stage')) 33 self.stage = stages.get(posted_stage, 'preview') 34 35 # For backwards compatiblity 31 36 self.parse_params(*args, **kwargs) 32 try: 33 method = getattr(self, stage + '_' + request.method.lower()) 34 except AttributeError: 35 raise Http404 36 return method(request) 37 38 return super(FormPreview, self).dispatch(request, *args, **kwargs) 37 39 38 40 def unused_name(self, name): 39 41 """ … … class FormPreview(object): 45 47 """ 46 48 while 1: 47 49 try: 48 f = self.form.base_fields[name]50 self.form_class.base_fields[name] 49 51 except KeyError: 50 52 break # This field name isn't being used by the form. 51 53 name += '_' 52 54 return name 53 55 54 def preview_get(self, request): 56 def _get_context_data(self, form): 57 """ For backwards compatiblity. """ 58 context = self.get_context_data() 59 context.update(self.get_context(self.request, form)) 60 return context 61 62 def get(self, request, *args, **kwargs): 55 63 "Displays the form" 56 f = self.form(auto_id=self.get_auto_id(), initial=self.get_initial(request)) 57 return render_to_response(self.form_template, 58 self.get_context(request, f), 59 context_instance=RequestContext(request)) 64 form_class = self.get_form_class() 65 form = self.get_form(form_class) 66 context = self._get_context_data(form) 67 self.template_name = self.form_template 68 return self.render_to_response(context) 69 70 def _check_security_hash(self, token, form): 71 expected = self.security_hash(self.request, form) 72 return constant_time_compare(token, expected) 73 74 def post(self, request, *args, **kwargs): 75 """ Validates the POST data. If valid, displays the preview 76 page or calls done, depending on the stage. Else, redisplays 77 form. """ 78 form_class = self.get_form_class() 79 form = self.get_form(form_class) 80 if form.is_valid(): 81 return self.form_valid(form) 82 else: 83 return self.form_invalid(form) 60 84 61 85 def preview_post(self, request): 62 "Validates the POST data. If valid, displays the preview page. Else, redisplays form." 63 f = self.form(request.POST, auto_id=self.get_auto_id()) 64 context = self.get_context(request, f) 65 if f.is_valid(): 66 self.process_preview(request, f, context) 86 """ For backwards compatibility. failed_hash calls this method by 87 default. """ 88 self.stage = self.preview_stage 89 return self.post(request) 90 91 def form_valid(self, form): 92 context = self._get_context_data(form) 93 if self.stage == self.preview_stage: 94 self.process_preview(self.request, form, context) 67 95 context['hash_field'] = self.unused_name('hash') 68 context['hash_value'] = self.security_hash(request, f) 69 return render_to_response(self.preview_template, context, context_instance=RequestContext(request)) 96 context['hash_value'] = self.security_hash(self.request, form) 97 self.template_name = self.preview_template 98 return self.render_to_response(context) 70 99 else: 71 return render_to_response(self.form_template, context, context_instance=RequestContext(request)) 100 form_hash = self.request.POST.get(self.unused_name('hash'), '') 101 if not self._check_security_hash(form_hash, form): 102 return self.failed_hash(self.request) # Security hash failed. 103 return self.done(self.request, form.cleaned_data) 72 104 73 def _check_security_hash(self, token, request, form): 74 expected = self.security_hash(request, form) 75 return constant_time_compare(token, expected) 76 77 def post_post(self, request): 78 "Validates the POST data. If valid, calls done(). Else, redisplays form." 79 f = self.form(request.POST, auto_id=self.get_auto_id()) 80 if f.is_valid(): 81 if not self._check_security_hash(request.POST.get(self.unused_name('hash'), ''), 82 request, f): 83 return self.failed_hash(request) # Security hash failed. 84 return self.done(request, f.cleaned_data) 85 else: 86 return render_to_response(self.form_template, 87 self.get_context(request, f), 88 context_instance=RequestContext(request)) 105 def form_invalid(self, form): 106 context = self._get_context_data(form) 107 self.template_name = self.form_template 108 return render_to_response(context) 89 109 90 110 # METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ######################## 91 111 … … class FormPreview(object): 96 116 """ 97 117 return AUTO_ID 98 118 99 def get_initial(self, request ):119 def get_initial(self, request=None): 100 120 """ 101 121 Takes a request argument and returns a dictionary to pass to the form's 102 122 ``initial`` kwarg when the form is being created from an HTTP get. 103 123 """ 104 return {}124 return self.initial 105 125 106 126 def get_context(self, request, form): 107 127 "Context for template rendering." 108 return {'form': form, 'stage_field': self.unused_name('stage'), 'state': self.state} 109 128 context = { 129 'form': form, 130 'stage_field': self.unused_name('stage'), 131 'state': self.state 132 } 133 return context 134 135 def get_form_kwargs(self): 136 """ This is overriden to maintain backward compatibility and pass 137 the request to to get_initial. """ 138 kwargs = { 139 'initial': self.get_initial(self.request), 140 'auto_id': self.get_auto_id() 141 } 142 if self.request.method in ('POST', 'PUT'): 143 kwargs.update({ 144 'data': self.request.POST, 145 'files': self.request.FILES, 146 }) 147 return kwargs 110 148 111 149 def parse_params(self, *args, **kwargs): 112 150 """ -
django/contrib/formtools/tests/__init__.py
diff --git a/django/contrib/formtools/tests/__init__.py b/django/contrib/formtools/tests/__init__.py index 7084386..8c5fbb5 100644
a b warnings.filterwarnings('ignore', category=PendingDeprecationWarning, 17 17 18 18 success_string = "Done was called!" 19 19 20 20 21 class TestFormPreview(preview.FormPreview): 21 22 def get_context(self, request, form): 22 23 context = super(TestFormPreview, self).get_context(request, form) 23 24 context.update({'custom_context': True}) 24 25 return context 25 26 27 def get_context_data(self, **kwargs): 28 context = super(TestFormPreview, self).get_context_data(**kwargs) 29 context['more_custom_context'] = True 30 return context 31 26 32 def get_initial(self, request): 27 33 return {'field1': 'Works!'} 28 34 … … class PreviewTests(TestCase): 67 73 stage = self.input % 1 68 74 self.assertContains(response, stage, 1) 69 75 self.assertEqual(response.context['custom_context'], True) 76 self.assertEqual(response.context['more_custom_context'], True) 70 77 self.assertEqual(response.context['form'].initial, {'field1': 'Works!'}) 71 78 72 79 def test_form_preview(self): … … class PreviewTests(TestCase): 86 93 stage = self.input % 2 87 94 self.assertContains(response, stage, 1) 88 95 96 # Check that the correct context was passed to the template 97 self.assertEqual(response.context['custom_context'], True) 98 self.assertEqual(response.context['more_custom_context'], True) 99 89 100 def test_form_submit(self): 90 101 """ 91 102 Test contrib.formtools.preview form submittal. … … class PreviewTests(TestCase): 140 151 response = self.client.post('/preview/', self.test_data) 141 152 self.assertEqual(response.content, success_string) 142 153 143 144 154 def test_form_submit_bad_hash(self): 145 155 """ 146 156 Test contrib.formtools.preview form submittal does not proceed … … class PreviewTests(TestCase): 154 164 self.assertNotEqual(response.content, success_string) 155 165 hash = utils.form_hmac(TestForm(self.test_data)) + "bad" 156 166 self.test_data.update({'hash': hash}) 157 response = self.client.post('/previewpreview/', self.test_data) 167 response = self.client.post('/preview/', self.test_data) 168 self.assertTemplateUsed(response, 'formtools/preview.html') 158 169 self.assertNotEqual(response.content, success_string) 159 170 160 171