diff --git a/django/contrib/formtools/preview.py b/django/contrib/formtools/preview.py
index b4cdeba..b32d7d0 100644
--- a/django/contrib/formtools/preview.py
+++ b/django/contrib/formtools/preview.py
@@ -1,39 +1,41 @@
 """
 Formtools Preview application.
 """
-
-try:
-    import cPickle as pickle
-except ImportError:
-    import pickle
-
 from django.conf import settings
-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
+from django.views.generic import FormView
 
 AUTO_ID = 'formtools_%s' # Each form here uses this as its auto_id parameter.
 
-class FormPreview(object):
+class FormPreview(FormView):
     preview_template = 'formtools/preview.html'
     form_template = 'formtools/form.html'
 
     # METHODS SUBCLASSES SHOULDN'T OVERRIDE ###################################
 
-    def __init__(self, form):
+    def __init__(self, form_class, *args, **kwargs):
+        super(FormPreview, self).__init__(*args, **kwargs)
         # form should be a Form class, not an instance.
-        self.form, self.state = form, {}
+        self.form_class = form_class
+        self.state = {}
 
     def __call__(self, request, *args, **kwargs):
-        stage = {'1': 'preview', '2': 'post'}.get(request.POST.get(self.unused_name('stage')), 'preview')
+        return self.dispatch(request, *args, **kwargs)
+
+    def dispatch(self, request, *args, **kwargs):
+        self.preview_stage = 'preview'
+        self.post_stage = 'post'
+        stages = {'1': self.preview_stage, '2': self.post_stage}
+
+        posted_stage = request.POST.get(self.unused_name('stage'))
+        self.stage = stages.get(posted_stage, 'preview')
+
+        # For backwards compatiblity
         self.parse_params(*args, **kwargs)
-        try:
-            method = getattr(self, stage + '_' + request.method.lower())
-        except AttributeError:
-            raise Http404
-        return method(request)
+
+        return super(FormPreview, self).dispatch(request, *args, **kwargs)
 
     def unused_name(self, name):
         """
@@ -45,47 +47,65 @@ class FormPreview(object):
         """
         while 1:
             try:
-                f = self.form.base_fields[name]
+                self.form_class.base_fields[name]
             except KeyError:
                 break # This field name isn't being used by the form.
             name += '_'
         return name
 
-    def preview_get(self, request):
+    def _get_context_data(self, form):
+        """ For backwards compatiblity. """
+        context = self.get_context_data()
+        context.update(self.get_context(self.request, form))
+        return context
+
+    def get(self, request, *args, **kwargs):
         "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))
+        form_class = self.get_form_class()
+        form = self.get_form(form_class)
+        context = self._get_context_data(form)
+        self.template_name = self.form_template
+        return self.render_to_response(context)
+
+    def _check_security_hash(self, token, form):
+        expected = self.security_hash(self.request, form)
+        return constant_time_compare(token, expected)
+
+    def post(self, request, *args, **kwargs):
+        """ Validates the POST data. If valid, displays the preview
+        page or calls done, depending on the stage. Else, redisplays
+        form. """
+        form_class = self.get_form_class()
+        form = self.get_form(form_class)
+        if form.is_valid():
+            return self.form_valid(form)
+        else:
+            return self.form_invalid(form)
 
     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)
+        """ For backwards compatibility. failed_hash calls this method by
+        default. """
+        self.stage = self.preview_stage
+        return self.post(request)
+
+    def form_valid(self, form):
+        context = self._get_context_data(form)
+        if self.stage == self.preview_stage:
+            self.process_preview(self.request, form, 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))
+            context['hash_value'] = self.security_hash(self.request, form)
+            self.template_name = self.preview_template
+            return self.render_to_response(context)
         else:
-            return render_to_response(self.form_template, context, context_instance=RequestContext(request))
+            form_hash = self.request.POST.get(self.unused_name('hash'), '')
+            if not self._check_security_hash(form_hash, form):
+                return self.failed_hash(self.request) # Security hash failed.
+            return self.done(self.request, form.cleaned_data)
 
-    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))
+    def form_invalid(self, form):
+        context = self._get_context_data(form)
+        self.template_name = self.form_template
+        return render_to_response(context)
 
     # METHODS SUBCLASSES MIGHT OVERRIDE IF APPROPRIATE ########################
 
@@ -96,17 +116,35 @@ class FormPreview(object):
         """
         return AUTO_ID
 
-    def get_initial(self, request):
+    def get_initial(self, request=None):
         """
         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 {}
+        return self.initial
 
     def get_context(self, request, form):
         "Context for template rendering."
-        return {'form': form, 'stage_field': self.unused_name('stage'), 'state': self.state}
-
+        context = {
+            'form': form,
+            'stage_field': self.unused_name('stage'),
+            'state': self.state
+        }
+        return context
+
+    def get_form_kwargs(self):
+        """ This is overriden to maintain backward compatibility and pass
+        the request to to get_initial. """
+        kwargs = {
+            'initial': self.get_initial(self.request),
+            'auto_id': self.get_auto_id()
+        }
+        if self.request.method in ('POST', 'PUT'):
+            kwargs.update({
+                'data': self.request.POST,
+                'files': self.request.FILES,
+            })
+        return kwargs
 
     def parse_params(self, *args, **kwargs):
         """
diff --git a/django/contrib/formtools/tests/__init__.py b/django/contrib/formtools/tests/__init__.py
index 7084386..8c5fbb5 100644
--- a/django/contrib/formtools/tests/__init__.py
+++ b/django/contrib/formtools/tests/__init__.py
@@ -17,12 +17,18 @@ warnings.filterwarnings('ignore', category=PendingDeprecationWarning,
 
 success_string = "Done was called!"
 
+
 class TestFormPreview(preview.FormPreview):
     def get_context(self, request, form):
         context = super(TestFormPreview, self).get_context(request, form)
         context.update({'custom_context': True})
         return context
 
+    def get_context_data(self, **kwargs):
+        context = super(TestFormPreview, self).get_context_data(**kwargs)
+        context['more_custom_context'] = True
+        return context
+
     def get_initial(self, request):
         return {'field1': 'Works!'}
 
@@ -67,6 +73,7 @@ class PreviewTests(TestCase):
         stage = self.input % 1
         self.assertContains(response, stage, 1)
         self.assertEqual(response.context['custom_context'], True)
+        self.assertEqual(response.context['more_custom_context'], True)
         self.assertEqual(response.context['form'].initial, {'field1': 'Works!'})
 
     def test_form_preview(self):
@@ -86,6 +93,10 @@ class PreviewTests(TestCase):
         stage = self.input % 2
         self.assertContains(response, stage, 1)
 
+        # Check that the correct context was passed to the template
+        self.assertEqual(response.context['custom_context'], True)
+        self.assertEqual(response.context['more_custom_context'], True)
+
     def test_form_submit(self):
         """
         Test contrib.formtools.preview form submittal.
@@ -140,7 +151,6 @@ class PreviewTests(TestCase):
         response = self.client.post('/preview/', self.test_data)
         self.assertEqual(response.content, success_string)
 
-
     def test_form_submit_bad_hash(self):
         """
         Test contrib.formtools.preview form submittal does not proceed
@@ -154,7 +164,8 @@ class PreviewTests(TestCase):
         self.assertNotEqual(response.content, success_string)
         hash = utils.form_hmac(TestForm(self.test_data)) + "bad"
         self.test_data.update({'hash': hash})
-        response = self.client.post('/previewpreview/', self.test_data)
+        response = self.client.post('/preview/', self.test_data)
+        self.assertTemplateUsed(response, 'formtools/preview.html')
         self.assertNotEqual(response.content, success_string)
 
 
