Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/django/template/response.py: 37%
65 statements
« prev ^ index » next coverage.py v7.0.5, created at 2023-01-17 06:13 +0000
« prev ^ index » next coverage.py v7.0.5, created at 2023-01-17 06:13 +0000
1from django.http import HttpResponse
3from .loader import get_template, select_template
6class ContentNotRenderedError(Exception):
7 pass
10class SimpleTemplateResponse(HttpResponse):
11 non_picklable_attrs = HttpResponse.non_picklable_attrs | frozenset(
12 ["template_name", "context_data", "_post_render_callbacks"]
13 )
15 def __init__(
16 self,
17 template,
18 context=None,
19 content_type=None,
20 status=None,
21 charset=None,
22 using=None,
23 headers=None,
24 ):
25 # It would seem obvious to call these next two members 'template' and
26 # 'context', but those names are reserved as part of the test Client
27 # API. To avoid the name collision, we use different names.
28 self.template_name = template
29 self.context_data = context
31 self.using = using
33 self._post_render_callbacks = []
35 # _request stores the current request object in subclasses that know
36 # about requests, like TemplateResponse. It's defined in the base class
37 # to minimize code duplication.
38 # It's called self._request because self.request gets overwritten by
39 # django.test.client.Client. Unlike template_name and context_data,
40 # _request should not be considered part of the public API.
41 self._request = None
43 # content argument doesn't make sense here because it will be replaced
44 # with rendered template so we always pass empty string in order to
45 # prevent errors and provide shorter signature.
46 super().__init__("", content_type, status, charset=charset, headers=headers)
48 # _is_rendered tracks whether the template and context has been baked
49 # into a final response.
50 # Super __init__ doesn't know any better than to set self.content to
51 # the empty string we just gave it, which wrongly sets _is_rendered
52 # True, so we initialize it to False after the call to super __init__.
53 self._is_rendered = False
55 def __getstate__(self):
56 """
57 Raise an exception if trying to pickle an unrendered response. Pickle
58 only rendered data, not the data used to construct the response.
59 """
60 if not self._is_rendered:
61 raise ContentNotRenderedError(
62 "The response content must be rendered before it can be pickled."
63 )
64 return super().__getstate__()
66 def resolve_template(self, template):
67 """Accept a template object, path-to-template, or list of paths."""
68 if isinstance(template, (list, tuple)):
69 return select_template(template, using=self.using)
70 elif isinstance(template, str):
71 return get_template(template, using=self.using)
72 else:
73 return template
75 def resolve_context(self, context):
76 return context
78 @property
79 def rendered_content(self):
80 """Return the freshly rendered content for the template and context
81 described by the TemplateResponse.
83 This *does not* set the final content of the response. To set the
84 response content, you must either call render(), or set the
85 content explicitly using the value of this property.
86 """
87 template = self.resolve_template(self.template_name)
88 context = self.resolve_context(self.context_data)
89 return template.render(context, self._request)
91 def add_post_render_callback(self, callback):
92 """Add a new post-rendering callback.
94 If the response has already been rendered,
95 invoke the callback immediately.
96 """
97 if self._is_rendered:
98 callback(self)
99 else:
100 self._post_render_callbacks.append(callback)
102 def render(self):
103 """Render (thereby finalizing) the content of the response.
105 If the content has already been rendered, this is a no-op.
107 Return the baked response instance.
108 """
109 retval = self
110 if not self._is_rendered:
111 self.content = self.rendered_content
112 for post_callback in self._post_render_callbacks:
113 newretval = post_callback(retval)
114 if newretval is not None:
115 retval = newretval
116 return retval
118 @property
119 def is_rendered(self):
120 return self._is_rendered
122 def __iter__(self):
123 if not self._is_rendered:
124 raise ContentNotRenderedError(
125 "The response content must be rendered before it can be iterated over."
126 )
127 return super().__iter__()
129 @property
130 def content(self):
131 if not self._is_rendered:
132 raise ContentNotRenderedError(
133 "The response content must be rendered before it can be accessed."
134 )
135 return super().content
137 @content.setter
138 def content(self, value):
139 """Set the content for the response."""
140 HttpResponse.content.fset(self, value)
141 self._is_rendered = True
144class TemplateResponse(SimpleTemplateResponse):
145 non_picklable_attrs = SimpleTemplateResponse.non_picklable_attrs | frozenset(
146 ["_request"]
147 )
149 def __init__(
150 self,
151 request,
152 template,
153 context=None,
154 content_type=None,
155 status=None,
156 charset=None,
157 using=None,
158 headers=None,
159 ):
160 super().__init__(
161 template, context, content_type, status, charset, using, headers=headers
162 )
163 self._request = request