2 Formtools Preview application.
5 import cPickle
as pickle
7 from django
.conf
import settings
8 from django
.http
import Http404
9 from django
.shortcuts
import render_to_response
10 from django
.template
.context
import RequestContext
11 from django
.utils
.hashcompat
import md5_constructor
12 from django
.contrib
.formtools
.utils
import security_hash
14 AUTO_ID
= 'formtools_%s' # Each form here uses this as its auto_id parameter.
16 class FormPreview(object):
17 preview_template
= 'formtools/preview.html'
18 form_template
= 'formtools/form.html'
20 # METHODS SUBCLASSES SHOULDN'T OVERRIDE ###################################
22 def __init__(self
, form
):
23 # form should be a Form class, not an instance.
24 self
.form
, self
.state
= form
, {}
26 def __call__(self
, request
, *args
, **kwargs
):
27 stage
= {'1': 'preview', '2': 'post'}.get(request
.POST
.get(self
.unused_name('stage')), 'preview')
28 self
.parse_params(*args
, **kwargs
)
30 method
= getattr(self
, stage
+ '_' + request
.method
.lower())
31 except AttributeError:
33 return method(request
)
35 def unused_name(self
, name
):
37 Given a first-choice name, adds an underscore to the name until it
38 reaches a name that isn't claimed by any field in the form.
40 This is calculated rather than being hard-coded so that no field names
41 are off-limits for use in the form.
45 f
= self
.form
.base_fields
[name
]
47 break # This field name isn't being used by the form.
51 def preview_get(self
, request
):
53 f
= self
.form(auto_id
=AUTO_ID
)
54 return render_to_response(self
.form_template
,
55 {'form': f
, 'stage_field': self
.unused_name('stage'), 'state': self
.state
},
56 context_instance
=RequestContext(request
))
58 def preview_post(self
, request
):
59 "Validates the POST data. If valid, displays the preview page. Else, redisplays form."
60 f
= self
.form(request
.POST
, auto_id
=AUTO_ID
)
61 context
= {'form': f
, 'stage_field': self
.unused_name('stage'), 'state': self
.state
}
63 context
['hash_field'] = self
.unused_name('hash')
64 context
['hash_value'] = self
.security_hash(request
, f
)
65 return render_to_response(self
.preview_template
, context
, context_instance
=RequestContext(request
))
67 return render_to_response(self
.form_template
, context
, context_instance
=RequestContext(request
))
69 def post_post(self
, request
):
70 "Validates the POST data. If valid, calls done(). Else, redisplays form."
71 f
= self
.form(request
.POST
, auto_id
=AUTO_ID
)
73 if self
.security_hash(request
, f
) != request
.POST
.get(self
.unused_name('hash')):
74 return self
.failed_hash(request
) # Security hash failed.
75 return self
.done(request
, f
.cleaned_data
)
77 return render_to_response(self
.form_template
,
78 {'form': f
, 'stage_field': self
.unused_name('stage'), 'state': self
.state
},
79 context_instance
=RequestContext(request
))
81 # METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
83 def parse_params(self
, *args
, **kwargs
):
85 Given captured args and kwargs from the URLconf, saves something in
86 self.state and/or raises Http404 if necessary.
88 For example, this URLconf captures a user_id variable:
90 (r'^contact/(?P<user_id>\d{1,6})/$', MyFormPreview(MyForm)),
92 In this case, the kwargs variable in parse_params would be
93 {'user_id': 32} for a request to '/contact/32/'. You can use that
94 user_id to make sure it's a valid user and/or save it for later, for
99 def security_hash(self
, request
, form
):
101 Calculates the security hash for the given HttpRequest and Form instances.
103 Subclasses may want to take into account request-specific information,
104 such as the IP address.
106 return security_hash(request
, form
)
108 def failed_hash(self
, request
):
109 "Returns an HttpResponse in the case of an invalid security hash."
110 return self
.preview_post(request
)
112 # METHODS SUBCLASSES MUST OVERRIDE ########################################
114 def done(self
, request
, cleaned_data
):
116 Does something with the cleaned_data and returns an
117 HttpResponseRedirect.
119 raise NotImplementedError('You must define a done() method on your %s subclass.' % self
.__class
__.__name
__)