Skip to main content

窗口小部件

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

小技巧

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

指定小部件

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

但是,如果要为字段使用不同的窗口小部件,则只需在字段定义中使用 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 使用单选按钮。

默认情况下,在 ChoiceField 字段上使用 Select 窗口小部件。窗口小部件上显示的选项从 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 的字段窗口小部件。

自定义小部件实例

当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>

在真实的网页上,你可能不想让每个小部件看起来一样。你可能想要一个更大的输入元素的注释,你可能希望’name’小部件有一些特殊的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 的例子。

样式控件类

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

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

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

基本widget类

基本窗口小部件类 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" 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 的属性。如果设置为 Falsedatetimetime 值的微秒部分将设置为 0

New in Django 1.9:

在旧版本中,此属性仅在日期和时间小部件(如 False)上定义。

format_value(value)

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

Changed in Django 1.10:

在旧版本中,此方法是一个名为 _format_value() 的私有API。旧的名称将工作,直到Django 2.0。

id_for_label(self, id_)[源代码]

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

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

render(name, value, attrs=None)[源代码]

返回窗口小部件的HTML,作为Unicode字符串。这个方法必须由子类实现,否则会引发 NotImplementedError

给定的“值”不能保证是有效的输入,因此子类实现应该编程防御。

value_from_datadict(data, files, name)[源代码]

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

value_omitted_from_data(data, files, name)
New in Django 1.10.2.

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

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

特殊情况是 CheckboxInputCheckboxSelectMultiple,它们总是返回 False,因为未提供的复选框未出现在HTML表单提交的数据中,因此不知道用户是否实际提交了值。

use_required_attribute(initial)
New in Django 1.10.1.

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

默认情况下,为隐藏的窗口小部件返回 False,否则返回 True。特殊情况是 ClearableFileInput,当 initial 未设置时返回 FalseCheckboxSelectMultiple 总是返回 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().replace(microsecond=0)]
        return [None, None]

小技巧

注意,MultiValueField 具有相反责任的补充方法 compress() - 将所有成员字段的清除值组合成一个。

可用于覆盖的其他方法包括:

render(name, value, attrs=None)[源代码]

参数 value 在此方法中与 Widget 的子类处理不同,因为它必须找出如何拆分单个值以在多个窗口小部件中显示。

渲染时使用的 value 参数可以是以下两种情况之一:

  • A list

  • 作为值的 list 的“压缩”表示的单个值(例如,字符串)。

如果 value 是一个列表,render() 的输出将是渲染的子控件的连接。如果 value 不是列表,它将首先由方法 decompress() 处理以创建列表然后再现。

render() 执行其HTML呈现时,列表中的每个值用相应的小部件呈现 - 第一值在第一小部件中呈现,第二值在第二小部件中呈现等。

与单值小部件不同,方法 render() 不需要在子类中实现。

format_output(rendered_widgets)[源代码]

给定一个呈现的小部件的列表(作为字符串),返回一个代表整个批次的HTML的Unicode字符串。

这个钩子允许你以任何你喜欢的方式格式化小部件的HTML设计。

下面是一个示例小部件,它将 MultiWidget 子类化为在不同的选择框中显示具有日,月和年的日期。这个小部件旨在与 DateField 而不是 MultiValueField 一起使用,因此我们已经实现了 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(DateSelectorWidget, self).__init__(_widgets, attrs)

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

    def format_output(self, rendered_widgets):
        return ''.join(rendered_widgets)

    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)

构造函数在元组中创建几个 Select 小部件。 super 类使用此元组来设置窗口小部件。

format_output() 方法在这里是相当的vanilla(实际上,它是作为 MultiWidget 的默认实现相同),但是想法是你可以添加自定义HTML之间的小部件,如果你想要的。

所需的方法 decompress()datetime.date 值分解成对应于每个小部件的日,月和年值。注意该方法如何处理 valueNone 的情况。

value_from_datadict() 的默认实现返回与每个 Widget 相对应的值的列表。当使用带有 MultiValueFieldMultiWidget 时,这是适当的,但是因为我们想要使用具有单个值的 DateField 的小部件,所以我们重写了该方法以将所有子小部件的数据组合到 datetime.date 中。该方法从 POST 词典提取数据并构造和验证日期。如果它是有效的,我们返回字符串,否则,我们返回一个空字符串,这将导致 form.is_valid 返回 False

内置小部件

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

处理文本输入的小部件

这些小部件使用HTML元素 inputtextarea

TextInput

class TextInput[源代码]

文本输入:<input type="text" ...>

NumberInput

class NumberInput[源代码]

文本输入:<input type="number" ...>

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

EmailInput

class EmailInput[源代码]

文本输入:<input type="email" ...>

URLInput

class URLInput[源代码]

文本输入:<input type="url" ...>

PasswordInput

class PasswordInput[源代码]

密码输入:<input type='password' ...>

采用一个可选参数:

render_value

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

HiddenInput

class HiddenInput[源代码]

隐藏输入:<input type='hidden' ...>

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

DateInput

class DateInput[源代码]

日期输入为一个简单的文本框:<input type='text' ...>

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

format

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

如果没有提供 format 参数,默认格式是 DATE_INPUT_FORMATS 中找到的第一个格式,并且遵守 格式本地化

DateTimeInput

class DateTimeInput[源代码]

日期/时间输入为一个简单的文本框:<input type='text' ...>

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

format

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

如果没有提供 format 参数,默认格式是 DATETIME_INPUT_FORMATS 中找到的第一个格式,并且遵守 格式本地化

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

TimeInput

class TimeInput[源代码]

时间输入为一个简单的文本框:<input type='text' ...>

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

format

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

如果没有提供 format 参数,默认格式是 TIME_INPUT_FORMATS 中找到的第一个格式,并且遵守 格式本地化

对于微秒的处理,参见 DateTimeInput

Textarea

class Textarea[源代码]

文字区域:<textarea>...</textarea>

选择器和复选框小部件

CheckboxInput

class CheckboxInput[源代码]

复选框:<input type='checkbox' ...>

采用一个可选参数:

check_test

一个可调用,它接受 CheckboxInput 的值并返回 True,如果复选框应该检查该值。

Select

class Select[源代码]

选择小部件:<select><option ...>...</select>

choices

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

NullBooleanSelect

class NullBooleanSelect[源代码]

选择具有选项“未知”,“是”和“否”的窗口小部件

SelectMultiple

class SelectMultiple[源代码]

Select 类似,但允许多重选择:<select multiple='multiple'>...</select>

RadioSelect

class RadioSelect[源代码]

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

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

为了对生成的标记进行更精细的控制,您可以循环模板中的单选按钮。假设具有使用 RadioSelect 作为其窗口部件的字段 beatles 的窗体 myform

{% 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 }} - 它们将输出在一个 <ul><li> 标签,如上。

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

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

CheckboxSelectMultiple

class CheckboxSelectMultiple[源代码]

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[源代码]

文件上传输入:<input type='file' ...>

ClearableFileInput

class ClearableFileInput[源代码]

文件上传输入:<input type='file' ...>,带有一个附加的复选框输入以清除字段的值,如果该字段不是必需的,并且有初始数据。

组合小部件

MultipleHiddenInput

class MultipleHiddenInput[源代码]

多个 <input type='hidden' ...> 小部件。

一个窗口小部件,用于处理具有值列表的字段的多个隐藏窗口小部件。

choices

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

SplitDateTimeWidget

class SplitDateTimeWidget[源代码]

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

SplitDateTimeWidget 有两个可选属性:

date_format

类似于 DateInput.format

time_format

类似于 TimeInput.format

SplitHiddenDateTimeWidget

class SplitHiddenDateTimeWidget[源代码]

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

SelectDateWidget

class SelectDateWidget

包装在三个 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

如果不需要 DateFieldSelectDateWidget 将在列表顶部有一个空选项(默认情况下是 ---)。您可以使用 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"),
    ),
)
Changed in Django 1.9:

此小组件以前位于 django.forms.extras.widgets 包中。它现在在 django.forms.widgets 中定义,像其他小部件一样,它可以直接从 django.forms 导入。