diff options
| author | Russell Keith-Magee <russell@keith-magee.com> | 2011-01-24 14:24:35 +0000 |
|---|---|---|
| committer | Russell Keith-Magee <russell@keith-magee.com> | 2011-01-24 14:24:35 +0000 |
| commit | 3f528e10d50ff7ba19a8a9e6cb2f9417a1e7f270 (patch) | |
| tree | d99a22a125586c1f932171a1c78c1daf5b0e6371 /django | |
| parent | 3d7afd5d2b64546cdcfd812d2cecb61795e788f0 (diff) | |
Fixed #15012 -- Added post-rendering callbacks to TemplateResponse so that decorators (in particular, the cache decorator) can defer processing until after rendering has occurred. Thanks to Joshua Ginsberg for the draft patch.
git-svn-id: http://code.djangoproject.com/svn/django/trunk@15295 bcc190cf-cafb-0310-a4f2-bffc1f526a37
Diffstat (limited to 'django')
| -rw-r--r-- | django/middleware/cache.py | 8 | ||||
| -rw-r--r-- | django/template/response.py | 48 |
2 files changed, 54 insertions, 2 deletions
diff --git a/django/middleware/cache.py b/django/middleware/cache.py index 907edbb16f..ab72db3c18 100644 --- a/django/middleware/cache.py +++ b/django/middleware/cache.py @@ -52,6 +52,7 @@ from django.conf import settings from django.core.cache import get_cache, DEFAULT_CACHE_ALIAS from django.utils.cache import get_cache_key, learn_cache_key, patch_response_headers, get_max_age + class UpdateCacheMiddleware(object): """ Response-phase cache middleware that updates the cache if the response is @@ -87,7 +88,12 @@ class UpdateCacheMiddleware(object): patch_response_headers(response, timeout) if timeout: cache_key = learn_cache_key(request, response, timeout, self.key_prefix, cache=self.cache) - self.cache.set(cache_key, response, timeout) + if hasattr(response, 'render') and callable(response.render): + response.add_post_render_callback( + lambda r: self.cache.set(cache_key, r, timeout) + ) + else: + self.cache.set(cache_key, response, timeout) return response class FetchFromCacheMiddleware(object): diff --git a/django/template/response.py b/django/template/response.py index 629461aa5e..a6ef893520 100644 --- a/django/template/response.py +++ b/django/template/response.py @@ -19,12 +19,30 @@ class SimpleTemplateResponse(HttpResponse): # a final response. self._is_rendered = False + self._post_render_callbacks = [] + # content argument doesn't make sense here because it will be replaced # with rendered template so we always pass empty string in order to # prevent errors and provide shorter signature. super(SimpleTemplateResponse, self).__init__('', mimetype, status, content_type) + def __getstate__(self): + """Pickling support function. + + Ensures that the object can't be pickled before it has been + rendered, and that the pickled state only includes rendered + data, not the data used to construct the response. + """ + obj_dict = self.__dict__.copy() + if not self._is_rendered: + raise ContentNotRenderedError('The response content must be rendered before it can be pickled.') + del obj_dict['template_name'] + del obj_dict['context_data'] + del obj_dict['_post_render_callbacks'] + + return obj_dict + def resolve_template(self, template): "Accepts a template object, path-to-template or list of paths" if isinstance(template, (list, tuple)): @@ -57,6 +75,16 @@ class SimpleTemplateResponse(HttpResponse): content = template.render(context) return content + def add_post_render_callback(self, callback): + """Add a new post-rendering callback. + + If the response has already been rendered, invoke the callback immediately. + """ + if self._is_rendered: + callback(self) + else: + self._post_render_callbacks.append(callback) + def render(self): """Render (thereby finalizing) the content of the response. @@ -66,6 +94,8 @@ class SimpleTemplateResponse(HttpResponse): """ if not self._is_rendered: self._set_content(self.rendered_content) + for post_callback in self._post_render_callbacks: + post_callback(self) return self is_rendered = property(lambda self: self._is_rendered) @@ -81,7 +111,7 @@ class SimpleTemplateResponse(HttpResponse): return super(SimpleTemplateResponse, self)._get_content() def _set_content(self, value): - "Overrides rendered content, unless you later call render()" + "Sets the content for the response" super(SimpleTemplateResponse, self)._set_content(value) self._is_rendered = True @@ -101,6 +131,20 @@ class TemplateResponse(SimpleTemplateResponse): super(TemplateResponse, self).__init__( template, context, mimetype, status, content_type) + def __getstate__(self): + """Pickling support function. + + Ensures that the object can't be pickled before it has been + rendered, and that the pickled state only includes rendered + data, not the data used to construct the response. + """ + obj_dict = super(TemplateResponse, self).__getstate__() + + del obj_dict['_request'] + del obj_dict['_current_app'] + + return obj_dict + def resolve_context(self, context): """Convert context data into a full RequestContext object (assuming it isn't already a Context object). @@ -109,3 +153,5 @@ class TemplateResponse(SimpleTemplateResponse): return context else: return RequestContext(self._request, context, current_app=self._current_app) + + |
