Author: lukeplant
Date: 2011-05-25 10:31:47 -0700 (Wed, 25 May 2011)
New Revision: 16279

Modified:
   django/branches/releases/1.3.X/django/core/handlers/base.py
   django/branches/releases/1.3.X/django/template/response.py
   django/branches/releases/1.3.X/django/utils/decorators.py
   django/branches/releases/1.3.X/docs/ref/template-response.txt
   django/branches/releases/1.3.X/tests/regressiontests/utils/decorators.py
Log:
[1.3.X] Fixed #16004 - csrf_protect does not send cookie if view returns 
TemplateResponse

The root bug was in decorator_from_middleware, and the fix also corrects
bugs with gzip_page and other decorators.

Backport of [16276] from trunk.

Modified: django/branches/releases/1.3.X/django/core/handlers/base.py
===================================================================
--- django/branches/releases/1.3.X/django/core/handlers/base.py 2011-05-25 
17:31:36 UTC (rev 16278)
+++ django/branches/releases/1.3.X/django/core/handlers/base.py 2011-05-25 
17:31:47 UTC (rev 16279)
@@ -133,7 +133,7 @@
                 if hasattr(response, 'render') and callable(response.render):
                     for middleware_method in 
self._template_response_middleware:
                         response = middleware_method(request, response)
-                    response.render()
+                    response = response.render()
 
             except http.Http404, e:
                 logger.warning('Not Found: %s' % request.path,

Modified: django/branches/releases/1.3.X/django/template/response.py
===================================================================
--- django/branches/releases/1.3.X/django/template/response.py  2011-05-25 
17:31:36 UTC (rev 16278)
+++ django/branches/releases/1.3.X/django/template/response.py  2011-05-25 
17:31:47 UTC (rev 16279)
@@ -92,11 +92,14 @@
 
         Returns the baked response instance.
         """
+        retval = self
         if not self._is_rendered:
             self._set_content(self.rendered_content)
             for post_callback in self._post_render_callbacks:
-                post_callback(self)
-        return self
+                newretval = post_callback(retval)
+                if newretval is not None:
+                    retval = newretval
+        return retval
 
     is_rendered = property(lambda self: self._is_rendered)
 

Modified: django/branches/releases/1.3.X/django/utils/decorators.py
===================================================================
--- django/branches/releases/1.3.X/django/utils/decorators.py   2011-05-25 
17:31:36 UTC (rev 16278)
+++ django/branches/releases/1.3.X/django/utils/decorators.py   2011-05-25 
17:31:47 UTC (rev 16279)
@@ -97,10 +97,17 @@
                         if result is not None:
                             return result
                     raise
-                if hasattr(middleware, 'process_response'):
-                    result = middleware.process_response(request, response)
-                    if result is not None:
-                        return result
+                if hasattr(response, 'render') and callable(response.render):
+                    if hasattr(middleware, 'process_template_response'):
+                        response = 
middleware.process_template_response(request, response)
+                    # Defer running of process_response until after the 
template
+                    # has been rendered:
+                    if hasattr(middleware, 'process_response'):
+                        callback = lambda response: 
middleware.process_response(request, response)
+                        response.add_post_render_callback(callback)
+                else:
+                    if hasattr(middleware, 'process_response'):
+                        return middleware.process_response(request, response)
                 return response
             return wraps(view_func, 
assigned=available_attrs(view_func))(_wrapped_view)
         return _decorator

Modified: django/branches/releases/1.3.X/docs/ref/template-response.txt
===================================================================
--- django/branches/releases/1.3.X/docs/ref/template-response.txt       
2011-05-25 17:31:36 UTC (rev 16278)
+++ django/branches/releases/1.3.X/docs/ref/template-response.txt       
2011-05-25 17:31:47 UTC (rev 16279)
@@ -119,6 +119,10 @@
     rendered :class:`~django.template.response.SimpleTemplateResponse`
     instance.
 
+    If the callback returns a value that is not `None`, this will be
+    used as the response instead of the original response object (and
+    will be passed to the next post rendering callback etc.)
+
 .. method:: SimpleTemplateResponse.render():
 
     Sets :attr:`response.content` to the result obtained by

Modified: 
django/branches/releases/1.3.X/tests/regressiontests/utils/decorators.py
===================================================================
--- django/branches/releases/1.3.X/tests/regressiontests/utils/decorators.py    
2011-05-25 17:31:36 UTC (rev 16278)
+++ django/branches/releases/1.3.X/tests/regressiontests/utils/decorators.py    
2011-05-25 17:31:47 UTC (rev 16279)
@@ -1,5 +1,7 @@
 from django.http import HttpResponse
 from django.middleware.doc import XViewMiddleware
+from django.template import Template, Context
+from django.template.response import TemplateResponse
 from django.test import TestCase, RequestFactory
 from django.utils.decorators import decorator_from_middleware
 
@@ -19,6 +21,26 @@
 class_xview = xview_dec(ClassXView())
 
 
+class FullMiddleware(object):
+    def process_request(self, request):
+        request.process_request_reached = True
+
+    def process_view(sef, request, view_func, view_args, view_kwargs):
+        request.process_view_reached = True
+
+    def process_template_response(self, request, response):
+        request.process_template_response_reached = True
+        return response
+
+    def process_response(self, request, response):
+        # This should never receive unrendered content.
+        request.process_response_content = response.content
+        request.process_response_reached = True
+        return response
+
+full_dec = decorator_from_middleware(FullMiddleware)
+
+
 class DecoratorFromMiddlewareTests(TestCase):
     """
     Tests for view decorators created using
@@ -37,3 +59,48 @@
         Test a middleware that implements process_view, operating on a 
callable class.
         """
         class_xview(self.rf.get('/'))
+
+    def test_full_dec_normal(self):
+        """
+        Test that all methods of middleware are called for normal HttpResponses
+        """
+
+        @full_dec
+        def normal_view(request):
+            t = Template("Hello world")
+            return HttpResponse(t.render(Context({})))
+
+        request = self.rf.get('/')
+        response = normal_view(request)
+        self.assertTrue(getattr(request, 'process_request_reached', False))
+        self.assertTrue(getattr(request, 'process_view_reached', False))
+        # process_template_response must not be called for HttpResponse
+        self.assertFalse(getattr(request, 'process_template_response_reached', 
False))
+        self.assertTrue(getattr(request, 'process_response_reached', False))
+
+    def test_full_dec_templateresponse(self):
+        """
+        Test that all methods of middleware are called for TemplateResponses in
+        the right sequence.
+        """
+
+        @full_dec
+        def template_response_view(request):
+            t = Template("Hello world")
+            return TemplateResponse(request, t, {})
+
+        request = self.rf.get('/')
+        response = template_response_view(request)
+        self.assertTrue(getattr(request, 'process_request_reached', False))
+        self.assertTrue(getattr(request, 'process_view_reached', False))
+        self.assertTrue(getattr(request, 'process_template_response_reached', 
False))
+        # response must not be rendered yet.
+        self.assertFalse(response._is_rendered)
+        # process_response must not be called until after response is rendered,
+        # otherwise some decorators like csrf_protect and gzip_page will not
+        # work correctly. See #16004
+        self.assertFalse(getattr(request, 'process_response_reached', False))
+        response.render()
+        self.assertTrue(getattr(request, 'process_response_reached', False))
+        # Check that process_response saw the rendered content
+        self.assertEqual(request.process_response_content, "Hello world")

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-updates?hl=en.

Reply via email to