Ticket #16174: cbv-formpreview1.diff
File cbv-formpreview1.diff, 10.4 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..67a2e39 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. 11 STAGE_FIELD = 'stage' 12 HASH_FIELD = 'hash' 18 13 19 class FormPreview( object):14 class FormPreview(FormView): 20 15 preview_template = 'formtools/preview.html' 21 16 form_template = 'formtools/form.html' 22 17 23 18 # METHODS SUBCLASSES SHOULDN'T OVERRIDE ################################### 24 19 25 def __init__(self, form): 20 def __init__(self, form_class, *args, **kwargs): 21 super(FormPreview, self).__init__(*args, **kwargs) 26 22 # form should be a Form class, not an instance. 27 self.form, self.state = form, {} 23 self.form_class = form_class 24 # A relic from the past; override get_context_data to pass extra context 25 # to the template. Left in for backwards compatibility. 26 self.state = {} 28 27 29 28 def __call__(self, request, *args, **kwargs): 30 stage = {'1': 'preview', '2': 'post'}.get(request.POST.get(self.unused_name('stage')), 'preview') 29 return self.dispatch(request, *args, **kwargs) 30 31 def dispatch(self, request, *args, **kwargs): 32 self.preview_stage = 'preview' 33 self.post_stage = 'post' 34 stages = {'1': self.preview_stage, '2': self.post_stage} 35 36 posted_stage = request.POST.get(self.unused_name(STAGE_FIELD)) 37 self.stage = stages.get(posted_stage, self.preview_stage) 38 39 # For backwards compatiblity 31 40 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) 41 42 return super(FormPreview, self).dispatch(request, *args, **kwargs) 37 43 38 44 def unused_name(self, name): 39 45 """ … … class FormPreview(object): 45 51 """ 46 52 while 1: 47 53 try: 48 f = self.form.base_fields[name]54 self.form_class.base_fields[name] 49 55 except KeyError: 50 56 break # This field name isn't being used by the form. 51 57 name += '_' 52 58 return name 53 59 54 def preview_get(self, request): 60 def _get_context_data(self, form): 61 """ For backwards compatiblity. """ 62 context = self.get_context_data() 63 context.update(self.get_context(self.request, form)) 64 return context 65 66 def get(self, request, *args, **kwargs): 55 67 "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)) 68 form_class = self.get_form_class() 69 form = self.get_form(form_class) 70 context = self._get_context_data(form) 71 self.template_name = self.form_template 72 return self.render_to_response(context) 73 74 def _check_security_hash(self, token, form): 75 expected = self.security_hash(self.request, form) 76 return constant_time_compare(token, expected) 60 77 61 78 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) 67 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)) 79 """ For backwards compatibility. failed_hash calls this method by 80 default. """ 81 self.stage = self.preview_stage 82 return self.post(request) 83 84 def form_valid(self, form): 85 context = self._get_context_data(form) 86 if self.stage == self.preview_stage: 87 self.process_preview(self.request, form, context) 88 context['hash_field'] = self.unused_name(HASH_FIELD) 89 context['hash_value'] = self.security_hash(self.request, form) 90 self.template_name = self.preview_template 91 return self.render_to_response(context) 70 92 else: 71 return render_to_response(self.form_template, context, context_instance=RequestContext(request)) 72 73 def _check_security_hash(self, token, request, form): 74 expected = self.security_hash(request, form) 75 return constant_time_compare(token, expected) 93 form_hash = self.request.POST.get(self.unused_name(HASH_FIELD), '') 94 if not self._check_security_hash(form_hash, form): 95 return self.failed_hash(self.request) # Security hash failed. 96 return self.done(self.request, form.cleaned_data) 76 97 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)) 98 def form_invalid(self, form): 99 context = self._get_context_data(form) 100 self.template_name = self.form_template 101 return render_to_response(context) 89 102 90 103 # METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ######################## 91 104 … … class FormPreview(object): 96 109 """ 97 110 return AUTO_ID 98 111 99 def get_initial(self, request ):112 def get_initial(self, request=None): 100 113 """ 101 114 Takes a request argument and returns a dictionary to pass to the form's 102 115 ``initial`` kwarg when the form is being created from an HTTP get. 103 116 """ 104 return {}117 return self.initial 105 118 106 119 def get_context(self, request, form): 107 120 "Context for template rendering." 108 return {'form': form, 'stage_field': self.unused_name('stage'), 'state': self.state} 109 121 context = { 122 'form': form, 123 'stage_field': self.unused_name(STAGE_FIELD), 124 'state': self.state 125 } 126 return context 127 128 def get_form_kwargs(self): 129 """ This is overriden to maintain backward compatibility and pass 130 the request to get_initial. """ 131 kwargs = { 132 'initial': self.get_initial(self.request), 133 'auto_id': self.get_auto_id() 134 } 135 if self.request.method in ('POST', 'PUT'): 136 kwargs.update({ 137 'data': self.request.POST, 138 'files': self.request.FILES, 139 }) 140 return kwargs 110 141 111 142 def parse_params(self, *args, **kwargs): 112 143 """ 113 Given captured args and kwargs from the URLconf, saves something in 114 self.state and/or raises Http404 if necessary. 144 Called in dispatch() prior to delegating the request to get() or post(). 145 Given captured args and kwargs from the URLconf, allows the ability to 146 save something on the instance and/or raises Http404 if necessary. 115 147 116 148 For example, this URLconf captures a user_id variable: 117 149 -
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