Index: django/views/generic/create_update.py
===================================================================
--- django/views/generic/create_update.py	(revision 13431)
+++ django/views/generic/create_update.py	(working copy)
@@ -45,24 +45,29 @@
     raise GenericViewError("Generic view must be called with either a model or"
                            " form_class argument.")
 
-def redirect(post_save_redirect, obj):
+def expand_redirect(post_save_redirect, obj):
     """
-    Returns a HttpResponseRedirect to ``post_save_redirect``.
+    Returns a string with a redirect to ``post_save_redirect``.
 
     ``post_save_redirect`` should be a string, and can contain named string-
-    substitution place holders of ``obj`` field names.
+    substitution place holders of ``obj`` field names or a callable which
+    receives ``obj`` as parameter and returns the url as string
 
     If ``post_save_redirect`` is None, then redirect to ``obj``'s URL returned
     by ``get_absolute_url()``.  If ``obj`` has no ``get_absolute_url`` method,
     then raise ImproperlyConfigured.
 
     This function is meant to handle the post_save_redirect parameter to the
-    ``create_object`` and ``update_object`` views.
+    ``create_object``, ``update_object`` and ``delete_object`` views.
     """
+
     if post_save_redirect:
-        return HttpResponseRedirect(post_save_redirect % obj.__dict__)
+        if callable(post_save_redirect):
+            return post_save_redirect(obj)
+        else:
+            return post_save_redirect % obj.__dict__
     elif hasattr(obj, 'get_absolute_url'):
-        return HttpResponseRedirect(obj.get_absolute_url())
+        return obj.get_absolute_url()
     else:
         raise ImproperlyConfigured(
             "No URL to redirect to.  Either pass a post_save_redirect"
@@ -115,7 +120,7 @@
             msg = ugettext("The %(verbose_name)s was created successfully.") %\
                                     {"verbose_name": model._meta.verbose_name}
             messages.success(request, msg, fail_silently=True)
-            return redirect(post_save_redirect, new_object)
+            return HttpResponseRedirect(expand_redirect(post_save_redirect, new_object))
     else:
         form = form_class()
 
@@ -158,7 +163,7 @@
             msg = ugettext("The %(verbose_name)s was updated successfully.") %\
                                     {"verbose_name": model._meta.verbose_name}
             messages.success(request, msg, fail_silently=True)
-            return redirect(post_save_redirect, obj)
+            return HttpResponseRedirect(expand_redirect(post_save_redirect, obj))
     else:
         form = form_class(instance=obj)
 
@@ -197,11 +202,12 @@
     obj = lookup_object(model, object_id, slug, slug_field)
 
     if request.method == 'POST':
+        redirect_url = expand_redirect(post_delete_redirect, obj)
         obj.delete()
         msg = ugettext("The %(verbose_name)s was deleted.") %\
                                     {"verbose_name": model._meta.verbose_name}
         messages.success(request, msg, fail_silently=True)
-        return HttpResponseRedirect(post_delete_redirect)
+        return HttpResponseRedirect(redirect_url)
     else:
         if not template_name:
             template_name = "%s/%s_confirm_delete.html" % (model._meta.app_label, model._meta.object_name.lower())
Index: tests/regressiontests/views/tests/generic/create_update.py
===================================================================
--- tests/regressiontests/views/tests/generic/create_update.py	(revision 13431)
+++ tests/regressiontests/views/tests/generic/create_update.py	(working copy)
@@ -163,6 +163,54 @@
         self.assertEqual(num_articles - 1, self.article_model.objects.count(),
                          "An Article should have been deleted.")
 
+class PostSaveCallableRedirectTests(TestCase):
+    """
+    Verifies that the views redirect to the correct locations when
+    using a callable to calculate the redirect URL.
+    """
+
+    fixtures = ['testdata.json']
+    article_model = Article
+
+    create_url = '/views/create_update/callable_redirect/create/article/'
+    update_url = '/views/create_update/callable_redirect/update/article/old_article/'
+    delete_url = '/views/create_update/callable_redirect/delete/article/old_article/'
+
+    create_redirect = '/views/crud_redirect_target/my-first-article/'
+    update_redirect = '/views/crud_redirect_target/another-article-slug/'
+    delete_redirect = '/views/crud_redirect_target/old_article/'
+
+    def test_create_article(self):
+        num_articles = self.article_model.objects.count()
+        response = self.client.post(self.create_url, {
+            'title': 'My First Article',
+            'slug': 'my-first-article',
+            'author': '1',
+            'date_created': datetime.datetime(2007, 6, 25),
+        })
+        self.assertRedirects(response, self.create_redirect)
+        self.assertEqual(num_articles + 1, self.article_model.objects.count(),
+                         "A new Article should have been created.")
+
+    def test_update_article(self):
+        num_articles = self.article_model.objects.count()
+        response = self.client.post(self.update_url, {
+            'title': 'Another Article',
+            'slug': 'another-article-slug',
+            'author': 1,
+            'date_created': datetime.datetime(2007, 6, 25),
+        })
+        self.assertRedirects(response, self.update_redirect)
+        self.assertEqual(num_articles, self.article_model.objects.count(),
+                         "A new Article should not have been created.")
+
+    def test_delete_article(self):
+        num_articles = self.article_model.objects.count()
+        response = self.client.post(self.delete_url)
+        self.assertRedirects(response, self.delete_redirect)
+        self.assertEqual(num_articles - 1, self.article_model.objects.count(),
+                         "An Article should have been deleted.")
+
 class NoPostSaveNoAbsoluteUrl(PostSaveRedirectTests):
     """
     Tests that when no post_save_redirect is passed and no get_absolute_url
Index: tests/regressiontests/views/views.py
===================================================================
--- tests/regressiontests/views/views.py	(revision 13431)
+++ tests/regressiontests/views/views.py	(working copy)
@@ -57,3 +57,9 @@
     return render_to_response('debug/template_exception.html',
         {'arg': except_args[int(n)]})
 
+
+def crud_redirect_target(request, slug):
+    """
+    Dummy view used as target for CRUD views redirects
+    """
+    return HttpResponse('<html><body>Redirected from CRUD view, slug: %s</body></html>' % slug)
Index: tests/regressiontests/views/urls.py
===================================================================
--- tests/regressiontests/views/urls.py	(revision 13431)
+++ tests/regressiontests/views/urls.py	(working copy)
@@ -2,6 +2,7 @@
 from os import path
 
 from django.conf.urls.defaults import *
+from django.core.urlresolvers import reverse
 
 from models import *
 import views
@@ -92,6 +93,17 @@
         dict(post_delete_redirect='/views/create_update/', slug_field='slug',
              model=Article)),
 
+    # Redirect using a callable
+    (r'^create_update/callable_redirect/create/article/$', 'create_object',
+     dict(post_save_redirect=lambda obj: reverse('crud_redirect_target', args=(obj.slug,)),
+             model=Article)),
+    (r'^create_update/callable_redirect/update/article/(?P<slug>[-\w]+)/$', 'update_object',
+        dict(post_save_redirect=lambda obj: reverse('crud_redirect_target', args=(obj.slug,)),
+             slug_field='slug', model=Article)),
+    (r'^create_update/callable_redirect/delete/article/(?P<slug>[-\w]+)/$', 'delete_object',
+        dict(post_delete_redirect=lambda obj: reverse('crud_redirect_target', args=(obj.slug,)),
+             slug_field='slug', model=Article)),
+
     # No post_save_redirect and no get_absolute_url on model.
     (r'^create_update/no_redirect/create/article/$', 'create_object',
         dict(model=Article)),
@@ -122,4 +134,5 @@
 urlpatterns += patterns('regressiontests.views.views',
     url(r'view_exception/(?P<n>\d+)/$', 'view_exception', name='view_exception'),
     url(r'template_exception/(?P<n>\d+)/$', 'template_exception', name='template_exception'),
+    url(r'crud_redirect_target/(?P<slug>[-\w]+)/$', 'crud_redirect_target', name='crud_redirect_target'),
 )
