Package django :: Package contrib :: Package formtools :: Module preview
[hide private]
[frames] | no frames]

Source Code for Module django.contrib.formtools.preview

  1  """ 
  2  Formtools Preview application. 
  3  """ 
  4   
  5  from django.conf import settings 
  6  from django.http import Http404 
  7  from django.shortcuts import render_to_response 
  8  from django.template.context import RequestContext 
  9  import cPickle as pickle 
 10  import md5 
 11   
 12  AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter. 
 13   
14 -class FormPreview(object):
15 preview_template = 'formtools/preview.html' 16 form_template = 'formtools/form.html' 17 18 # METHODS SUBCLASSES SHOULDN'T OVERRIDE ################################### 19
20 - def __init__(self, form):
21 # form should be a Form class, not an instance. 22 self.form, self.state = form, {}
23
24 - def __call__(self, request, *args, **kwargs):
25 stage = {'1': 'preview', '2': 'post'}.get(request.POST.get(self.unused_name('stage')), 'preview') 26 self.parse_params(*args, **kwargs) 27 try: 28 method = getattr(self, stage + '_' + request.method.lower()) 29 except AttributeError: 30 raise Http404 31 return method(request)
32
33 - def unused_name(self, name):
34 """ 35 Given a first-choice name, adds an underscore to the name until it 36 reaches a name that isn't claimed by any field in the form. 37 38 This is calculated rather than being hard-coded so that no field names 39 are off-limits for use in the form. 40 """ 41 while 1: 42 try: 43 f = self.form.base_fields[name] 44 except KeyError: 45 break # This field name isn't being used by the form. 46 name += '_' 47 return name
48
49 - def preview_get(self, request):
50 "Displays the form" 51 f = self.form(auto_id=AUTO_ID) 52 return render_to_response(self.form_template, 53 {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state}, 54 context_instance=RequestContext(request))
55
56 - def preview_post(self, request):
57 "Validates the POST data. If valid, displays the preview page. Else, redisplays form." 58 f = self.form(request.POST, auto_id=AUTO_ID) 59 context = {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state} 60 if f.is_valid(): 61 context['hash_field'] = self.unused_name('hash') 62 context['hash_value'] = self.security_hash(request, f) 63 return render_to_response(self.preview_template, context, context_instance=RequestContext(request)) 64 else: 65 return render_to_response(self.form_template, context, context_instance=RequestContext(request))
66
67 - def post_post(self, request):
68 "Validates the POST data. If valid, calls done(). Else, redisplays form." 69 f = self.form(request.POST, auto_id=AUTO_ID) 70 if f.is_valid(): 71 if self.security_hash(request, f) != request.POST.get(self.unused_name('hash')): 72 return self.failed_hash(request) # Security hash failed. 73 return self.done(request, f.cleaned_data) 74 else: 75 return render_to_response(self.form_template, 76 {'form': f, 'stage_field': self.unused_name('stage'), 'state': self.state}, 77 context_instance=RequestContext(request))
78 79 # METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ######################## 80
81 - def parse_params(self, *args, **kwargs):
82 """ 83 Given captured args and kwargs from the URLconf, saves something in 84 self.state and/or raises Http404 if necessary. 85 86 For example, this URLconf captures a user_id variable: 87 88 (r'^contact/(?P<user_id>\d{1,6})/$', MyFormPreview(MyForm)), 89 90 In this case, the kwargs variable in parse_params would be 91 {'user_id': 32} for a request to '/contact/32/'. You can use that 92 user_id to make sure it's a valid user and/or save it for later, for 93 use in done(). 94 """ 95 pass
96
97 - def security_hash(self, request, form):
98 """ 99 Calculates the security hash for the given Form instance. 100 101 This creates a list of the form field names/values in a deterministic 102 order, pickles the result with the SECRET_KEY setting and takes an md5 103 hash of that. 104 105 Subclasses may want to take into account request-specific information 106 such as the IP address. 107 """ 108 data = [(bf.name, bf.data or '') for bf in form] + [settings.SECRET_KEY] 109 # Use HIGHEST_PROTOCOL because it's the most efficient. It requires 110 # Python 2.3, but Django requires 2.3 anyway, so that's OK. 111 pickled = pickle.dumps(data, pickle.HIGHEST_PROTOCOL) 112 return md5.new(pickled).hexdigest()
113
114 - def failed_hash(self, request):
115 "Returns an HttpResponse in the case of an invalid security hash." 116 return self.preview_post(request)
117 118 # METHODS SUBCLASSES MUST OVERRIDE ######################################## 119
120 - def done(self, request, cleaned_data):
121 """ 122 Does something with the cleaned_data and returns an 123 HttpResponseRedirect. 124 """ 125 raise NotImplementedError('You must define a done() method on your %s subclass.' % self.__class__.__name__)
126