Ticket #16174: cbv-formpreview1.diff

File cbv-formpreview1.diff, 10.4 KB (added by Ryan Kaskel, 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  
    11"""
    22Formtools Preview application.
    33"""
    4 
    5 try:
    6     import cPickle as pickle
    7 except ImportError:
    8     import pickle
    9 
    104from django.conf import settings
    11 from django.http import Http404
    125from django.shortcuts import render_to_response
    13 from django.template.context import RequestContext
    146from django.utils.crypto import constant_time_compare
    157from django.contrib.formtools.utils import form_hmac
     8from django.views.generic import FormView
    169
    1710AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter.
     11STAGE_FIELD = 'stage'
     12HASH_FIELD = 'hash'
    1813
    19 class FormPreview(object):
     14class FormPreview(FormView):
    2015    preview_template = 'formtools/preview.html'
    2116    form_template = 'formtools/form.html'
    2217
    2318    # METHODS SUBCLASSES SHOULDN'T OVERRIDE ###################################
    2419
    25     def __init__(self, form):
     20    def __init__(self, form_class, *args, **kwargs):
     21        super(FormPreview, self).__init__(*args, **kwargs)
    2622        # 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 = {}
    2827
    2928    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
    3140        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)
    3743
    3844    def unused_name(self, name):
    3945        """
    class FormPreview(object):  
    4551        """
    4652        while 1:
    4753            try:
    48                 f = self.form.base_fields[name]
     54                self.form_class.base_fields[name]
    4955            except KeyError:
    5056                break # This field name isn't being used by the form.
    5157            name += '_'
    5258        return name
    5359
    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):
    5567        "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)
    6077
    6178    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)
    7092        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)
    7697
    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)
    89102
    90103    # METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
    91104
    class FormPreview(object):  
    96109        """
    97110        return AUTO_ID
    98111
    99     def get_initial(self, request):
     112    def get_initial(self, request=None):
    100113        """
    101114        Takes a request argument and returns a dictionary to pass to the form's
    102115        ``initial`` kwarg when the form is being created from an HTTP get.
    103116        """
    104         return {}
     117        return self.initial
    105118
    106119    def get_context(self, request, form):
    107120        "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
    110141
    111142    def parse_params(self, *args, **kwargs):
    112143        """
    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.
    115147
    116148        For example, this URLconf captures a user_id variable:
    117149
  • 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,  
    1717
    1818success_string = "Done was called!"
    1919
     20
    2021class TestFormPreview(preview.FormPreview):
    2122    def get_context(self, request, form):
    2223        context = super(TestFormPreview, self).get_context(request, form)
    2324        context.update({'custom_context': True})
    2425        return context
    2526
     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
    2632    def get_initial(self, request):
    2733        return {'field1': 'Works!'}
    2834
    class PreviewTests(TestCase):  
    6773        stage = self.input % 1
    6874        self.assertContains(response, stage, 1)
    6975        self.assertEqual(response.context['custom_context'], True)
     76        self.assertEqual(response.context['more_custom_context'], True)
    7077        self.assertEqual(response.context['form'].initial, {'field1': 'Works!'})
    7178
    7279    def test_form_preview(self):
    class PreviewTests(TestCase):  
    8693        stage = self.input % 2
    8794        self.assertContains(response, stage, 1)
    8895
     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
    89100    def test_form_submit(self):
    90101        """
    91102        Test contrib.formtools.preview form submittal.
    class PreviewTests(TestCase):  
    140151        response = self.client.post('/preview/', self.test_data)
    141152        self.assertEqual(response.content, success_string)
    142153
    143 
    144154    def test_form_submit_bad_hash(self):
    145155        """
    146156        Test contrib.formtools.preview form submittal does not proceed
    class PreviewTests(TestCase):  
    154164        self.assertNotEqual(response.content, success_string)
    155165        hash = utils.form_hmac(TestForm(self.test_data)) + "bad"
    156166        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')
    158169        self.assertNotEqual(response.content, success_string)
    159170
    160171
Back to Top