小部件 — Django 文档

来自菜鸟教程
Django/docs/2.2.x/ref/forms/widgets
跳转至:导航、​搜索

小工具

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

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

提示

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


指定部件

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

但是,如果您想对某个字段使用不同的小部件,则只需在字段定义中使用 widget 参数即可。 例如:

from django import forms

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

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


为部件设置参数

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

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 小部件的小部件

继承自 Select 小部件的小部件处理选择。 它们为用户提供了一个可供选择的选项列表。 不同的小部件以不同的方式呈现此选择; Select 小部件本身使用 <select> HTML 列表表示,而 RadioSelect 使用单选按钮。

Select 小部件默认用于 ChoiceField 字段。 小部件上显示的选项继承自 ChoiceField,更改 ChoiceField.choices 将更新 Select.choices。 例如:

>>> 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')]

然而,提供 choices 属性的小部件可以与不基于选择的字段一起使用 - 例如 CharField - 但建议使用 ChoiceField -based field when the choices are inherent to the model and not just the representational widget.


自定义部件实例

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

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

样式化部件实例

如果你想让一个部件实例看起来与另一个不同,你需要在实例化部件对象并将其分配给表单字段时指定额外的属性(也许还需要在你的 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'}))

您还可以修改表单定义中的小部件:

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

    name.widget.attrs.update({'class': 'special'})
    comment.widget.attrs.update(size='40')

或者,如果该字段未直接在表单上声明(例如模型表单字段),则可以使用 Form.fields 属性:

class CommentForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['name'].widget.attrs.update({'class': 'special'})
        self.fields['comment'].widget.attrs.update(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


样式化部件类

使用小部件,可以添加资产(cssjavascript)并更深入地自定义它们的外观和行为。

简而言之,您需要对小部件进行子类化,并 定义“媒体”内部类创建“媒体”属性

这些方法涉及一些高级 Python 编程,并在 Form Assets 主题指南中进行了详细描述。


基础部件类

基本小部件类 WidgetMultiWidget 是所有 内置小部件 的子类,可以作为自定义小部件的基础。

Widget

class Widget(attrs=None)

这个抽象类不能被渲染,但提供了基本属性 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">'

如果您为属性分配 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 的属性。 如果设置为 Falsedatetimetime 值的微秒部分将设置为 0

format_value(value)

清理并返回一个值以在小部件模板中使用。 value 不能保证是有效的输入,因此子类实现应该防御性地编程。

get_context(name, value, attrs)

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

  • 'name':来自 name 参数的字段名称。

  • 'is_hidden':指示此小部件是否隐藏的布尔值。

  • 'required':一个布尔值,指示是否需要此小部件的字段。

  • 'value'format_value() 返回的值。

  • 'attrs':要在呈现的小部件上设置的 HTML 属性。 attrs 属性和 attrs 参数的组合。

  • 'template_name'self.template_name 的值。

Widget 子类可以通过覆盖此方法提供自定义上下文值。

id_for_label(id_)

给定字段的 ID,返回此小部件的 HTML ID 属性以供 <label> 使用。 如果 ID 不可用,则返回 None

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

render(name, value, attrs=None, renderer=None)

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

value_from_datadict(data, files, name)

给定一个数据字典和这个小部件的名称,返回这个小部件的值。 files 可能包含来自 request.FILES 的数据。 如果未提供值,则返回 None。 还要注意 value_from_datadict 在处理表单数据的过程中可能会被多次调用,所以如果你自定义它并添加昂贵的处理,你应该自己实现一些缓存机制。

value_omitted_from_data(data, files, name)

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

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

特殊情况是 CheckboxInputCheckboxSelectMultipleSelectMultiple,它们总是返回 False 因为未选中的复选框和未选中的 <select multiple> '不会出现在 HTML 表单提交的数据中,因此不知道用户是否提交了值。

use_required_attribute(initial)

给定表单字段的 initial 值,返回是否可以使用 required HTML 属性呈现小部件。 表单使用此方法以及 Field.requiredForm.use_required_attribute 来确定是否为每个字段显示 required 属性。

默认情况下,为隐藏小部件返回 False,否则返回 True。 特殊情况是 ClearableFileInput,当 initial 未设置时返回 False,以及 CheckboxSelectMultiple,它总是返回 False,因为浏览器验证将要求选中所有复选框,而不是至少选中一个。

在与浏览器验证不兼容的自定义小部件中覆盖此方法。 例如,由隐藏的 textarea 元素支持的 WSYSIWG 文本编辑器小部件可能希望始终返回 False 以避免对隐藏字段进行浏览器验证。


MultiWidget

class MultiWidget(widgets, attrs=None)

由多个小部件组成的小部件。 MultiWidgetMultiValueField 协同工作。

MultiWidget 有一个必需的参数:

widgets

包含所需小部件的可迭代对象。

还有一个必要的方法:

decompress(value)

此方法从字段中获取单个“压缩”值并返回“解压缩”值列表。 可以假定输入值有效,但不一定非空。

这个方法必须由子类实现,并且由于值可能为空,所以实现必须是防御性的。

“解压”背后的基本原理是必须将表单字段的组合值“拆分”为每个小部件的值。

这方面的一个例子是 SplitDateTimeWidget 如何将 datetime 值转换为一个列表,其中日期和时间分为两个单独的值:

from django.forms import MultiWidget

class SplitDateTimeWidget(MultiWidget):

    # ...

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

提示

请注意, MultiValueField 有一个补充方法 compress() 具有相反的职责 - 将所有成员字段的清理值合并为一个。

它提供了一些自定义上下文:

get_context(name, value, attrs)

除了 Widget.get_context() 中描述的 'widget' 键之外,MultiValueWidget 添加了一个 widget['subwidgets'] 键。

这些可以在部件模板中循环使用:

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

这是一个示例小部件,它子类化 MultiWidget 以在不同的选择框中显示带有日、月和年的日期。 此小部件旨在与 DateField 而不是 MultiValueField 一起使用,因此我们实现了 value_from_datadict()

from datetime import date
from django import forms

class DateSelectorWidget(forms.MultiWidget):
    def __init__(self, attrs=None):
        days = [(day, day) for day in range(1, 32)]
        months = [(month, month) for month in range(1, 13)]
        years = [(year, year) for year in [2018, 2019, 2020]]
        widgets = [
            forms.Select(attrs=attrs, choices=days),
            forms.Select(attrs=attrs, choices=months),
            forms.Select(attrs=attrs, choices=years),
        ]
        super().__init__(widgets, attrs)

    def decompress(self, value):
        if isinstance(value, date):
            return [value.day, value.month, value.year]
        elif isinstance(value, str):
            year, month, day = value.split('-')
            return [day, month, year]
        return [None, None, None]

    def value_from_datadict(self, data, files, name):
        day, month, year = super().value_from_datadict(data, files, name)
        # DateField expects a single string that it can parse into a date.
        return '{}-{}-{}'.format(year, month, day)

构造函数在一个列表中创建了几个 Select 小部件。 super() 方法使用此列表来设置小部件。

所需的方法 decompress()datetime.date 值分解为与每个小部件对应的日、月和年值。 如果选择了无效的日期,例如不存在的 2 月 30 日,则 DateField 会向该方法传递一个字符串,因此需要解析。 当 valueNone 时,最终的 return 处理,这意味着我们的子部件没有任何默认值。

value_from_datadict() 的默认实现返回与每个 Widget 对应的值列表。 这适用于使用 MultiWidgetMultiValueField。 但是由于我们想要使用带有一个 DateField 的小部件,它接受一个值,我们已经覆盖了这个方法。 这里的实现将来自 subwidget 的数据以 DateField 期望的格式组合成一个字符串。


内置部件

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

处理文本输入的部件

这些小部件使用 HTML 元素 inputtextarea

TextInput

class TextInput
;* input_type'text'
  • template_name'django/forms/widgets/text.html'
  • 呈现为:<input type="text" ...>


NumberInput

class NumberInput
  • input_type'number'

  • template_name'django/forms/widgets/number.html'

  • 呈现为:<input type="number" ...>

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


EmailInput

class EmailInput
;* input_type'email'
  • template_name'django/forms/widgets/email.html'
  • 呈现为:<input type="email" ...>


URLInput

class URLInput
;* input_type'url'
  • template_name'django/forms/widgets/url.html'
  • 呈现为:<input type="url" ...>


PasswordInput

class PasswordInput
  • input_type'password'

  • template_name'django/forms/widgets/password.html'

  • 呈现为:<input type="password" ...>

需要一个可选的参数:

render_value

确定在验证错误后重新显示表单时小部件是否将填充值(默认为 False)。


HiddenInput

class HiddenInput
  • input_type'hidden'

  • template_name'django/forms/widgets/hidden.html'

  • 呈现为:<input type="hidden" ...>

请注意,还有一个 MultipleHiddenInput 小部件封装了一组隐藏的输入元素。


DateInput

class DateInput
  • input_type'text'

  • template_name'django/forms/widgets/date.html'

  • 呈现为:<input type="text" ...>

采用与 TextInput 相同的参数,还有一个可选参数:

format

此字段的初始值将显示的格式。

如果没有提供 format 参数,默认格式是在 :setting:`DATE_INPUT_FORMATS` 中找到的第一个格式,并尊重 格式本地化


DateTimeInput

class DateTimeInput
  • input_type'text'

  • template_name'django/forms/widgets/datetime.html'

  • 呈现为:<input type="text" ...>

采用与 TextInput 相同的参数,还有一个可选参数:

format

此字段的初始值将显示的格式。

如果没有提供 format 参数,默认格式是在 :setting:`DATETIME_INPUT_FORMATS` 中找到的第一个格式,并尊重 格式本地化

默认情况下,时间值的微秒部分始终设置为 0。 如果需要微秒,请使用 supports_microseconds 属性设置为 True 的子类。


TimeInput

class TimeInput
  • input_type'text'

  • template_name'django/forms/widgets/time.html'

  • 呈现为:<input type="text" ...>

采用与 TextInput 相同的参数,还有一个可选参数:

format

此字段的初始值将显示的格式。

如果没有提供 format 参数,则默认格式是在 :setting:`TIME_INPUT_FORMATS` 中找到的第一个格式,并尊重 格式本地化

微秒的处理见DateTimeInput


Textarea

class Textarea
;* template_name'django/forms/widgets/textarea.html'
  • 呈现为:<textarea>...</textarea>


选择器和复选框部件

这些小部件使用 HTML 元素 <select><input type="checkbox"><input type="radio">

呈现多个选项的小部件具有 option_template_name 属性,该属性指定用于呈现每个选项的模板。 例如,对于 Select 小部件,select_option.html<select> 渲染 <option>

CheckboxInput

class CheckboxInput
  • input_type'checkbox'

  • template_name'django/forms/widgets/checkbox.html'

  • 呈现为:<input type="checkbox" ...>

需要一个可选的参数:

check_test

如果应选中该值的复选框,则采用 CheckboxInput 的值并返回 True 的可调用对象。


Select

class Select
  • template_name'django/forms/widgets/select.html'

  • option_template_name'django/forms/widgets/select_option.html'

  • 呈现为:<select><option ...>...</select>

choices

当表单字段没有 choices 属性时,此属性是可选的。 如果是,它会在 Field 上更新属性时覆盖您在此处设置的任何内容。


NullBooleanSelect

class NullBooleanSelect
  • template_name'django/forms/widgets/select.html'

  • option_template_name'django/forms/widgets/select_option.html'

选择带有“未知”、“是”和“否”选项的小部件


SelectMultiple

class SelectMultiple
  • template_name'django/forms/widgets/select.html'

  • option_template_name'django/forms/widgets/select_option.html'

类似于 Select,但允许多选:<select multiple>...</select>


RadioSelect

class RadioSelect
  • template_name'django/forms/widgets/radio.html'

  • option_template_name'django/forms/widgets/radio_option.html'

类似于 Select,但呈现为 <li> 标签内的单选按钮列表:

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

要对生成的标记进行更精细的控制,您可以在模板中的单选按钮上循环。 假设表单 myform 的字段 beatles 使用 RadioSelect 作为其小部件:

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

这将产生以下 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>

其中包括 <label> 标签。 要获得更详细的信息,您可以使用每个单选按钮的 tagchoice_labelid_for_label 属性。 例如,这个模板…

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

...将产生以下 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>

如果您决定不循环使用单选按钮——例如,如果您的模板只包含 模板:Myform.beatles——它们将在带有 <li> 标签的 <ul> 中输出,如上所述。

外部 <ul> 容器接收小部件的 id 属性(如果已定义),否则接收 BoundField.auto_id

在单选按钮上循环时,labelinput 标签分别包含 forid 属性。 每个单选按钮都有一个 id_for_label 属性来输出元素的 ID。


CheckboxSelectMultiple

class CheckboxSelectMultiple
  • template_name'django/forms/widgets/checkbox_select.html'

  • option_template_name'django/forms/widgets/checkbox_option.html'

类似于 SelectMultiple,但呈现为复选框列表:

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

外部 <ul> 容器接收小部件的 id 属性(如果已定义),否则接收 BoundField.auto_id

RadioSelect 一样,您可以遍历小部件选项的各个复选框。 与 RadioSelect 不同,如果该字段是必需的,则复选框将不包含 required HTML 属性,因为浏览器验证将要求选中所有复选框,而不是至少选中一个。

当循环复选框时,labelinput 标签分别包含 forid 属性。 每个复选框都有一个 id_for_label 属性来输出元素的 ID。


文件上传部件

FileInput

class FileInput
;* template_name'django/forms/widgets/file.html'
  • 呈现为:<input type="file" ...>


ClearableFileInput

class ClearableFileInput
;* template_name'django/forms/widgets/clearable_file_input.html'
  • 呈现为:<input type="file" ...> 带有额外的复选框输入以清除该字段的值,如果该字段不是必需的并且具有初始数据。


复合部件

MultipleHiddenInput

class MultipleHiddenInput
  • template_name'django/forms/widgets/multiple_hidden.html'

  • 呈现为:多个 <input type="hidden" ...> 标签

一个处理具有值列表的字段的多个隐藏部件。

choices

当表单字段没有 choices 属性时,此属性是可选的。 如果是,它会在 Field 上更新属性时覆盖您在此处设置的任何内容。


SplitDateTimeWidget

class SplitDateTimeWidget
  • template_name'django/forms/widgets/splitdatetime.html'

包装器(使用 MultiWidget)围绕两个小部件:DateInput 表示日期,TimeInput 表示时间。 必须与 SplitDateTimeField 而不是 DateTimeField 一起使用。

SplitDateTimeWidget 有几个可选参数:

date_format

类似于 DateInput.format

time_format

类似于 TimeInput.format

date_attrs
time_attrs

类似于 Widget.attrs。 包含要分别在呈现的 DateInputTimeInput 小部件上设置的 HTML 属性的字典。 如果未设置这些属性,则使用 Widget.attrs 代替。


SplitHiddenDateTimeWidget

class SplitHiddenDateTimeWidget
  • template_name'django/forms/widgets/splithiddendatetime.html'

类似于 SplitDateTimeWidget,但使用 HiddenInput 作为日期和时间。


SelectDateWidget

class SelectDateWidget
  • template_name'django/forms/widgets/select_date.html'

环绕三个 Select 小部件:每个月、日和年各一个。

需要几个可选的参数:

years

在“年份”选择框中使用的可选年份列表/元组。 默认值为包含当前年份和未来 9 年的列表。

months

在“月份”选择框中使用的可选月份字典。

dict 的键对应于月份编号(1-indexed),值是显示的月份:

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

如果不需要 DateField,则 SelectDateWidget 将在列表顶部有一个空选项(默认为 ---)。 您可以使用 empty_label 属性更改此标签的文本。 empty_label 可以是 stringlisttuple。 使用字符串时,所有选择框都会有一个带有此标签的空选项。 如果 empty_label 是 3 个字符串元素的 listtuple,选择框将有自己的自定义标签。 标签应按此顺序 ('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"),
    ),
)