Source code for django.contrib.formtools.preview

"""
Formtools Preview application.
"""

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.contrib.formtools.utils import form_hmac

AUTO_ID = 'formtools_%s'  # Each form here uses this as its auto_id parameter.


[docs]class FormPreview(object): preview_template = 'formtools/preview.html' form_template = 'formtools/form.html' # METHODS SUBCLASSES SHOULDN'T OVERRIDE ################################### def __init__(self, form): # form should be a Form class, not an instance. self.form, self.state = form, {} def __call__(self, request, *args, **kwargs): stage = {'1': 'preview', '2': 'post'}.get(request.POST.get(self.unused_name('stage')), 'preview') self.parse_params(*args, **kwargs) try: method = getattr(self, stage + '_' + request.method.lower()) except AttributeError: raise Http404 return method(request) def unused_name(self, name): """ Given a first-choice name, adds an underscore to the name until it reaches a name that isn't claimed by any field in the form. This is calculated rather than being hard-coded so that no field names are off-limits for use in the form. """ while 1: try: self.form.base_fields[name] except KeyError: break # This field name isn't being used by the form. name += '_' return name def preview_get(self, request): "Displays the form" f = self.form(auto_id=self.get_auto_id(), initial=self.get_initial(request)) return render_to_response(self.form_template, self.get_context(request, f), context_instance=RequestContext(request)) def preview_post(self, request): "Validates the POST data. If valid, displays the preview page. Else, redisplays form." f = self.form(request.POST, auto_id=self.get_auto_id()) context = self.get_context(request, f) if f.is_valid(): self.process_preview(request, f, context) context['hash_field'] = self.unused_name('hash') context['hash_value'] = self.security_hash(request, f) return render_to_response(self.preview_template, context, context_instance=RequestContext(request)) else: return render_to_response(self.form_template, context, context_instance=RequestContext(request)) def _check_security_hash(self, token, request, form): expected = self.security_hash(request, form) return constant_time_compare(token, expected) def post_post(self, request): "Validates the POST data. If valid, calls done(). Else, redisplays form." f = self.form(request.POST, auto_id=self.get_auto_id()) if f.is_valid(): if not self._check_security_hash(request.POST.get(self.unused_name('hash'), ''), request, f): return self.failed_hash(request) # Security hash failed. return self.done(request, f.cleaned_data) else: return render_to_response(self.form_template, self.get_context(request, f), context_instance=RequestContext(request)) # METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ######################## def get_auto_id(self): """ Hook to override the ``auto_id`` kwarg for the form. Needed when rendering two form previews in the same template. """ return AUTO_ID def get_initial(self, request): """ Takes a request argument and returns a dictionary to pass to the form's ``initial`` kwarg when the form is being created from an HTTP get. """ return {} def get_context(self, request, form): "Context for template rendering." return {'form': form, 'stage_field': self.unused_name('stage'), 'state': self.state} def parse_params(self, *args, **kwargs): """ Given captured args and kwargs from the URLconf, saves something in self.state and/or raises Http404 if necessary. For example, this URLconf captures a user_id variable: (r'^contact/(?P<user_id>\d{1,6})/$', MyFormPreview(MyForm)), In this case, the kwargs variable in parse_params would be {'user_id': 32} for a request to '/contact/32/'. You can use that user_id to make sure it's a valid user and/or save it for later, for use in done(). """ pass
[docs] def process_preview(self, request, form, context): """ Given a validated form, performs any extra processing before displaying the preview page, and saves any extra data in context. """ pass
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 failed_hash(self, request): "Returns an HttpResponse in the case of an invalid security hash." return self.preview_post(request) # METHODS SUBCLASSES MUST OVERRIDE ######################################## def done(self, request, cleaned_data): """ Does something with the cleaned_data and returns an HttpResponseRedirect. """ raise NotImplementedError('You must define a done() method on your %s subclass.' % self.__class__.__name__)