Widgets
Widget 是Django 对HTML 输入元素的表示。Widget 负责渲染HTML和提取GET/POST 字典中的数据。
小贴士
不要将Widget 与_表单字段_搞混淆。表单字段负责验证输入并直接在模板中使用。Widget 负责渲染网页上HTML 表单的输入元素和提取提交的原始数据。但是,Widget 需要_赋值_给表单的字段。
指定Widget
每当你指定表单的一个字段的时候,Django 将使用适合其数据类型的默认Widget。若要查找每个字段使用的Widget,参见_内建的字段_文档。
然而,如果你想要使用一个不同的Widget,你可以在定义字段时使用widget
参数。例如:
from django import forms
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField(widget=forms.Textarea)
这将使用一个Textarea
Widget来设置表单的评论 ,而不是默认的TextInput
Widget。
设置Widget 的参数
很多Widget 都有可选的参数;它们可以在定义字段的Widget 时设置。在下面的示例中,设置了SelectDateWidget
的years
属性:
from django import forms
from django.forms.extras.widgets import SelectDateWidget
BIRTH_YEAR_CHOICES = ('1980', '1981', '1982')
FAVORITE_COLORS_CHOICES = (('blue', 'Blue'),
('green', 'Green'),
('black', 'Black'))
class SimpleForm(forms.Form):
birth_year = forms.DateField(widget=SelectDateWidget(years=BIRTH_YEAR_CHOICES))
favorite_colors = forms.MultipleChoiceField(required=False,
widget=forms.CheckboxSelectMultiple, choices=FAVORITE_COLORS_CHOICES)
可用的Widget 以及它们接收的参数,参见_内建的Widget_。
继承自Select 的Widget
继承自Select
的Widget 负责处理HTML 选项。它们呈现给用户一个可以选择的选项列表。不同的Widget 以不同的方式呈现选项;Select
使用HTML 的列表形式<select>
,而RadioSelect
使用单选按钮。
ChoiceField
字段默认使用Select
。Widget 上显示的选项来自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
属性的Widget 也可以用于不是基于选项的字段 , 例如CharField
—— 当选项与模型有关而不只是Widget 时,建议使用基于ChoiceField
的字段。
自定义Widget 的实例
当Django 渲染Widget 成HTML 时,它只渲染最少的标记 —— Django 不会添加class 的名称和特定于Widget 的其它属性。这表示,网页上所有TextInput
的外观是一样的。
有两种自定义Widget 的方式:基于每个_Widget 实例_和基于每个_Widget 类_。
设置Widget 实例的样式
如果你想让某个Widget 实例与其它Widget 看上去不一样,你需要在Widget 对象实例化并赋值给一个表单字段时指定额外的属性(以及可能需要在你的CSS 文件中添加一些规则)。
例如下面这个简单的表单:
from django import forms
class CommentForm(forms.Form):
name = forms.CharField()
url = forms.URLField()
comment = forms.CharField()
这个表单包含三个默认的TextInput
Widget,以默认的方式渲染 —— 没有CSS 类、没有额外的属性。这表示每个Widget 的输入框将渲染得一模一样:
>>> f = CommentForm(auto_id=False)
>>> f.as_table()
<tr><th>Name:</th><td><input type="text" name="name" /></td></tr>
<tr><th>Url:</th><td><input type="url" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" /></td></tr>
在真正得网页中,你可能不想让每个Widget 看上去都一样。你可能想要给comment 一个更大的输入元素,你可能想让‘name’ Widget 具有一些特殊的CSS 类。可以指定‘type’ 属性来利用新式的HTML5 输入类型。在创建Widget 时使用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"/></td></tr>
<tr><th>Url:</th><td><input type="url" name="url"/></td></tr>
<tr><th>Comment:</th><td><input type="text" name="comment" size="40"/></td></tr>
你还可以使用attrs
设置HTML id
。参见BoundField.id_for_label
示例。
设置Widget 类的样式
可以添加(css
和javascript
)给Widget,以及深度定制它们的外观和行为。
概况来讲,你需要子类化Widget 并_定义一个“Media” 内联类_ 或 _创建一个“media” 属性_。
这些方法涉及到Python 高级编程,详细细节在_表单Assets_ 主题中讲述。
Widget 的基类
Widget
和MultiWidget
是所有_内建Widget_ 的基类,并可用于自定义Widget 的基类。
_class _Widget
(_attrs=None_)
这是个抽象类,它不可以渲染,但是提供基本的属性attrs
。你可以在自定义的Widget 中实现或覆盖render()
方法。
attrs
包含渲染后的Widget 将要设置的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" />'
Changed in Django 1.8:
如果你给一个属性赋值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" />'
render
(_name_, _value_, _attrs=None_)
返回Widget 的HTML,为一个Unicode 字符串。子类必须实现这个方法,否则将引发NotImplementedError
。
它不会确保给出的‘value’ 是一个合法的输入,因此子类的实现应该防卫式地编程。
value_from_datadict
(_data_, _files_, _name_)
根据一个字典和该Widget 的名称,返回该Widget 的值。files
may contain data coming from request.
FILES. 如果没有提供value,则返回None
。 在处理表单数据的过程中,value_from_datadict
可能调用多次,所以如果你自定义并添加额外的耗时处理时,你应该自己实现一些缓存机制。
_class _MultiWidget
(_widgets_, _attrs=None_)
由多个Widget 组合而成的Widget。MultiWidget
始终与MultiValueField
联合使用。
MultiWidget
具有一个必选参数:
widgets
一个包含需要的Widget 的可迭代对象。
以及一个必需的方法:
decompress
(_value_)
这个方法接受来自字段的一个“压缩”的值,并返回“解压”的值的一个列表。可以假设输入的值是合法的,但不一定是非空的。
子类必须实现 这个方法,而且因为值可能为空,实现必须要防卫这点。
“解压”的基本原理是需要“分离”组合的表单字段的值为每个Widget 的值。
有个例子是,SplitDateTimeWidget
将