Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.10/site-packages/django/forms/utils.py: 48%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

128 statements  

1import json 

2from collections import UserList 

3 

4from django.conf import settings 

5from django.core.exceptions import ValidationError 

6from django.forms.renderers import get_default_renderer 

7from django.utils import timezone 

8from django.utils.html import escape, format_html_join 

9from django.utils.safestring import mark_safe 

10from django.utils.translation import gettext_lazy as _ 

11 

12 

13def pretty_name(name): 

14 """Convert 'first_name' to 'First name'.""" 

15 if not name: 

16 return "" 

17 return name.replace("_", " ").capitalize() 

18 

19 

20def flatatt(attrs): 

21 """ 

22 Convert a dictionary of attributes to a single string. 

23 The returned string will contain a leading space followed by key="value", 

24 XML-style pairs. In the case of a boolean value, the key will appear 

25 without a value. It is assumed that the keys do not need to be 

26 XML-escaped. If the passed dictionary is empty, then return an empty 

27 string. 

28 

29 The result is passed through 'mark_safe' (by way of 'format_html_join'). 

30 """ 

31 key_value_attrs = [] 

32 boolean_attrs = [] 

33 for attr, value in attrs.items(): 

34 if isinstance(value, bool): 

35 if value: 

36 boolean_attrs.append((attr,)) 

37 elif value is not None: 

38 key_value_attrs.append((attr, value)) 

39 

40 return format_html_join("", ' {}="{}"', sorted(key_value_attrs)) + format_html_join( 

41 "", " {}", sorted(boolean_attrs) 

42 ) 

43 

44 

45class RenderableMixin: 

46 def get_context(self): 

47 raise NotImplementedError( 

48 "Subclasses of RenderableMixin must provide a get_context() method." 

49 ) 

50 

51 def render(self, template_name=None, context=None, renderer=None): 

52 renderer = renderer or self.renderer 

53 template = template_name or self.template_name 

54 context = context or self.get_context() 

55 return mark_safe(renderer.render(template, context)) 

56 

57 __str__ = render 

58 __html__ = render 

59 

60 

61class RenderableFieldMixin(RenderableMixin): 

62 def as_field_group(self): 

63 return self.render() 

64 

65 def as_hidden(self): 

66 raise NotImplementedError( 

67 "Subclasses of RenderableFieldMixin must provide an as_hidden() method." 

68 ) 

69 

70 def as_widget(self): 

71 raise NotImplementedError( 

72 "Subclasses of RenderableFieldMixin must provide an as_widget() method." 

73 ) 

74 

75 def __str__(self): 

76 """Render this field as an HTML widget.""" 

77 if self.field.show_hidden_initial: 

78 return self.as_widget() + self.as_hidden(only_initial=True) 

79 return self.as_widget() 

80 

81 __html__ = __str__ 

82 

83 

84class RenderableFormMixin(RenderableMixin): 

85 def as_p(self): 

86 """Render as <p> elements.""" 

87 return self.render(self.template_name_p) 

88 

89 def as_table(self): 

90 """Render as <tr> elements excluding the surrounding <table> tag.""" 

91 return self.render(self.template_name_table) 

92 

93 def as_ul(self): 

94 """Render as <li> elements excluding the surrounding <ul> tag.""" 

95 return self.render(self.template_name_ul) 

96 

97 def as_div(self): 

98 """Render as <div> elements.""" 

99 return self.render(self.template_name_div) 

100 

101 

102class RenderableErrorMixin(RenderableMixin): 

103 def as_json(self, escape_html=False): 

104 return json.dumps(self.get_json_data(escape_html)) 

105 

106 def as_text(self): 

107 return self.render(self.template_name_text) 

108 

109 def as_ul(self): 

110 return self.render(self.template_name_ul) 

111 

112 

113class ErrorDict(dict, RenderableErrorMixin): 

114 """ 

115 A collection of errors that knows how to display itself in various formats. 

116 

117 The dictionary keys are the field names, and the values are the errors. 

118 """ 

119 

120 template_name = "django/forms/errors/dict/default.html" 

121 template_name_text = "django/forms/errors/dict/text.txt" 

122 template_name_ul = "django/forms/errors/dict/ul.html" 

123 

124 def __init__(self, *args, renderer=None, **kwargs): 

125 super().__init__(*args, **kwargs) 

126 self.renderer = renderer or get_default_renderer() 

127 

128 def as_data(self): 

129 return {f: e.as_data() for f, e in self.items()} 

130 

131 def get_json_data(self, escape_html=False): 

132 return {f: e.get_json_data(escape_html) for f, e in self.items()} 

133 

134 def get_context(self): 

135 return { 

136 "errors": self.items(), 

137 "error_class": "errorlist", 

138 } 

139 

140 

141class ErrorList(UserList, list, RenderableErrorMixin): 

142 """ 

143 A collection of errors that knows how to display itself in various formats. 

144 """ 

145 

146 template_name = "django/forms/errors/list/default.html" 

147 template_name_text = "django/forms/errors/list/text.txt" 

148 template_name_ul = "django/forms/errors/list/ul.html" 

149 

150 def __init__(self, initlist=None, error_class=None, renderer=None, field_id=None): 

151 super().__init__(initlist) 

152 

153 if error_class is None: 

154 self.error_class = "errorlist" 

155 else: 

156 self.error_class = "errorlist {}".format(error_class) 

157 self.renderer = renderer or get_default_renderer() 

158 self.field_id = field_id 

159 

160 def as_data(self): 

161 return ValidationError(self.data).error_list 

162 

163 def copy(self): 

164 copy = super().copy() 

165 copy.error_class = self.error_class 

166 copy.renderer = self.renderer 

167 return copy 

168 

169 def get_json_data(self, escape_html=False): 

170 errors = [] 

171 for error in self.as_data(): 

172 message = next(iter(error)) 

173 errors.append( 

174 { 

175 "message": escape(message) if escape_html else message, 

176 "code": error.code or "", 

177 } 

178 ) 

179 return errors 

180 

181 def get_context(self): 

182 return { 

183 "errors": self, 

184 "error_class": self.error_class, 

185 } 

186 

187 def __repr__(self): 

188 return repr(list(self)) 

189 

190 def __contains__(self, item): 

191 return item in list(self) 

192 

193 def __eq__(self, other): 

194 return list(self) == other 

195 

196 def __getitem__(self, i): 

197 error = self.data[i] 

198 if isinstance(error, ValidationError): 

199 return next(iter(error)) 

200 return error 

201 

202 def __reduce_ex__(self, *args, **kwargs): 

203 # The `list` reduce function returns an iterator as the fourth element 

204 # that is normally used for repopulating. Since we only inherit from 

205 # `list` for `isinstance` backward compatibility (Refs #17413) we 

206 # nullify this iterator as it would otherwise result in duplicate 

207 # entries. (Refs #23594) 

208 info = super(UserList, self).__reduce_ex__(*args, **kwargs) 

209 return info[:3] + (None, None) 

210 

211 

212# Utilities for time zone support in DateTimeField et al. 

213 

214 

215def from_current_timezone(value): 

216 """ 

217 When time zone support is enabled, convert naive datetimes 

218 entered in the current time zone to aware datetimes. 

219 """ 

220 if settings.USE_TZ and value is not None and timezone.is_naive(value): 

221 current_timezone = timezone.get_current_timezone() 

222 try: 

223 if timezone._datetime_ambiguous_or_imaginary(value, current_timezone): 

224 raise ValueError("Ambiguous or non-existent time.") 

225 return timezone.make_aware(value, current_timezone) 

226 except Exception as exc: 

227 raise ValidationError( 

228 _( 

229 "%(datetime)s couldn’t be interpreted " 

230 "in time zone %(current_timezone)s; it " 

231 "may be ambiguous or it may not exist." 

232 ), 

233 code="ambiguous_timezone", 

234 params={"datetime": value, "current_timezone": current_timezone}, 

235 ) from exc 

236 return value 

237 

238 

239def to_current_timezone(value): 

240 """ 

241 When time zone support is enabled, convert aware datetimes 

242 to naive datetimes in the current time zone for display. 

243 """ 

244 if settings.USE_TZ and value is not None and timezone.is_aware(value): 

245 return timezone.make_naive(value) 

246 return value