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.