Code

Ticket #13953: django-callable-crud-redirect.diff

File django-callable-crud-redirect.diff, 8.3 KB (added by Alberto Donato <alberto.donato@…>, 4 years ago)
Line 
1Index: django/views/generic/create_update.py
2===================================================================
3--- django/views/generic/create_update.py       (revision 13431)
4+++ django/views/generic/create_update.py       (working copy)
5@@ -45,24 +45,29 @@
6     raise GenericViewError("Generic view must be called with either a model or"
7                            " form_class argument.")
8 
9-def redirect(post_save_redirect, obj):
10+def expand_redirect(post_save_redirect, obj):
11     """
12-    Returns a HttpResponseRedirect to ``post_save_redirect``.
13+    Returns a string with a redirect to ``post_save_redirect``.
14 
15     ``post_save_redirect`` should be a string, and can contain named string-
16-    substitution place holders of ``obj`` field names.
17+    substitution place holders of ``obj`` field names or a callable which
18+    receives ``obj`` as parameter and returns the url as string
19 
20     If ``post_save_redirect`` is None, then redirect to ``obj``'s URL returned
21     by ``get_absolute_url()``.  If ``obj`` has no ``get_absolute_url`` method,
22     then raise ImproperlyConfigured.
23 
24     This function is meant to handle the post_save_redirect parameter to the
25-    ``create_object`` and ``update_object`` views.
26+    ``create_object``, ``update_object`` and ``delete_object`` views.
27     """
28+
29     if post_save_redirect:
30-        return HttpResponseRedirect(post_save_redirect % obj.__dict__)
31+        if callable(post_save_redirect):
32+            return post_save_redirect(obj)
33+        else:
34+            return post_save_redirect % obj.__dict__
35     elif hasattr(obj, 'get_absolute_url'):
36-        return HttpResponseRedirect(obj.get_absolute_url())
37+        return obj.get_absolute_url()
38     else:
39         raise ImproperlyConfigured(
40             "No URL to redirect to.  Either pass a post_save_redirect"
41@@ -115,7 +120,7 @@
42             msg = ugettext("The %(verbose_name)s was created successfully.") %\
43                                     {"verbose_name": model._meta.verbose_name}
44             messages.success(request, msg, fail_silently=True)
45-            return redirect(post_save_redirect, new_object)
46+            return HttpResponseRedirect(expand_redirect(post_save_redirect, new_object))
47     else:
48         form = form_class()
49 
50@@ -158,7 +163,7 @@
51             msg = ugettext("The %(verbose_name)s was updated successfully.") %\
52                                     {"verbose_name": model._meta.verbose_name}
53             messages.success(request, msg, fail_silently=True)
54-            return redirect(post_save_redirect, obj)
55+            return HttpResponseRedirect(expand_redirect(post_save_redirect, obj))
56     else:
57         form = form_class(instance=obj)
58 
59@@ -197,11 +202,12 @@
60     obj = lookup_object(model, object_id, slug, slug_field)
61 
62     if request.method == 'POST':
63+        redirect_url = expand_redirect(post_delete_redirect, obj)
64         obj.delete()
65         msg = ugettext("The %(verbose_name)s was deleted.") %\
66                                     {"verbose_name": model._meta.verbose_name}
67         messages.success(request, msg, fail_silently=True)
68-        return HttpResponseRedirect(post_delete_redirect)
69+        return HttpResponseRedirect(redirect_url)
70     else:
71         if not template_name:
72             template_name = "%s/%s_confirm_delete.html" % (model._meta.app_label, model._meta.object_name.lower())
73Index: tests/regressiontests/views/tests/generic/create_update.py
74===================================================================
75--- tests/regressiontests/views/tests/generic/create_update.py  (revision 13431)
76+++ tests/regressiontests/views/tests/generic/create_update.py  (working copy)
77@@ -163,6 +163,54 @@
78         self.assertEqual(num_articles - 1, self.article_model.objects.count(),
79                          "An Article should have been deleted.")
80 
81+class PostSaveCallableRedirectTests(TestCase):
82+    """
83+    Verifies that the views redirect to the correct locations when
84+    using a callable to calculate the redirect URL.
85+    """
86+
87+    fixtures = ['testdata.json']
88+    article_model = Article
89+
90+    create_url = '/views/create_update/callable_redirect/create/article/'
91+    update_url = '/views/create_update/callable_redirect/update/article/old_article/'
92+    delete_url = '/views/create_update/callable_redirect/delete/article/old_article/'
93+
94+    create_redirect = '/views/crud_redirect_target/my-first-article/'
95+    update_redirect = '/views/crud_redirect_target/another-article-slug/'
96+    delete_redirect = '/views/crud_redirect_target/old_article/'
97+
98+    def test_create_article(self):
99+        num_articles = self.article_model.objects.count()
100+        response = self.client.post(self.create_url, {
101+            'title': 'My First Article',
102+            'slug': 'my-first-article',
103+            'author': '1',
104+            'date_created': datetime.datetime(2007, 6, 25),
105+        })
106+        self.assertRedirects(response, self.create_redirect)
107+        self.assertEqual(num_articles + 1, self.article_model.objects.count(),
108+                         "A new Article should have been created.")
109+
110+    def test_update_article(self):
111+        num_articles = self.article_model.objects.count()
112+        response = self.client.post(self.update_url, {
113+            'title': 'Another Article',
114+            'slug': 'another-article-slug',
115+            'author': 1,
116+            'date_created': datetime.datetime(2007, 6, 25),
117+        })
118+        self.assertRedirects(response, self.update_redirect)
119+        self.assertEqual(num_articles, self.article_model.objects.count(),
120+                         "A new Article should not have been created.")
121+
122+    def test_delete_article(self):
123+        num_articles = self.article_model.objects.count()
124+        response = self.client.post(self.delete_url)
125+        self.assertRedirects(response, self.delete_redirect)
126+        self.assertEqual(num_articles - 1, self.article_model.objects.count(),
127+                         "An Article should have been deleted.")
128+
129 class NoPostSaveNoAbsoluteUrl(PostSaveRedirectTests):
130     """
131     Tests that when no post_save_redirect is passed and no get_absolute_url
132Index: tests/regressiontests/views/views.py
133===================================================================
134--- tests/regressiontests/views/views.py        (revision 13431)
135+++ tests/regressiontests/views/views.py        (working copy)
136@@ -57,3 +57,9 @@
137     return render_to_response('debug/template_exception.html',
138         {'arg': except_args[int(n)]})
139 
140+
141+def crud_redirect_target(request, slug):
142+    """
143+    Dummy view used as target for CRUD views redirects
144+    """
145+    return HttpResponse('<html><body>Redirected from CRUD view, slug: %s</body></html>' % slug)
146Index: tests/regressiontests/views/urls.py
147===================================================================
148--- tests/regressiontests/views/urls.py (revision 13431)
149+++ tests/regressiontests/views/urls.py (working copy)
150@@ -2,6 +2,7 @@
151 from os import path
152 
153 from django.conf.urls.defaults import *
154+from django.core.urlresolvers import reverse
155 
156 from models import *
157 import views
158@@ -92,6 +93,17 @@
159         dict(post_delete_redirect='/views/create_update/', slug_field='slug',
160              model=Article)),
161 
162+    # Redirect using a callable
163+    (r'^create_update/callable_redirect/create/article/$', 'create_object',
164+     dict(post_save_redirect=lambda obj: reverse('crud_redirect_target', args=(obj.slug,)),
165+             model=Article)),
166+    (r'^create_update/callable_redirect/update/article/(?P<slug>[-\w]+)/$', 'update_object',
167+        dict(post_save_redirect=lambda obj: reverse('crud_redirect_target', args=(obj.slug,)),
168+             slug_field='slug', model=Article)),
169+    (r'^create_update/callable_redirect/delete/article/(?P<slug>[-\w]+)/$', 'delete_object',
170+        dict(post_delete_redirect=lambda obj: reverse('crud_redirect_target', args=(obj.slug,)),
171+             slug_field='slug', model=Article)),
172+
173     # No post_save_redirect and no get_absolute_url on model.
174     (r'^create_update/no_redirect/create/article/$', 'create_object',
175         dict(model=Article)),
176@@ -122,4 +134,5 @@
177 urlpatterns += patterns('regressiontests.views.views',
178     url(r'view_exception/(?P<n>\d+)/$', 'view_exception', name='view_exception'),
179     url(r'template_exception/(?P<n>\d+)/$', 'template_exception', name='template_exception'),
180+    url(r'crud_redirect_target/(?P<slug>[-\w]+)/$', 'crud_redirect_target', name='crud_redirect_target'),
181 )