from django import forms from formtools.utils import form_hmac import copy class PreviewMixin(object): ''' Mixin for a 2-stage form For usage with the default django class-based views (CreateView, UpdateView, FormView) Procedure: First display a form in a template. When submitted, execute process_preview, then redisplay the form. If submitted again and the data matches the previous submit, run done. Else run process_preview again and redisplay the form once again. ''' stage = 1 preview_template_name = None def get_template_names(self): ''' Depending on the stage, return the appropriate template Use the default template of the Django View for the first stage If a different template is wanted for the second stage, preview_template_name can be set. Otherwise the default template of the Django View is also used for the second stage. ''' if self.stage == 2 and self.preview_template_name is not None: return [self.preview_template_name] return super(PreviewMixin, self).get_template_names() def get_form(self, form_class=None): ''' Add a hash value to each form. If stage==1 (first form or previous invalid data), pass 0 as hash value ''' form = super(PreviewMixin, self).get_form(form_class) if self.stage == 2: form.fields['hash'] = forms.CharField(initial='0', widget=forms.HiddenInput) return form def form_valid(self, form): ''' Check if form was submitted the first or second time. If it is the second time and the hash-value matches, run done() Else run process_preview and return the second form. Should not be overwritten. ''' if self.security_hash(form) == self.request.POST.get('hash', '0'): return self.done(form) self.process_preview(form) self.stage = 2 self.request.POST['hash'] = self.security_hash(form) form = self.get_form(self.get_form_class()) return self.render_to_response(self.get_context_data(form=form)) def security_hash(self, form): ''' Function to return the hash value of the form ''' hash_form = copy.copy(form) hash_form.fields.pop('hash', '') return form_hmac(hash_form) def process_preview(self, form): ''' Function which is run after the form was valid for the first time. Does nothing by default. ''' pass def done(self, form): ''' Function which is run after the form was submitted the second time if it is valid and the hash values match. By default does the same as the corresponding form_valid function of the class-based view it inherits from. ''' return super(PreviewMixin, self).form_valid()