小部件是Django对HTML输入元素的表示。该小部件处理HTML的呈现,以及从对应于该小部件的get/post字典中提取数据。
内置小部件生成的HTML使用HTML5语法,目标 <!DOCTYPE html>
. 例如,它使用布尔属性,例如 checked
而不是XHTML风格的 checked='checked'
.
小技巧
小部件不应与 form fields . 表单域处理输入验证的逻辑,并直接在模板中使用。小部件处理网页上HTML表单输入元素的呈现和原始提交数据的提取。但是,小部件确实需要 assigned 形成字段。
每当您在表单上指定一个字段时,Django将使用一个适合要显示的数据类型的默认小部件。要查找在哪个字段上使用的小部件,请参阅有关 内置的 Field 班 .
但是,如果您想对字段使用不同的小部件,则可以使用 widget
关于字段定义的争论。例如::
from django import forms
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField(widget=forms.Textarea)
许多小部件都有可选的额外参数;它们可以在字段上定义小部件时设置。在下面的示例中, years
属性设置为 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
小装置¶小部件继承自 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
-当选择是模型固有的而不仅仅是代表性小部件时,基于字段。
当Django将小部件呈现为HTML时,它只呈现非常少的标记--Django不添加类名或任何其他小部件特定的属性。这意味着,例如,所有 TextInput
Widget将以相同的方式出现在您的网页上。
自定义小工具有两种方法: per widget instance 和 per widget class .
如果要使一个小部件实例与另一个小部件实例看起来不同,则需要在小部件对象实例化并分配给表单字段时指定其他属性(并可能向CSS文件添加一些规则)。
例如,采用以下形式::
from django import forms
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField()
此表单将包括三个默认设置 TextInput
Widget,默认呈现--没有css类,没有额外的属性。这意味着为每个小部件提供的输入框将以完全相同的方式呈现:
>>> f = CommentForm(auto_id=False)
>>> print(f)
<div>Name:<input type="text" name="name" required></div>
<div>Url:<input type="url" name="url" required></div>
<div>Comment:<input type="text" name="comment" required></div>
在真正的Web页面上,您可能不希望每个小部件看起来都一样。您可能希望为评论添加更大的输入元素,并且可能希望‘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"}))
您还可以修改表单定义中的小部件:
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)
>>> print(f)
<div>Name:<input type="text" name="name" class="special" required></div>
<div>Url:<input type="url" name="url" required></div>
<div>Comment:<input type="text" name="comment" size="40" required></div>
也可以设置HTML id
使用 attrs
. 见 BoundField.id_for_label
举个例子。
使用小部件,可以添加资产 (css
和 javascript
)更深入地定制他们的外表和行为。
简言之,您将需要子类化该小部件,并且 define a "Media" inner class 或 create a "media" property .
这些方法涉及一些高级的Python编程,并在 Form Assets 主题指南。
基本控件类 Widget
和 MultiWidget
是所有 built-in widgets 并且可以作为自定义小部件的基础。
Widget
¶无法呈现此抽象类,但提供了基本属性 attrs
. 您还可以实现或重写 render()
自定义小部件上的方法。
包含要在呈现的小部件上设置的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">'
如果您将值指定为 True
或 False
属性,它将呈现为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">'
返回呈现小部件模板时要使用的值字典。默认情况下,字典包含一个键, 'widget'
,它是包含以下键的小部件的字典表示形式:
'name'
:字段的名称 name
参数。
'is_hidden'
:指示此小部件是否隐藏的布尔值。
'required'
:一个布尔值,指示是否需要此小部件的字段。
'value'
:返回的值 format_value()
.
'attrs'
:要在呈现的小部件上设置的HTML属性。的组合 attrs
属性和 attrs
参数。
'template_name'
的价值 self.template_name
.
Widget
子类可以通过重写此方法提供自定义上下文值。
返回此小工具的HTMLID属性,以供 <label>
,给定该字段的ID。如果ID不可用,则返回空字符串。
这个钩子是必要的,因为一些小部件有多个HTML元素,因此有多个ID。在这种情况下,该方法应该返回一个ID值,该值对应于小部件标记中的第一个ID。
使用给定的呈现器将小部件呈现为HTML。如果 renderer
是 None
,来自的渲染器 FORM_RENDERER
使用设置。
给定一个数据字典和这个小部件的名称,返回这个小部件的值。 files
可能包含来自 request.FILES
. 退换商品 None
如果没有提供值。还要注意 value_from_datadict
在处理表单数据的过程中可能会多次调用,因此,如果自定义表单数据并添加昂贵的处理,则应该自己实现一些缓存机制。
鉴于 data
和 files
字典和这个小部件的名称,返回小部件是否有数据或文件。
方法的结果会影响模型表单中的字段 falls back to its default .
特殊情况是 CheckboxInput
, CheckboxSelectMultiple
和 SelectMultiple
,它总是返回 False
因为一个未选中的复选框和未选中的 <select multiple>
不要出现在HTML表单提交的数据中,因此用户是否提交了值是未知的。
用于标识是否应将小部件分组到 <fieldset>
使用一个 <legend>
渲染时。默认为 False
但现在是 True
当小部件包含多个 <input>
标签,如 CheckboxSelectMultiple
, RadioSelect
, MultiWidget
, SplitDateTimeWidget
,以及 SelectDateWidget
。
给定表单域的 initial
值,返回是否可以使用 required
HTML属性。窗体与此方法一起使用 Field.required
和 Form.use_required_attribute
确定是否显示 required
每个字段的属性。
默认情况下,返回 False
用于隐藏的小部件和 True
否则。特殊情况进行了 FileInput
和 ClearableFileInput
,返回 False
什么时候 initial
来设置和 CheckboxSelectMultiple
,总是会回来 False
因为浏览器验证需要勾选所有复选框,而不是至少一个。
在与浏览器验证不兼容的自定义小部件中重写此方法。例如,一个由隐藏的 textarea
元素可能希望始终返回 False
以避免对隐藏字段进行浏览器验证。
MultiWidget
¶由多个小部件组成的小部件。 MultiWidget
与…携手合作 MultiValueField
.
MultiWidget
有一个必需的参数:
一个包含所需小部件的迭代数。例如:
>>> from django.forms import MultiWidget, TextInput
>>> widget = MultiWidget(widgets=[TextInput, TextInput])
>>> widget.render("name", ["john", "paul"])
'<input type="text" name="name_0" value="john"><input type="text" name="name_1" value="paul">'
您可以提供一个词典,以便为 name
属性添加到每个子部件上。在这种情况下,对于每个 (key, widget)
对,则密钥将被追加到 name
以生成属性值。您可以提供空字符串 (''
),以便隐藏一个小部件的后缀。例如:
>>> widget = MultiWidget(widgets={"": TextInput, "last": TextInput})
>>> widget.render("name", ["john", "paul"])
'<input type="text" name="name" value="john"><input type="text" name="name_last" value="paul">'
以及一个必需的方法:
此方法从字段中获取单个“compressed”值,并返回“compressed”值的列表。可以假定输入值有效,但不一定非空。
这种方法 必须执行 通过子类,由于值可能为空,因此实现必须是防御性的。
“解压”的基本原理是必须将表单字段的组合值“拆分”为每个小部件的值。
一个例子就是 SplitDateTimeWidget
转A 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()
与此相反的职责是将所有成员字段的已清除值组合为一个。
它提供了一些自定义上下文:
除 'widget'
中介绍的关键字 Widget.get_context()
, MultiWidget
添加一个 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
而是向该方法传递一个字符串,因此需要解析。最终 return
处理时 value
是 None
,这意味着我们的子部件没有任何默认值。
的默认实现 value_from_datadict()
返回与每个值对应的值列表 Widget
.当使用 MultiWidget
使用一个 MultiValueField
.但由于我们想将此小部件与 DateField
,它接受单个值,我们已经重写了这个方法。这里的实现将子小部件中的数据组合为字符串,格式如下 DateField
期望。
Django提供了所有基本HTML小部件的表示,以及 django.forms.widgets
模块,包括 the input of text , various checkboxes and selectors , uploading files 和 handling of multi-valued input .
这些小部件使用HTML元素 input
和 textarea
.
TextInput
¶NumberInput
¶EmailInput
¶URLInput
¶PasswordInput
¶DateInput
¶DateTimeInput
¶input_type
: 'text'
template_name
: 'django/forms/widgets/datetime.html'
呈现为: <input type="text" ...>
采用与相同的参数 TextInput
,还有一个可选参数:
显示此字段初始值的格式。
如果没有 format
参数,则默认格式为 DATETIME_INPUT_FORMATS
和敬意 格式本地化 。 %U
, %W
,以及 %j
此小工具不支持格式。
默认情况下,时间值的微秒部分始终设置为 0
. 如果需要微秒,请将子类与 supports_microseconds
属性设置为 True
.
TimeInput
¶input_type
: 'text'
template_name
: 'django/forms/widgets/time.html'
呈现为: <input type="text" ...>
采用与相同的参数 TextInput
,还有一个可选参数:
显示此字段初始值的格式。
如果没有 format
提供了参数,默认格式是 TIME_INPUT_FORMATS
和尊重 格式本地化 .
有关微秒的处理,请参见 DateTimeInput
.
Textarea
¶这些小部件使用HTML元素 <select>
, <input type="checkbox">
和 <input type="radio">
.
呈现多个选项的小部件具有 option_template_name
属性,指定用于呈现每个选项的模板。例如,对于 Select
小装置, select_option.html
渲染 <option>
对于一个 <select>
.
CheckboxInput
¶Select
¶NullBooleanSelect
¶SelectMultiple
¶RadioSelect
¶template_name
: 'django/forms/widgets/radio.html'
option_template_name
: 'django/forms/widgets/radio_option.html'
类似于 Select
,但呈现为内的单选按钮列表 <div>
标签:
<div>
<div><input type="radio" name="..."></div>
...
</div>
为了对生成的标记进行更精细的控制,可以循环使用模板中的单选按钮。假设形式 myform
用一块地 beatles
使用A RadioSelect
作为它的小部件:
<fieldset>
<legend>{{ myform.beatles.label }}</legend>
{% for radio in myform.beatles %}
<div class="myradio">
{{ radio }}
</div>
{% endfor %}
</fieldset>
这将生成以下HTML:
<fieldset>
<legend>Radio buttons</legend>
<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>
</fieldset>
其中包括 <label>
标签。为了更精细,可以使用每个单选按钮 tag
, choice_label
和 id_for_label
属性。例如,此模板…
<fieldset>
<legend>{{ myform.beatles.label }}</legend>
{% for radio in myform.beatles %}
<label for="{{ radio.id_for_label }}">
{{ radio.choice_label }}
<span class="radio">{{ radio.tag }}</span>
</label>
{% endfor %}
</fieldset>
…将生成以下HTML:
<fieldset>
<legend>Radio buttons</legend>
<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>
</fieldset>
如果您决定不遍历单选按钮--例如,如果您的模板包括 {{ myform.beatles }}
--它们将以一种 <div>
使用 <div>
标签,如上所述。
外墙 <div>
容器接收 id
小组件的属性(如果已定义)或 BoundField.auto_id
否则的话。
循环切换单选按钮时, label
和 input
标签包括 for
和 id
属性。每个单选按钮都有一个 id_for_label
属性以输出元素的ID。
CheckboxSelectMultiple
¶template_name
: 'django/forms/widgets/checkbox_select.html'
option_template_name
: 'django/forms/widgets/checkbox_option.html'
类似 SelectMultiple
,但呈现为复选框列表:
<div>
<div><input type="checkbox" name="..." ></div>
...
</div>
外墙 <div>
容器接收 id
小组件的属性(如果已定义)或 BoundField.auto_id
否则的话。
喜欢 RadioSelect
,您可以循环显示小部件选择的各个复选框。不像 RadioSelect
,复选框将不包括 required
HTML属性,如果该字段是必需的,因为浏览器验证将要求选中所有复选框,而不是至少选中一个复选框。
循环检查复选框时, label
和 input
标签包括 for
和 id
属性。每个复选框都有一个 id_for_label
属性以输出元素的ID。
FileInput
¶ClearableFileInput
¶SplitDateTimeWidget
¶template_name
: 'django/forms/widgets/splitdatetime.html'
包装器(使用) MultiWidget
)大约两个小部件: DateInput
日期,以及 TimeInput
暂时。必须与一起使用 SplitDateTimeField
而不是 DateTimeField
.
SplitDateTimeWidget
有几个可选参数:
类似 Widget.attrs
. 包含要在呈现的 DateInput
和 TimeInput
分别是小部件。如果没有设置这些属性, Widget.attrs
而是使用。
SelectDateWidget
¶template_name
: 'django/forms/widgets/select_date.html'
三点左右包装 Select
小部件:每月、每天和每年各一个。
接受几个可选参数:
在“年份”选择框中使用的可选年份列表/元组。默认值是包含当前年份和未来9年的列表。
“月份”选择框中要使用的月份的可选dict。
dict的键对应于月份号(1个索引),值是显示的月份:
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"),
}
如果 DateField
不需要, SelectDateWidget
将在列表顶部有一个空选项(即 ---
默认情况下)。您可以使用 empty_label
属性。 empty_label
可以是 string
, list
或 tuple
. 使用字符串时,所有选择框都将有一个带有此标签的空选项。如果 empty_label
是一个 list
或 tuple
在3个字符串元素中,选择框将有自己的自定义标签。标签应按此顺序排列 ('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"),
),
)
7月 22, 2024