14.6 Widgets: Widget and its subclasses
A widget is the part of a form field that knows HTML: Widget draws the field’s input from a template and reads the field’s value back out of the submitted data. Its subclasses are the <input> widgets, among them the file inputs and CheckboxInput, Textarea, the choice widgets, and the widgets of several inputs, MultiWidget, SplitDateTimeWidget and SelectDateWidget (django/).
Neither half knows about a form. To draw, the bound field hands the widget the input’s name, the value, the attributes it adds and the form’s renderer (BoundField: a field in a form); to read, the form hands it its data, its files and the name; and each form has its own copy of the widget (Declaring a form: DeclarativeFieldsMetaclass and BaseForm). The choice widgets are told in Choices: ChoiceField, ModelChoiceField and the choice widgets, and a widget’s CSS and JavaScript in Form assets: Media.
What a widget holds
Widget.__init__ keeps a copy of the HTML attributes it is given as Widget.attrs; the rest is flags, class attributes that other objects read. A field sets Widget on its widget, and Widget if it is localized, and merges its Field into the attributes, as a CharField adds "maxlength" (A form field: the base class).
flag, on Widget |
changed by | read by |
|---|---|---|
is_required, False |
changed by the field | read by the context’s "required"; the clear checkbox and the blank date choices, offered only when it is false |
is_localized, False |
changed by the field; Bound; a MultiWidget, on its widgets |
read by Widget |
Widget, False |
changed by FileInput; MultiWidget, from its widgets |
read by Base |
Widget, True |
changed by the date and time inputs, Split |
read by Base, which then drops an initial value’s microseconds |
Widget, False |
changed by MultiWidget, SelectDateWidget, RadioSelect |
read by Bound: the field is drawn in a <fieldset> |
Widget.is_hidden is a property, true when the widget’s Input.input_type is "hidden", and for a MultiWidget when all its widgets are hidden. A hidden field is drawn among the form’s hidden fields, and its input gets neither "required" nor "aria-invalid".
Drawing: Widget.render, get_context and format_value
Widget.render asks Widget for a dictionary and hands it to Widget._render, which has the renderer it was given, or the default one, render the widget’s template with it, and marks the result safe. In a form the caller is BoundField, with the form’s renderer and attributes such as "id" and "required".
The context’s one key, "widget", holds the name, "is_hidden", "required", the value as Widget writes it, "template_name", and the attributes: the widget’s own and then the call’s, which win, merged by Widget. The berth form’s ship widget, with the "maxlength" its field gave it:
>>> from pprint import pp
>>> widget = BerthForm.base_fields["ship"].widget
>>> pp(widget.get_context("ship", " heron ", {"id": "id_ship"}))
{'widget': {'name': 'ship',
'is_hidden': False,
'required': True,
'value': ' heron ',
'attrs': {'maxlength': '60', 'id': 'id_ship'},
'template_name': 'django/forms/widgets/text.html',
'type': 'text'}}
>>> print(widget.render("ship", " heron ", {"id": "id_ship"}))
<input type="text" name="ship" value=" heron " maxlength="60" id="id_ship">
Widget turns an empty string, None, and the DatabaseDefault an unsaved instance holds for a database default into None, for which the template writes no "value"; a localized widget formats the value with django, which leaves a string as it is; any other value becomes str(value).
Each concrete widget names its template in template_name. The attributes are written by "django/, which leaves out one that is False and writes one that is True bare; where the templates are found is in Rendering a form: renderers and templates.
Reading: value_from_datadict and value_omitted_from_data
Widget is given the form’s data, its files and the input’s name, and returns the raw value that Field.clean starts from when the field is not disabled: in the base class, what the data holds under that name, or None. The form asks the widget and not the field because, as the comment in BaseForm says, some widgets split their data over several HTML inputs.
Widget says whether the input was left out of the submission: in the base class, whether its name is not in the data. Besides the widgets made of others, its reader is construct_instance, which leaves a model field that has a default as the instance holds it when the widget says its input was omitted and the cleaned value is empty (Model forms: ModelForm and fields_for_model).
The label and the "required" attribute
Widget gives the id a <label> names in its "for": in the base class the input’s own; for a widget of several inputs, one of theirs, or an empty string, which leaves the label without a "for". Widget, given the bound field’s initial value, says whether the input may carry the HTML attribute "required": in the base class, unless it is hidden. The bound field writes "required" only when the widget agrees, the field is required and BaseForm is true. Widget becomes no attribute, as the ship’s input shows.
The input widgets
Input is the base of the <input> widgets. Its Input.input_type, which its subclasses set, goes into the context as "type", and a "type" among the attributes an instance is made with replaces it. TextInput, NumberInput, EmailInput, URLInput, ColorInput, SearchInput, TelInput, PasswordInput, HiddenInput, FileInput and CheckboxInput set the type their names say; the date and time inputs are text inputs.
PasswordInput draws no value unless made with render_value=True, so a form sent back with errors does not repeat the password. MultipleHiddenInput draws a hidden input for each value of a list and reads the list back with getlist. Textarea, which is no Input, writes the value between its tags, with "cols" of "40" and "rows" of "10" unless given others.
The date and time inputs share DateTimeBaseInput, which writes a value in its DateTimeBaseInput, if it was made with one, or else in the first of the locale’s formats named by DateTimeBaseInput, as DATE_INPUT_FORMATS; a string it leaves alone (Locale formats: get_format and the format modules).
File inputs: FileInput and ClearableFileInput
FileInput reads from the files and not the data, counts as omitted when its name is not among them, never draws a value, and refuses "required" when the field has an initial value. Made with a "multiple" attribute it raises ValueError, unless its class sets FileInput; then it is drawn with "multiple" and reads a list.
ClearableFileInput adds a way to remove a stored file, which to ClearableFileInput is a value with a URL, as a model’s FieldFile has. Given one, the template writes a link to it and, for a field that is not required, a checkbox named after the input with "-clear" added, as "scan-clear", before the file input.
ClearableFileInput reads the upload and then, for a field that is not required, the checkbox. Ticked with no upload, it returns False, which FileField.clean takes as an order to clear the stored file; ticked with an upload too, the marker FILE_INPUT_CONTRADICTION, which the field makes an error (The built-in form fields). It keeps in ClearableFileInput whether the box was sent, and draws the box ticked if so; it counts as omitted only when neither file nor box was sent.
CheckboxInput
CheckboxInput draws the box ticked when its CheckboxInput is true of the value. By default that is boolean_check, true of anything but False, None and the empty string, a function at module level so that the widget can be pickled, as the comment above it says, citing ticket 17976.
CheckboxInput returns False when the name is not in the data, since a browser sends nothing for an unticked box. Otherwise it reads "true" and "false", in any case, as True and False, and anything else by its truth, so that "0" is True, and so is "on": by HTML’s rule a browser sends that for a ticked box with no "value", and CheckboxInput writes none for True. An absent box cannot be told from an unticked one, so CheckboxInput always answers False, as its comment says. What BooleanField makes of the value is in The built-in form fields.
MultiWidget and SplitDateTimeWidget
MultiWidget draws one field as several inputs and reads it back as a list. It is made with its widgets, in a sequence or a dictionary, and names each input after the field with a suffix from MultiWidget: "_0", "_1" and on, or an underscore and the key (none for an empty key). To draw, it hands a value that is not a list or a tuple to MultiWidget, which subclasses write, and has each widget make its context with its own name, value and id. To read, it returns what each widget reads under its own name, for a MultiValueField to clean and compress (The built-in form fields).
A MultiWidget counts as omitted when all its widgets do, and has their media together; its MultiWidget is empty.
SplitDateTimeWidget is a MultiWidget of a DateInput and a TimeInput; its SplitDateTimeWidget splits a datetime, an aware one first made naive in the current time zone.
SelectDateWidget
SelectDateWidget draws a date as three <select> boxes, named after the field with "_year", "_month" and "_day" added, in the order of the locale’s DATE_FORMAT; it is no MultiWidget, and makes a Select for each box itself. Read back, boxes that make no date give a string such as "2026-2-31", which the date field refuses and SelectDateWidget reads back, so that, as its docstring says, an invalid date such as February 31 is shown again.