小部件是Django对HTML输入元素的表示。 窗口小部件处理HTML的呈现,以及从与窗口小部件对应的GET / POST字典中提取数据。
内置窗口小部件生成的HTML使用HTML5语法,目标是<!DOCTYPE html>
例如,它使用布尔属性,例如checked
,而不是checked ='checked'
的XHTML样式。
无论何时在表单上指定字段,Django都将使用适合于要显示的数据类型的默认窗口小部件。 要查找在哪个字段上使用哪个窗口小部件,请参阅有关内置字段类的文档。
但是,如果要为字段使用不同的窗口小部件,则可以在字段定义中使用窗口小部件
参数。 例如:
from django import forms
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField(widget=forms.Textarea)
许多小部件都有可选的额外参数;在字段上定义窗口小部件时可以设置它们。 在以下示例中,为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
的字段,当选择是模型固有的而不仅仅是表示小部件时。
当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'}))
然后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
。
使用小部件,可以添加资源(css
和javascript
)并更深入地定制其外观和行为。
简而言之,您需要对窗口小部件进行子类化,并且定义“Media”内部类或创建“media”属性。
这些方法涉及一些高级Python编程,并在表单资产主题指南中有详细描述。
基本窗口小部件类窗口小部件
和MultiWidget
由所有内置窗口小部件子类化,并可作为自定义窗口小部件的基础。
Widget
¶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 />'
如果为属性指定值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" />'
get_context
(name, value, attrs)[source]¶返回呈现窗口小部件模板时要使用的值的字典。 默认情况下,字典包含单个键'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。 如果renderer
为None
,则使用FORM_RENDERER
设置中的渲染器。
添加了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]¶给定data
和files
字典和此小部件的名称,返回是否有小部件的数据或文件。
方法的结果会影响模型形式中的字段是否回退到其默认值。
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
¶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
.
Django提供了所有基本HTML小部件的表示,以及django.forms.widgets
模块中一些常用的小部件组,包括文本输入,各种复选框和选择器,上传文件,以及处理多值输入。
这些小部件使用HTML元素input
和textarea
。
TextInput
¶NumberInput
¶EmailInput
¶URLInput
¶PasswordInput
¶PasswordInput
[source]¶input_type
: 'password'
template_name
: 'django/forms/widgets/password.html'
<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
).
DateInput
¶DateInput
[source]¶input_type
: 'text'
template_name
: 'django/forms/widgets/date.html'
<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
¶DateTimeInput
[source]¶input_type
: 'text'
template_name
: 'django/forms/widgets/datetime.html'
<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
¶TimeInput
[source]¶input_type
: 'text'
template_name
: 'django/forms/widgets/time.html'
<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
.
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
¶CheckboxInput
[source]¶input_type
: 'checkbox'
template_name
: 'django/forms/widgets/checkbox.html'
<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
¶NullBooleanSelect
¶SelectMultiple
¶RadioSelect
¶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
¶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.
SplitDateTimeWidget
¶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
¶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.
SelectDateWidget
¶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"),
),
)
Jan 17, 2018