Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixed #12815 -- Added TemplateResponse, a lazy-evaluated Response cla…
…ss. Thanks to Simon Willison for the original idea, and to Mikhail Korobov and Ivan Sagalaev for their assistance, including the draft patch from Mikhail. git-svn-id: http://code.djangoproject.com/svn/django/trunk@14850 bcc190cf-cafb-0310-a4f2-bffc1f526a37
- Loading branch information
1 parent
22fc30b
commit e0dcd76
Showing
19 changed files
with
842 additions
and
210 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
from django.http import HttpResponse | ||
from django.template import loader, Context, RequestContext | ||
|
||
class ContentNotRenderedError(Exception): | ||
pass | ||
|
||
class SimpleTemplateResponse(HttpResponse): | ||
|
||
def __init__(self, template, context=None, mimetype=None, status=None, | ||
content_type=None): | ||
# It would seem obvious to call these next two members 'template' and | ||
# 'context', but those names are reserved as part of the test Client API. | ||
# To avoid the name collision, we use | ||
# tricky-to-debug problems | ||
self.template_name = template | ||
self.context_data = context | ||
|
||
# _is_rendered tracks whether the template and context has been baked into | ||
# a final response. | ||
self._is_rendered = False | ||
|
||
# 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 resolve_template(self, template): | ||
"Accepts a template object, path-to-template or list of paths" | ||
if isinstance(template, (list, tuple)): | ||
return loader.select_template(template) | ||
elif isinstance(template, basestring): | ||
return loader.get_template(template) | ||
else: | ||
return template | ||
|
||
def resolve_context(self, context): | ||
"""Convert context data into a full Context object | ||
(assuming it isn't already a Context object). | ||
""" | ||
if isinstance(context, Context): | ||
return context | ||
else: | ||
return Context(context) | ||
|
||
@property | ||
def rendered_content(self): | ||
"""Returns the freshly rendered content for the template and context | ||
described by the TemplateResponse. | ||
This *does not* set the final content of the response. To set the | ||
response content, you must either call render(), or set the | ||
content explicitly using the value of this property. | ||
""" | ||
template = self.resolve_template(self.template_name) | ||
context = self.resolve_context(self.context_data) | ||
content = template.render(context) | ||
return content | ||
|
||
def render(self): | ||
"""Render (thereby finalizing) the content of the response. | ||
If the content has already been rendered, this is a no-op. | ||
Returns the baked response instance. | ||
""" | ||
if not self._is_rendered: | ||
self._set_content(self.rendered_content) | ||
return self | ||
|
||
is_rendered = property(lambda self: self._is_rendered) | ||
|
||
def __iter__(self): | ||
if not self._is_rendered: | ||
raise ContentNotRenderedError('The response content must be rendered before it can be iterated over.') | ||
return super(SimpleTemplateResponse, self).__iter__() | ||
|
||
def _get_content(self): | ||
if not self._is_rendered: | ||
raise ContentNotRenderedError('The response content must be rendered before it can be accessed.') | ||
return super(SimpleTemplateResponse, self)._get_content() | ||
|
||
def _set_content(self, value): | ||
"Overrides rendered content, unless you later call render()" | ||
super(SimpleTemplateResponse, self)._set_content(value) | ||
self._is_rendered = True | ||
|
||
content = property(_get_content, _set_content) | ||
|
||
|
||
class TemplateResponse(SimpleTemplateResponse): | ||
def __init__(self, request, template, context=None, mimetype=None, | ||
status=None, content_type=None): | ||
# self.request gets over-written by django.test.client.Client - and | ||
# unlike context_data and template_name the _request should not | ||
# be considered part of the public API. | ||
self._request = request | ||
super(TemplateResponse, self).__init__( | ||
template, context, mimetype, status, content_type) | ||
|
||
def resolve_context(self, context): | ||
"""Convert context data into a full RequestContext object | ||
(assuming it isn't already a Context object). | ||
""" | ||
if isinstance(context, Context): | ||
return context | ||
else: | ||
return RequestContext(self._request, context) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.