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

1from django.http import HttpResponse 

2 

3from .loader import get_template, select_template 

4 

5 

6class ContentNotRenderedError(Exception): 

7 pass 

8 

9 

10class SimpleTemplateResponse(HttpResponse): 

11 non_picklable_attrs = HttpResponse.non_picklable_attrs | frozenset( 

12 ["template_name", "context_data", "_post_render_callbacks"] 

13 ) 

14 

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 

30 

31 self.using = using 

32 

33 self._post_render_callbacks = [] 

34 

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 

42 

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) 

47 

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 

54 

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__() 

65 

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 

74 

75 def resolve_context(self, context): 

76 return context 

77 

78 @property 

79 def rendered_content(self): 

80 """Return the freshly rendered content for the template and context 

81 described by the TemplateResponse. 

82 

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) 

90 

91 def add_post_render_callback(self, callback): 

92 """Add a new post-rendering callback. 

93 

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) 

101 

102 def render(self): 

103 """Render (thereby finalizing) the content of the response. 

104 

105 If the content has already been rendered, this is a no-op. 

106 

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 

117 

118 @property 

119 def is_rendered(self): 

120 return self._is_rendered 

121 

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__() 

128 

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 

136 

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 

142 

143 

144class TemplateResponse(SimpleTemplateResponse): 

145 non_picklable_attrs = SimpleTemplateResponse.non_picklable_attrs | frozenset( 

146 ["_request"] 

147 ) 

148 

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