Django

Code

root/django/branches/newforms-admin/django/contrib/formtools/preview.py

Revision 7351, 5.0 kB (checked in by jkocherhans, 8 months ago)

newforms-admin: Merged from trunk up to [7350].

  • Property svn:eol-style set to native
Line 
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__)
Note: See TracBrowser for help on using the browser.