Widgets

小部件是Django对HTML输入元素的表示。 窗口小部件处理HTML的呈现,以及从与窗口小部件对应的GET / POST字典中提取数据。

内置窗口小部件生成的HTML使用HTML5语法,目标是<!DOCTYPE html> 例如,它使用布尔属性,例如checked,而不是checked ='checked'的XHTML样式。

Tip

小部件不应与表单字段混淆 表单字段处理输入验证的逻辑,并直接在模板中使用。 窗口小部件处理在网页上呈现HTML表单输入元素以及提取原始提交的数据。 但是,窗口小部件需要指定才能形成字段。

Specifying widgets

无论何时在表单上指定字段,Django都将使用适合于要显示的数据类型的默认窗口小部件。 要查找在哪个字段上使用哪个窗口小部件,请参阅有关内置字段类的文档。

但是,如果要为字段使用不同的窗口小部件,则可以在字段定义中使用窗口小部件参数。 例如:

from django import forms

class CommentForm(forms.Form):
    name = forms.CharField()
    url = forms.URLField()
    comment = forms.CharField(widget=forms.Textarea)

这将指定一个带有注释的表单,该注释使用较大的Textarea小部件,而不是默认的TextInput小部件。

Setting arguments for widgets

许多小部件都有可选的额外参数;在字段上定义窗口小部件时可以设置它们。 在以下示例中,为SelectDateWidget设置了属性:

from django import forms

BIRTH_YEAR_CHOICES = ('1980', '1981', '1982')
FAVORITE_COLORS_CHOICES = (
    ('blue', 'Blue'),
    ('green', 'Green'),
    ('black', 'Black'),
)

class SimpleForm(forms.Form):
    birth_year = forms.DateField(widget=forms.SelectDateWidget(years=BIRTH_YEAR_CHOICES))
    favorite_colors = forms.MultipleChoiceField(
        required=False,
        widget=forms.CheckboxSelectMultiple,
        choices=FAVORITE_COLORS_CHOICES,
    )

有关哪些窗口小部件可用以及它们接受哪些参数的详细信息,请参阅内置窗口小部件

Select小部件继承的小部件

选择小部件继承的小部件处理选择。 它们向用户显示可供选择的选项列表。 The different widgets present this choice differently; the Select widget itself uses a <select> HTML list representation, while RadioSelect uses radio buttons.

默认情况下,在ChoiceField字段中使用选择小部件。 窗口小部件上显示的选项继承自ChoiceField,更改ChoiceField.choices将更新Select.choices For example:

>>> from django import forms
>>> CHOICES = (('1', 'First',), ('2', 'Second',))
>>> choice_field = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES)
>>> choice_field.choices
[('1', 'First'), ('2', 'Second')]
>>> choice_field.widget.choices
[('1', 'First'), ('2', 'Second')]
>>> choice_field.widget.choices = ()
>>> choice_field.choices = (('1', 'First and only',),)
>>> choice_field.widget.choices
[('1', 'First and only')]

但是,提供选项属性的窗口小部件可以与不基于选择的字段一起使用 - 例如CharField - 但建议使用基于ChoiceField 的字段,当选择是模型固有的而不仅仅是表示小部件时。

Customizing widget instances

当Django将窗口小部件呈现为HTML时,它只呈现非常小的标记 - Django不会添加类名或任何其他特定于窗口小部件的属性。 这意味着,例如,所有TextInput小部件在您的网页上显示相同。

有两种方法可以自定义小部件:每个小部件实例每个小部件类

Styling widget instances

如果要使一个窗口小部件实例看起来与另一个窗口小部件不同,则需要在实例化窗口小部件对象并将其分配给窗体域时指定其他属性(并且可能会向CSS文件添加一些规则)。

例如,采用以下简单形式:

from django import forms

class CommentForm(forms.Form):
    name = forms.CharField()
    url = forms.URLField()
    comment = forms.CharField()

此表单将包含三个默认的TextInput小部件,默认呈现 - 没有CSS类,没有额外的属性。 这意味着为每个窗口小部件提供的输入框将呈现完全相同:

>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" required /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" required /></td></tr>

在真实的Web页面上,您可能不希望每个小部件看起来都一样。 您可能需要更大的注释输入元素,并且您可能希望“名称”小部件具有一些特殊的CSS类。 也可以指定'type'属性以利用新的HTML5输入类型。 为此,在创建窗口小部件时使用Widget.attrs参数:

class CommentForm(forms.Form):
    name = forms.CharField(widget=forms.TextInput(attrs={'class': 'special'}))
    url = forms.URLField()
    comment = forms.CharField(widget=forms.TextInput(attrs={'size': '40'}))

然后Django将在渲染输出中包含额外属性:

>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" class="special" required /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url" required /></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40" required /></td></tr>

您还可以使用attrs设置HTML id 有关示例,请参见BoundField.id_for_label

Styling widget classes

使用小部件,可以添加资源(cssjavascript)并更深入地定制其外观和行为。

简而言之,您需要对窗口小部件进行子类化,并且定义“Media”内部类创建“media”属性

这些方法涉及一些高级Python编程,并在表单资产主题指南中有详细描述。

Base widget classes

基本窗口小部件类窗口小部件MultiWidget由所有内置窗口小部件子类化,并可作为自定义窗口小部件的基础。

Widget

class Widget(attrs=None)[source]

此抽象类无法呈现,但提供基本属性attrs 您还可以在自定义窗口小部件上实现或覆盖render()方法。

attrs

包含要在呈现的窗口小部件上设置的HTML属性的字典。

>>> from django import forms
>>> name = forms.TextInput(attrs={'size': 10, 'title': 'Your name',})
>>> name.render('name', 'A name')
'<input title="Your name" type="text" name="name" value="A name" size="10" required />'

如果为属性指定值TrueFalse,则它将呈现为HTML5布尔属性:

>>> name = forms.TextInput(attrs={'required': True})
>>> name.render('name', 'A name')
'<input name="name" type="text" value="A name" required />'
>>>
>>> name = forms.TextInput(attrs={'required': False})
>>> name.render('name', 'A name')
'<input name="name" type="text" value="A name" />'
supports_microseconds

默认为True的属性。 如果设置为False,则datetimetime值的微秒部分将设置为0

format_value(value)[source]

清除并返回要在窗口小部件模板中使用的值。 value不保证是有效输入,因此子类实现应该以防御方式编程。

get_context(name, value, attrs)[source]
New in Django 1.11.

返回呈现窗口小部件模板时要使用的值的字典。 默认情况下,字典包含单个键'widget',它是包含以下键的窗口小部件的字典表示:

  • 'name': The name of the field from the name argument.
  • 'is_hidden': A boolean indicating whether or not this widget is hidden.
  • 'required': A boolean indicating whether or not the field for this widget is required.
  • 'value': The value as returned by format_value().
  • 'attrs': HTML attributes to be set on the rendered widget. The combination of the attrs attribute and the attrs argument.
  • 'template_name': The value of self.template_name.

Widget subclasses can provide custom context values by overriding this method.

id_for_label(id_)[source]

Returns the HTML ID attribute of this widget for use by a <label>, given the ID of the field. 如果ID不可用,则返回

这个钩子是必要的,因为一些小部件有多个HTML元素,因此有多个ID。 在这种情况下,此方法应返回与窗口小部件标记中的第一个ID对应的ID值。

render(name, value, attrs=None, renderer=None)[source]

使用给定的渲染器将窗口小部件渲染为HTML。 如果rendererNone,则使用FORM_RENDERER设置中的渲染器。

Changed in Django 1.11:

添加了renderer参数。 在Django 2.1中不再支持不接受这个参数的子类。

value_from_datadict(data, files, name)[source]

给定数据字典和此窗口小部件的名称,返回此窗口小部件的值。 文件可能包含来自request.FILES的数据。 Returns None if a value wasn’t provided. 另请注意,在处理表单数据期间可能会多次调用value_from_datadict,因此如果您自定义并添加昂贵的处理,则应自行实现一些缓存机制。

value_omitted_from_data(data, files, name)[source]

给定datafiles字典和此小部件的名称,返回是否有小部件的数据或文件。

方法的结果会影响模型形式中的字段是否回退到其默认值

Special cases are CheckboxInput, CheckboxSelectMultiple, and SelectMultiple, which always return False because an unchecked checkbox and unselected <select multiple> don’t appear in the data of an HTML form submission, so it’s unknown whether or not the user submitted a value.

use_required_attribute(initial)[source]

Given a form field’s initial value, returns whether or not the widget can be rendered with the required HTML attribute. Forms use this method along with Field.required and Form.use_required_attribute to determine whether or not to display the required attribute for each field.

By default, returns False for hidden widgets and True otherwise. Special cases are ClearableFileInput, which returns False when initial is not set, and CheckboxSelectMultiple, which always returns False because browser validation would require all checkboxes to be checked instead of at least one.

Override this method in custom widgets that aren’t compatible with browser validation. For example, a WSYSIWG text editor widget backed by a hidden textarea element may want to always return False to avoid browser validation on the hidden field.

MultiWidget

class MultiWidget(widgets, attrs=None)[source]

A widget that is composed of multiple widgets. MultiWidget works hand in hand with the MultiValueField.

MultiWidget has one required argument:

widgets

An iterable containing the widgets needed.

And one required method:

decompress(value)[source]

This method takes a single “compressed” value from the field and returns a list of “decompressed” values. The input value can be assumed valid, but not necessarily non-empty.

This method must be implemented by the subclass, and since the value may be empty, the implementation must be defensive.

The rationale behind “decompression” is that it is necessary to “split” the combined value of the form field into the values for each widget.

An example of this is how SplitDateTimeWidget turns a datetime value into a list with date and time split into two separate values:

from django.forms import MultiWidget

class SplitDateTimeWidget(MultiWidget):

    # ...

    def decompress(self, value):
        if value:
            return [value.date(), value.time().replace(microsecond=0)]
        return [None, None]

Tip

Note that MultiValueField has a complementary method compress() with the opposite responsibility - to combine cleaned values of all member fields into one.

It provides some custom context:

get_context(name, value, attrs)[source]

In addition to the 'widget' key described in Widget.get_context(), MultiValueWidget adds a widget['subwidgets'] key.

These can be looped over in the widget template:

{% for subwidget in widget.subwidgets %}
    {% include widget.template_name with widget=subwidget %}
{% endfor %}

Here’s an example widget which subclasses MultiWidget to display a date with the day, month, and year in different select boxes. This widget is intended to be used with a DateField rather than a MultiValueField, thus we have implemented value_from_datadict():

from datetime import date
from django.forms import widgets

class DateSelectorWidget(widgets.MultiWidget):
    def __init__(self, attrs=None):
        # create choices for days, months, years
        # example below, the rest snipped for brevity.
        years = [(year, year) for year in (2011, 2012, 2013)]
        _widgets = (
            widgets.Select(attrs=attrs, choices=days),
            widgets.Select(attrs=attrs, choices=months),
            widgets.Select(attrs=attrs, choices=years),
        )
        super().__init__(_widgets, attrs)

    def decompress(self, value):
        if value:
            return [value.day, value.month, value.year]
        return [None, None, None]

    def value_from_datadict(self, data, files, name):
        datelist = [
            widget.value_from_datadict(data, files, name + '_%s' % i)
            for i, widget in enumerate(self.widgets)]
        try:
            D = date(
                day=int(datelist[0]),
                month=int(datelist[1]),
                year=int(datelist[2]),
            )
        except ValueError:
            return ''
        else:
            return str(D)

The constructor creates several Select widgets in a tuple. The super class uses this tuple to setup the widget.

The required method decompress() breaks up a datetime.date value into the day, month, and year values corresponding to each widget. Note how the method handles the case where value is None.

The default implementation of value_from_datadict() returns a list of values corresponding to each Widget. This is appropriate when using a MultiWidget with a MultiValueField, but since we want to use this widget with a DateField which takes a single value, we have overridden this method to combine the data of all the subwidgets into a datetime.date. The method extracts data from the POST dictionary and constructs and validates the date. If it is valid, we return the string, otherwise, we return an empty string which will cause form.is_valid to return False.

Built-in widgets

Django提供了所有基本HTML小部件的表示,以及django.forms.widgets模块中一些常用的小部件组,包括文本输入各种复选框和选择器上传文件,以及处理多值输入

Widgets handling input of text

这些小部件使用HTML元素inputtextarea

TextInput

class TextInput[source]
  • input_type: 'text'
  • template_name: 'django/forms/widgets/text.html'
  • Renders as: <input type="text" ...>

NumberInput

class NumberInput[source]
  • input_type: 'number'
  • template_name: 'django/forms/widgets/number.html'
  • Renders as: <input type="number" ...>

请注意,并非所有浏览器都支持在number输入类型中输入本地化数字。 Django本身避免将它们用于localize属性设置为True的字段。

EmailInput

class EmailInput[source]
  • input_type: 'email'
  • template_name: 'django/forms/widgets/email.html'
  • Renders as: <input type="email" ...>

URLInput

class URLInput[source]
  • input_type: 'url'
  • template_name: 'django/forms/widgets/url.html'
  • Renders as: <input type="url" ...>

PasswordInput

class PasswordInput[source]
  • input_type: 'password'
  • template_name: 'django/forms/widgets/password.html'
  • Renders as: <input type="password" ...>

Takes one optional argument:

render_value

Determines whether the widget will have a value filled in when the form is re-displayed after a validation error (default is False).

HiddenInput

class HiddenInput[source]
  • input_type: 'hidden'
  • template_name: 'django/forms/widgets/hidden.html'
  • Renders as: <input type="hidden" ...>

Note that there also is a MultipleHiddenInput widget that encapsulates a set of hidden input elements.

DateInput

class DateInput[source]
  • input_type: 'text'
  • template_name: 'django/forms/widgets/date.html'
  • Renders as: <input type="text" ...>

Takes same arguments as TextInput, with one more optional argument:

format

The format in which this field’s initial value will be displayed.

If no format argument is provided, the default format is the first format found in DATE_INPUT_FORMATS and respects Format localization.

DateTimeInput

class DateTimeInput[source]
  • input_type: 'text'
  • template_name: 'django/forms/widgets/datetime.html'
  • Renders as: <input type="text" ...>

Takes same arguments as TextInput, with one more optional argument:

format

The format in which this field’s initial value will be displayed.

If no format argument is provided, the default format is the first format found in DATETIME_INPUT_FORMATS and respects Format localization.

By default, the microseconds part of the time value is always set to 0. If microseconds are required, use a subclass with the supports_microseconds attribute set to True.

TimeInput

class TimeInput[source]
  • input_type: 'text'
  • template_name: 'django/forms/widgets/time.html'
  • Renders as: <input type="text" ...>

Takes same arguments as TextInput, with one more optional argument:

format

The format in which this field’s initial value will be displayed.

If no format argument is provided, the default format is the first format found in TIME_INPUT_FORMATS and respects Format localization.

For the treatment of microseconds, see DateTimeInput.

Textarea

class Textarea[source]
  • template_name: 'django/forms/widgets/textarea.html'
  • Renders as: <textarea>...</textarea>

Selector and checkbox widgets

These widgets make use of the HTML elements <select>, <input type="checkbox">, and <input type="radio">.

Widgets that render multiple choices have an option_template_name attribute that specifies the template used to render each choice. For example, for the Select widget, select_option.html renders the <option> for a <select>.

CheckboxInput

class CheckboxInput[source]
  • input_type: 'checkbox'
  • template_name: 'django/forms/widgets/checkbox.html'
  • Renders as: <input type="checkbox" ...>

Takes one optional argument:

check_test

A callable that takes the value of the CheckboxInput and returns True if the checkbox should be checked for that value.

Select

class Select[source]
  • template_name: 'django/forms/widgets/select.html'
  • option_template_name: 'django/forms/widgets/select_option.html'
  • Renders as: <select><option ...>...</select>
choices

This attribute is optional when the form field does not have a choices attribute. If it does, it will override anything you set here when the attribute is updated on the Field.

NullBooleanSelect

class NullBooleanSelect[source]
  • template_name: 'django/forms/widgets/select.html'
  • option_template_name: 'django/forms/widgets/select_option.html'

Select widget with options ‘Unknown’, ‘Yes’ and ‘No’

SelectMultiple

class SelectMultiple[source]
  • template_name: 'django/forms/widgets/select.html'
  • option_template_name: 'django/forms/widgets/select_option.html'

Similar to Select, but allows multiple selection: <select multiple="multiple">...</select>

RadioSelect

class RadioSelect[source]
  • template_name: 'django/forms/widgets/radio.html'
  • option_template_name: 'django/forms/widgets/radio_option.html'

Similar to Select, but rendered as a list of radio buttons within <li> tags:

<ul>
  <li><input type="radio" name="..."></li>
  ...
</ul>

For more granular control over the generated markup, you can loop over the radio buttons in the template. Assuming a form myform with a field beatles that uses a RadioSelect as its widget:

{% for radio in myform.beatles %}
<div class="myradio">
    {{ radio }}
</div>
{% endfor %}

This would generate the following HTML:

<div class="myradio">
    <label for="id_beatles_0"><input id="id_beatles_0" name="beatles" type="radio" value="john" required /> John</label>
</div>
<div class="myradio">
    <label for="id_beatles_1"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required /> Paul</label>
</div>
<div class="myradio">
    <label for="id_beatles_2"><input id="id_beatles_2" name="beatles" type="radio" value="george" required /> George</label>
</div>
<div class="myradio">
    <label for="id_beatles_3"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required /> Ringo</label>
</div>

That included the <label> tags. To get more granular, you can use each radio button’s tag, choice_label and id_for_label attributes. For example, this template…

{% for radio in myform.beatles %}
    <label for="{{ radio.id_for_label }}">
        {{ radio.choice_label }}
        <span class="radio">{{ radio.tag }}</span>
    </label>
{% endfor %}

…will result in the following HTML:

<label for="id_beatles_0">
    John
    <span class="radio"><input id="id_beatles_0" name="beatles" type="radio" value="john" required /></span>
</label>

<label for="id_beatles_1">
    Paul
    <span class="radio"><input id="id_beatles_1" name="beatles" type="radio" value="paul" required /></span>
</label>

<label for="id_beatles_2">
    George
    <span class="radio"><input id="id_beatles_2" name="beatles" type="radio" value="george" required /></span>
</label>

<label for="id_beatles_3">
    Ringo
    <span class="radio"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" required /></span>
</label>

If you decide not to loop over the radio buttons – e.g., if your template simply includes {{ myform.beatles }} – they’ll be output in a <ul> with <li> tags, as above.

The outer <ul> container receives the id attribute of the widget, if defined, or BoundField.auto_id otherwise.

When looping over the radio buttons, the label and input tags include for and id attributes, respectively. Each radio button has an id_for_label attribute to output the element’s ID.

CheckboxSelectMultiple

class CheckboxSelectMultiple[source]
  • template_name: 'django/forms/widgets/checkbox_select.html'
  • option_template_name: 'django/forms/widgets/checkbox_option.html'

Similar to SelectMultiple, but rendered as a list of check buttons:

<ul>
  <li><input type="checkbox" name="..." ></li>
  ...
</ul>

The outer <ul> container receives the id attribute of the widget, if defined, or BoundField.auto_id otherwise.

Like RadioSelect, you can loop over the individual checkboxes for the widget’s choices. Unlike RadioSelect, the checkboxes won’t include the required HTML attribute if the field is required because browser validation would require all checkboxes to be checked instead of at least one.

When looping over the checkboxes, the label and input tags include for and id attributes, respectively. Each checkbox has an id_for_label attribute to output the element’s ID.

File upload widgets

FileInput

class FileInput[source]
  • template_name: 'django/forms/widgets/file.html'
  • Renders as: <input type="file" ...>

ClearableFileInput

class ClearableFileInput[source]
  • template_name: 'django/forms/widgets/clearable_file_input.html'
  • Renders as: <input type="file" ...> with an additional checkbox input to clear the field’s value, if the field is not required and has initial data.

Composite widgets

MultipleHiddenInput

class MultipleHiddenInput[source]
  • template_name: 'django/forms/widgets/multiple_hidden.html'
  • Renders as: multiple <input type="hidden" ...> tags

A widget that handles multiple hidden widgets for fields that have a list of values.

choices

This attribute is optional when the form field does not have a choices attribute. If it does, it will override anything you set here when the attribute is updated on the Field.

SplitDateTimeWidget

class SplitDateTimeWidget[source]
  • template_name: 'django/forms/widgets/splitdatetime.html'

Wrapper (using MultiWidget) around two widgets: DateInput for the date, and TimeInput for the time. Must be used with SplitDateTimeField rather than DateTimeField.

SplitDateTimeWidget has several optional arguments:

date_format

Similar to DateInput.format

time_format

Similar to TimeInput.format

date_attrs
time_attrs
New in Django 2.0.

Similar to Widget.attrs. A dictionary containing HTML attributes to be set on the rendered DateInput and TimeInput widgets, respectively. If these attributes aren’t set, Widget.attrs is used instead.

SplitHiddenDateTimeWidget

class SplitHiddenDateTimeWidget[source]
  • template_name: 'django/forms/widgets/splithiddendatetime.html'

Similar to SplitDateTimeWidget, but uses HiddenInput for both date and time.

SelectDateWidget

class SelectDateWidget[source]
  • template_name: 'django/forms/widgets/select_date.html'

Wrapper around three Select widgets: one each for month, day, and year.

Takes several optional arguments:

years

An optional list/tuple of years to use in the “year” select box. The default is a list containing the current year and the next 9 years.

months

An optional dict of months to use in the “months” select box.

The keys of the dict correspond to the month number (1-indexed) and the values are the displayed months:

MONTHS = {
    1:_('jan'), 2:_('feb'), 3:_('mar'), 4:_('apr'),
    5:_('may'), 6:_('jun'), 7:_('jul'), 8:_('aug'),
    9:_('sep'), 10:_('oct'), 11:_('nov'), 12:_('dec')
}
empty_label

If the DateField is not required, SelectDateWidget will have an empty choice at the top of the list (which is --- by default). You can change the text of this label with the empty_label attribute. empty_label can be a string, list, or tuple. When a string is used, all select boxes will each have an empty choice with this label. If empty_label is a list or tuple of 3 string elements, the select boxes will have their own custom label. The labels should be in this order ('year_label', 'month_label', 'day_label').

# A custom empty label with string
field1 = forms.DateField(widget=SelectDateWidget(empty_label="Nothing"))

# A custom empty label with tuple
field1 = forms.DateField(
    widget=SelectDateWidget(
        empty_label=("Choose Year", "Choose Month", "Choose Day"),
    ),
)