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

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

198 statements  

1""" 

2Form classes 

3""" 

4 

5import copy 

6import datetime 

7 

8from django.core.exceptions import NON_FIELD_ERRORS, ValidationError 

9from django.forms.fields import Field 

10from django.forms.utils import ErrorDict, ErrorList, RenderableFormMixin 

11from django.forms.widgets import Media, MediaDefiningClass 

12from django.utils.datastructures import MultiValueDict 

13from django.utils.functional import cached_property 

14from django.utils.translation import gettext as _ 

15 

16from .renderers import get_default_renderer 

17 

18__all__ = ("BaseForm", "Form") 

19 

20 

21class DeclarativeFieldsMetaclass(MediaDefiningClass): 

22 """Collect Fields declared on the base classes.""" 

23 

24 def __new__(mcs, name, bases, attrs): 

25 # Collect fields from current class and remove them from attrs. 

26 attrs["declared_fields"] = { 

27 key: attrs.pop(key) 

28 for key, value in list(attrs.items()) 

29 if isinstance(value, Field) 

30 } 

31 

32 new_class = super().__new__(mcs, name, bases, attrs) 

33 

34 # Walk through the MRO. 

35 declared_fields = {} 

36 for base in reversed(new_class.__mro__): 

37 # Collect fields from base class. 

38 if hasattr(base, "declared_fields"): 

39 declared_fields.update(base.declared_fields) 

40 

41 # Field shadowing. 

42 for attr, value in base.__dict__.items(): 

43 if value is None and attr in declared_fields: 

44 declared_fields.pop(attr) 

45 

46 new_class.base_fields = declared_fields 

47 new_class.declared_fields = declared_fields 

48 

49 return new_class 

50 

51 

52class BaseForm(RenderableFormMixin): 

53 """ 

54 The main implementation of all the Form logic. Note that this class is 

55 different than Form. See the comments by the Form class for more info. Any 

56 improvements to the form API should be made to this class, not to the Form 

57 class. 

58 """ 

59 

60 default_renderer = None 

61 field_order = None 

62 prefix = None 

63 use_required_attribute = True 

64 

65 template_name_div = "django/forms/div.html" 

66 template_name_p = "django/forms/p.html" 

67 template_name_table = "django/forms/table.html" 

68 template_name_ul = "django/forms/ul.html" 

69 template_name_label = "django/forms/label.html" 

70 

71 bound_field_class = None 

72 

73 def __init__( 

74 self, 

75 data=None, 

76 files=None, 

77 auto_id="id_%s", 

78 prefix=None, 

79 initial=None, 

80 error_class=ErrorList, 

81 label_suffix=None, 

82 empty_permitted=False, 

83 field_order=None, 

84 use_required_attribute=None, 

85 renderer=None, 

86 bound_field_class=None, 

87 ): 

88 self.is_bound = data is not None or files is not None 

89 self.data = MultiValueDict() if data is None else data 

90 self.files = MultiValueDict() if files is None else files 

91 self.auto_id = auto_id 

92 if prefix is not None: 

93 self.prefix = prefix 

94 self.initial = initial or {} 

95 self.error_class = error_class 

96 # Translators: This is the default suffix added to form field labels 

97 self.label_suffix = label_suffix if label_suffix is not None else _(":") 

98 self.empty_permitted = empty_permitted 

99 self._errors = None # Stores the errors after clean() has been called. 

100 

101 # The base_fields class attribute is the *class-wide* definition of 

102 # fields. Because a particular *instance* of the class might want to 

103 # alter self.fields, we create self.fields here by copying base_fields. 

104 # Instances should always modify self.fields; they should not modify 

105 # self.base_fields. 

106 self.fields = copy.deepcopy(self.base_fields) 

107 self._bound_fields_cache = {} 

108 self.order_fields(self.field_order if field_order is None else field_order) 

109 

110 if use_required_attribute is not None: 

111 self.use_required_attribute = use_required_attribute 

112 

113 if self.empty_permitted and self.use_required_attribute: 

114 raise ValueError( 

115 "The empty_permitted and use_required_attribute arguments may " 

116 "not both be True." 

117 ) 

118 

119 # Initialize form renderer. Use a global default if not specified 

120 # either as an argument or as self.default_renderer. 

121 if renderer is None: 

122 if self.default_renderer is None: 

123 renderer = get_default_renderer() 

124 else: 

125 renderer = self.default_renderer 

126 if isinstance(self.default_renderer, type): 

127 renderer = renderer() 

128 self.renderer = renderer 

129 

130 self.bound_field_class = ( 

131 bound_field_class 

132 or self.bound_field_class 

133 or getattr(self.renderer, "bound_field_class", None) 

134 ) 

135 

136 def order_fields(self, field_order): 

137 """ 

138 Rearrange the fields according to field_order. 

139 

140 field_order is a list of field names specifying the order. Append fields 

141 not included in the list in the default order for backward compatibility 

142 with subclasses not overriding field_order. If field_order is None, 

143 keep all fields in the order defined in the class. Ignore unknown 

144 fields in field_order to allow disabling fields in form subclasses 

145 without redefining ordering. 

146 """ 

147 if field_order is None: 

148 return 

149 fields = {} 

150 for key in field_order: 

151 try: 

152 fields[key] = self.fields.pop(key) 

153 except KeyError: # ignore unknown fields 

154 pass 

155 fields.update(self.fields) # add remaining fields in original order 

156 self.fields = fields 

157 

158 def __repr__(self): 

159 if self._errors is None: 

160 is_valid = "Unknown" 

161 else: 

162 is_valid = self.is_bound and not self._errors 

163 return "<%(cls)s bound=%(bound)s, valid=%(valid)s, fields=(%(fields)s)>" % { 

164 "cls": self.__class__.__name__, 

165 "bound": self.is_bound, 

166 "valid": is_valid, 

167 "fields": ";".join(self.fields), 

168 } 

169 

170 def _bound_items(self): 

171 """Yield (name, bf) pairs, where bf is a BoundField object.""" 

172 for name in self.fields: 

173 yield name, self[name] 

174 

175 def __iter__(self): 

176 """Yield the form's fields as BoundField objects.""" 

177 for name in self.fields: 

178 yield self[name] 

179 

180 def __getitem__(self, name): 

181 """Return a BoundField with the given name.""" 

182 try: 

183 field = self.fields[name] 

184 except KeyError: 

185 raise KeyError( 

186 "Key '%s' not found in '%s'. Choices are: %s." 

187 % ( 

188 name, 

189 self.__class__.__name__, 

190 ", ".join(sorted(self.fields)), 

191 ) 

192 ) 

193 if name not in self._bound_fields_cache: 

194 self._bound_fields_cache[name] = field.get_bound_field(self, name) 

195 return self._bound_fields_cache[name] 

196 

197 @property 

198 def errors(self): 

199 """Return an ErrorDict for the data provided for the form.""" 

200 if self._errors is None: 

201 self.full_clean() 

202 return self._errors 

203 

204 def is_valid(self): 

205 """Return True if the form has no errors, or False otherwise.""" 

206 return self.is_bound and not self.errors 

207 

208 def add_prefix(self, field_name): 

209 """ 

210 Return the field name with a prefix appended, if this Form has a 

211 prefix set. 

212 

213 Subclasses may wish to override. 

214 """ 

215 return "%s-%s" % (self.prefix, field_name) if self.prefix else field_name 

216 

217 def add_initial_prefix(self, field_name): 

218 """Add an 'initial' prefix for checking dynamic initial values.""" 

219 return "initial-%s" % self.add_prefix(field_name) 

220 

221 def _widget_data_value(self, widget, html_name): 

222 # value_from_datadict() gets the data from the data dictionaries. 

223 # Each widget type knows how to retrieve its own data, because some 

224 # widgets split data over several HTML fields. 

225 return widget.value_from_datadict(self.data, self.files, html_name) 

226 

227 @property 

228 def template_name(self): 

229 return self.renderer.form_template_name 

230 

231 def get_context(self): 

232 fields = [] 

233 hidden_fields = [] 

234 top_errors = self.non_field_errors().copy() 

235 for name, bf in self._bound_items(): 

236 if bf.is_hidden: 

237 if bf.errors: 

238 top_errors += [ 

239 _("(Hidden field %(name)s) %(error)s") 

240 % {"name": name, "error": str(e)} 

241 for e in bf.errors 

242 ] 

243 hidden_fields.append(bf) 

244 else: 

245 fields.append((bf, bf.errors)) 

246 return { 

247 "form": self, 

248 "fields": fields, 

249 "hidden_fields": hidden_fields, 

250 "errors": top_errors, 

251 } 

252 

253 def non_field_errors(self): 

254 """ 

255 Return an ErrorList of errors that aren't associated with a particular 

256 field -- i.e., from Form.clean(). Return an empty ErrorList if there 

257 are none. 

258 """ 

259 return self.errors.get( 

260 NON_FIELD_ERRORS, 

261 self.error_class(error_class="nonfield", renderer=self.renderer), 

262 ) 

263 

264 def add_error(self, field, error): 

265 """ 

266 Update the content of `self._errors`. 

267 

268 The `field` argument is the name of the field to which the errors 

269 should be added. If it's None, treat the errors as NON_FIELD_ERRORS. 

270 

271 The `error` argument can be a single error, a list of errors, or a 

272 dictionary that maps field names to lists of errors. An "error" can be 

273 either a simple string or an instance of ValidationError with its 

274 message attribute set and a "list or dictionary" can be an actual 

275 `list` or `dict` or an instance of ValidationError with its 

276 `error_list` or `error_dict` attribute set. 

277 

278 If `error` is a dictionary, the `field` argument *must* be None and 

279 errors will be added to the fields that correspond to the keys of the 

280 dictionary. 

281 """ 

282 if not isinstance(error, ValidationError): 

283 # Normalize to ValidationError and let its constructor 

284 # do the hard work of making sense of the input. 

285 error = ValidationError(error) 

286 

287 if hasattr(error, "error_dict"): 

288 if field is not None: 

289 raise TypeError( 

290 "The argument `field` must be `None` when the `error` " 

291 "argument contains errors for multiple fields." 

292 ) 

293 else: 

294 error = error.error_dict 

295 else: 

296 error = {field or NON_FIELD_ERRORS: error.error_list} 

297 

298 for field, error_list in error.items(): 

299 if field not in self.errors: 

300 if field != NON_FIELD_ERRORS and field not in self.fields: 

301 raise ValueError( 

302 "'%s' has no field named '%s'." 

303 % (self.__class__.__name__, field) 

304 ) 

305 if field == NON_FIELD_ERRORS: 

306 self._errors[field] = self.error_class( 

307 error_class="nonfield", renderer=self.renderer 

308 ) 

309 else: 

310 self._errors[field] = self.error_class( 

311 renderer=self.renderer, 

312 field_id=self[field].auto_id, 

313 ) 

314 self._errors[field].extend(error_list) 

315 if field in self.cleaned_data: 

316 del self.cleaned_data[field] 

317 

318 def has_error(self, field, code=None): 

319 return field in self.errors and ( 

320 code is None 

321 or any(error.code == code for error in self.errors.as_data()[field]) 

322 ) 

323 

324 def full_clean(self): 

325 """ 

326 Clean all of self.data and populate self._errors and self.cleaned_data. 

327 """ 

328 self._errors = ErrorDict(renderer=self.renderer) 

329 if not self.is_bound: # Stop further processing. 

330 return 

331 self.cleaned_data = {} 

332 # If the form is permitted to be empty, and none of the form data has 

333 # changed from the initial data, short circuit any validation. 

334 if self.empty_permitted and not self.has_changed(): 

335 return 

336 

337 self._clean_fields() 

338 self._clean_form() 

339 self._post_clean() 

340 

341 def _clean_fields(self): 

342 for name, bf in self._bound_items(): 

343 field = bf.field 

344 try: 

345 self.cleaned_data[name] = field._clean_bound_field(bf) 

346 if hasattr(self, "clean_%s" % name): 

347 value = getattr(self, "clean_%s" % name)() 

348 self.cleaned_data[name] = value 

349 except ValidationError as e: 

350 self.add_error(name, e) 

351 

352 def _clean_form(self): 

353 try: 

354 cleaned_data = self.clean() 

355 except ValidationError as e: 

356 self.add_error(None, e) 

357 else: 

358 if cleaned_data is not None: 

359 self.cleaned_data = cleaned_data 

360 

361 def _post_clean(self): 

362 """ 

363 An internal hook for performing additional cleaning after form cleaning 

364 is complete. Used for model validation in model forms. 

365 """ 

366 pass 

367 

368 def clean(self): 

369 """ 

370 Hook for doing any extra form-wide cleaning after Field.clean() has been 

371 called on every field. Any ValidationError raised by this method will 

372 not be associated with a particular field; it will have a special-case 

373 association with the field named '__all__'. 

374 """ 

375 return self.cleaned_data 

376 

377 def has_changed(self): 

378 """Return True if data differs from initial.""" 

379 return bool(self.changed_data) 

380 

381 @cached_property 

382 def changed_data(self): 

383 return [name for name, bf in self._bound_items() if bf._has_changed()] 

384 

385 @property 

386 def media(self): 

387 """Return all media required to render the widgets on this form.""" 

388 media = Media() 

389 for field in self.fields.values(): 

390 media += field.widget.media 

391 return media 

392 

393 def is_multipart(self): 

394 """ 

395 Return True if the form needs to be multipart-encoded, i.e. it has 

396 FileInput, or False otherwise. 

397 """ 

398 return any(field.widget.needs_multipart_form for field in self.fields.values()) 

399 

400 def hidden_fields(self): 

401 """ 

402 Return a list of all the BoundField objects that are hidden fields. 

403 Useful for manual form layout in templates. 

404 """ 

405 return [field for field in self if field.is_hidden] 

406 

407 def visible_fields(self): 

408 """ 

409 Return a list of BoundField objects that aren't hidden fields. 

410 The opposite of the hidden_fields() method. 

411 """ 

412 return [field for field in self if not field.is_hidden] 

413 

414 def get_initial_for_field(self, field, field_name): 

415 """ 

416 Return initial data for field on form. Use initial data from the form 

417 or the field, in that order. Evaluate callable values. 

418 """ 

419 value = self.initial.get(field_name, field.initial) 

420 if callable(value): 

421 value = value() 

422 # If this is an auto-generated default date, nix the microseconds 

423 # for standardized handling. See #22502. 

424 if ( 

425 isinstance(value, (datetime.datetime, datetime.time)) 

426 and not field.widget.supports_microseconds 

427 ): 

428 value = value.replace(microsecond=0) 

429 return value 

430 

431 

432class Form(BaseForm, metaclass=DeclarativeFieldsMetaclass): 

433 "A collection of Fields, plus their associated data." 

434 # This is a separate class from BaseForm in order to abstract the way 

435 # self.fields is specified. This class (Form) is the one that does the 

436 # fancy metaclass stuff purely for the semantic sugar -- it allows one 

437 # to define a form using declarative syntax. 

438 # BaseForm itself has no way of designating self.fields.