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 时设置。在下面的示例中,设置了SelectDateWidgetyears 属性:

from django import forms
from django.forms.extras.widgets import SelectDateWidgetBIRTH_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 类的样式

可以添加(cssjavascript)给Widget,以及深度定制它们的外观和行为。

概况来讲,你需要子类化Widget 并定义一个“Media” 内联类创建一个“media” 属性

这些方法涉及到Python 高级编程,详细细节在表单Assets 主题中讲述。

Widget 的基类

WidgetMultiWidget 是所有内建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:

如果你给一个属性赋值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" />'

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 的值。

有个例子是,SplitDateTimeWidgetdatetime 值分离成两个独立的值分别表示日期和时间:

from django.forms import MultiWidgetclass 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子类不同,因为需要弄清楚如何为了在不同widget中展示分割单一值。

渲染中使用的value参数可以是二者之一:

  • 一个列表
  • 一个单一值(比如字符串),它是列表的“压缩”表现形式。

如果value是个列表,render()的输出会是一系列渲染后的子widget。如果value不是一个列表,首先会通过decompress()方法来预处理,创建列表,之后再渲染。

render()方法执行HTML渲染时,列表中的每个值都使用相应的widget来渲染 – 第一个值在第一个widget中渲染,第二个值在第二个widget中渲染,以此类推。

不像单一值的widget,render() 方法并不需要在子类中实现。

format_output(rendered_widgets)

接受选然后的widget(以字符串形式)的一个列表,返回表示全部HTML的Unicode字符串。

这个钩子允许你以任何你想要的方式,格式化widget的HTML设计。

下面示例中的Widget 继承MultiWidget 以在不同的选择框中显示年、月、日。这个Widget 主要想用于DateField 而不是MultiValueField,所以我们实现了value_from_datadict()

from datetime import date
from django.forms import widgetsclass 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 widget。类使用这个元组来启动widget。

format_output()方法相当于在这里没有干什么新的事情(实际上,它和MultiWidget中默认实现的东西相同),但是这个想法是,你可以以自己的方式在widget之间添加自定义的HTML。

必需的decompress()方法将datetime.date 值拆成年、月和日的值,对应每个widget。注意这个方法如何处理valueNone的情况。

value_from_datadict()的默认实现会返回一个列表,对应每一个Widget。当和MultiValueField一起使用MultiWidget的时候,这样会非常合理,但是由于我们想要和拥有单一值得DateField一起使用这个widget,我们必须覆写这一方法,将所有子widget的数据组装成datetime.date。这个方法从POST 字典中获取数据,并且构造和验证日期。如果日期有效,会返回它的字符串,否则会返回一个空字符串,它会使form.is_valid返回False

内建的Widget

Django 提供所有基本的HTML Widget,并在django.forms.widgets 模块中提供一些常见的Widget 组,包括文本的输入各种选择框文件上传多值输入

处理文本输入的Widget

这些Widget 使用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

决定在验证错误后重新显示表单时,Widget 是否填充(默认为False)。

HiddenInput

class HiddenInput

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

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

DateInput

class DateInput

日期以普通的文本框输入:<input type='text' ...>

接收的参数与TextInput 相同,但是带有一些可选的参数:

format

字段的初始值应该显示的格式。

如果没有提供format 参数,默认的格式为参考本地化格式DATE_INPUT_FORMATS 中找到的第一个格式。

DateTimeInput

class DateTimeInput

日期/时间以普通的文本框输入:<input type='text' ...>

接收的参数与TextInput 相同,但是带有一些可选的参数:

format

字段的初始值应该显示的格式。

如果没有提供format 参数,默认的格式为参考本地化格式DATETIME_INPUT_FORMATS 中找到的第一个格式。

TimeInput

class TimeInput

时间以普通的文本框输入:<input type='text' ...>

接收的参数与TextInput 相同,但是带有一些可选的参数:

format

字段的初始值应该显示的格式。

如果没有提供format 参数,默认的格式为参考本地化格式TIME_INPUT_FORMATS 中找到的第一个格式。

Textarea

class Textarea

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

选择和复选框Widget

CheckboxInput

class CheckboxInput

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

接受一个可选的参数:

check_test

一个可调用的对象,接收CheckboxInput 的值并如果复选框应该勾上返回True

Select

class Select

Select widget:<select><option ...>...</select>

choices

当表单字段没有choices 属性时,该属性是随意的。如果字段有choice 属性,当字段的该属性更新时,它将覆盖你在这里的任何设置。

NullBooleanSelect

class NullBooleanSelect

Select Widget,选项为‘Unknown’、‘Yes’ 和‘No’。

SelectMultiple

class SelectMultiple

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

RadioSelect

class RadioSelect

类似Select,但是渲染成<li> 标签中的一个单选按钮列表:

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

你可以迭代模板中的单选按钮来更细致地控制生成的HTML。假设表单myform 具有一个字段beatles,它使用RadioSelect 作为Widget:

{% 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" /> John</label>
</div>
<div class="myradio"><label for="id_beatles_1"><input id="id_beatles_1" name="beatles" type="radio" value="paul" /> Paul</label>
</div>
<div class="myradio"><label for="id_beatles_2"><input id="id_beatles_2" name="beatles" type="radio" value="george" /> George</label>
</div>
<div class="myradio"><label for="id_beatles_3"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" /> 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" /></span>
</label><label for="id_beatles_1">Paul<span class="radio"><input id="id_beatles_1" name="beatles" type="radio" value="paul" /></span>
</label><label for="id_beatles_2">George<span class="radio"><input id="id_beatles_2" name="beatles" type="radio" value="george" /></span>
</label><label for="id_beatles_3">Ringo<span class="radio"><input id="id_beatles_3" name="beatles" type="radio" value="ringo" /></span>
</label>

如果你不迭代单选按钮 —— 例如,你的模板只是简单地包含{{ myform.beatles }} —— 它们将以<ul> 中的<li> 标签输出,就像上面一样。

外层的<ul> 将带有定义在Widget 上的id 属性。

Changed in Django 1.7:

当迭代单选按钮时,labelinput 标签分别包含forid 属性。每个单项按钮具有一个id_for_label 属性来输出元素的ID。

CheckboxSelectMultiple

class CheckboxSelectMultiple

类似SelectMultiple,但是渲染成一个复选框列表:

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

外层的<ul> 具有定义在Widget 上的id 属性。

类似RadioSelect,你可以迭代列表的每个复选框。更多细节参见RadioSelect 的文档。

Changed in Django 1.7:

当迭代单选按钮时,labelinput 标签分别包含forid 属性。 每个单项按钮具有一个id_for_label 属性来输出元素的ID。

文件上传Widget

FileInput

class FileInput

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

ClearableFileInput

class ClearableFileInput

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

复合Widget

MultipleHiddenInput

class MultipleHiddenInput

多个<input type='hidden' ...> Widget。

一个处理多个隐藏的Widget 的Widget,用于值为一个列表的字段。

choices

当表单字段没有choices 属性时,这个属性是可选的。如果字段有choice 属性,当字段的该属性更新时,它将覆盖你在这里的任何设置。

SplitDateTimeWidget

class SplitDateTimeWidget

封装(使用MultiWidget)两个Widget:DateInput 用于日期,TimeInput 用于时间。

SplitDateTimeWidget 有两个可选的属性:

date_format

类似DateInput.format

time_format

类似TimeInput.format

SplitHiddenDateTimeWidget

class SplitHiddenDateTimeWidget

类似SplitDateTimeWidget,但是日期和时间都使用HiddenInput

SelectDateWidget

class SelectDateWidget[source]

封装三个Select Widget:分别用于年、月、日。注意,这个Widget 与标准的Widget 位于不同的文件中。

接收一个可选的参数:

years

一个可选的列表/元组,用于”年“选择框。默认为包含当前年份和未来9年的一个列表。

months

New in Django 1.7.

一个可选的字典,用于”月“选择框。

字典的键对应于月份的数字(从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')
}

empty_label

New in Django 1.8.

如果DateField 不是必选的,SelectDateWidget 将有一个空的选项位于选项的顶部(默认为---)。你可以通过empty_label 属性修改这个文本。empty_label 可以是一个字符串列表元组。当使用字符串时,所有的选择框都带有这个空选项。如果empty_label 为具有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"))

译者:Django 文档协作翻译小组,原文:Built-in widgets。

本文以 CC BY-NC-SA 3.0 协议发布,转载请保留作者署名和文章出处。

Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质。交流群:467338606。

django 1.8 官方文档翻译:5-1-4 内建的Widget相关推荐

  1. django 1.8 官方文档翻译: 2-5-7 自定义查找

    自定义查找 New in Django 1.7. Django为过滤提供了大量的内建的查找(例如,exact和icontains).这篇文档阐述了如何编写自定义查找,以及如何修改现存查找的功能.关于查 ...

  2. django 1.8 官方文档翻译: 3-4-2 基于类的内建通用视图

    基于类的内建通用视图 编写Web应用可能是单调的,因为你需要不断的重复某一种模式. Django尝试从model和 template层移除一些单调的情况,但是Web开发者依然会在view(视图)层经历 ...

  3. django 1.8 官方文档翻译: 3-4-2 内建显示视图

    Django 文档协作翻译小组人手紧缺,有兴趣的朋友可以加入我们,完全公益性质. 交流群:467338606 网站:http://python.usyiyi.cn/django/index.html ...

  4. django 1.8 官方文档翻译:2-1-1 模型语法

    模型 模型是你的数据的唯一的.权威的信息源.它包含你所储存数据的必要字段和行为.通常,每个模型对应数据库中唯一的一张表. 基础: 每个模型都是django.db.models.Model 的一个Pyt ...

  5. django 1.8 官方文档翻译:6-3 Django异常

    Django异常 DJango会抛出一些它自己的异常,以及Python的标准异常. Django核心异常 Django核心异常类定义在django.core.exceptions中. ObjectDo ...

  6. django 1.8 官方文档翻译:14-5 信号

    信号 Django包含一个"信号的分发器",允许解耦的应用在信号出现在框架的任何地方时,都能获得通知.简单来说,信号允许指定的 发送器通知一系列的接收器,一些操作已经发生了.当一些 ...

  7. django 1.8 官方文档翻译:7-2 管理操作

    管理操作 简而言之,Django管理后台的基本流程是,"选择一个对象并改变它".在大多数情况下,这是非常适合的.然而当你一次性要对多个对象做相同的改变,这个流程是非常的单调乏味的. ...

  8. django 1.8 官方文档翻译:13-12 验证器

    验证器 编写验证器 验证器是一个可调用的对象,它接受一个值,并在不符合一些规则时抛出ValidationError异常.验证器有助于在不同类型的字段之间重复使用验证逻辑. 例如,这个验证器只允许偶数: ...

  9. django 1.8 官方文档翻译:7-3 Django管理文档生成器

    Django管理文档生成器 Django的admindocs应用从模型.视图.模板标签以及模板过滤器中,为任何INSTALLED_APPS中的应用获取文档.并且让文档可以在Django admin中使 ...

最新文章

  1. (C++)1036 跟奥巴马一起编程
  2. python最新版本-官方宣布不再维护Python2,并每年发布一个新版本
  3. Java中“==”的使用,以及“==”和equal的比较
  4. Python Tensorflow神经网络实现股票预测
  5. python pickle模块操作
  6. 枚举详解之EnumSet、EnumMap用法
  7. 为什么代码正确却没有爬虫的信息_为什么敷面膜没有效果?原来这才是敷面膜的正确步骤...
  8. HTML4基本编译原理,Stanford公开课《编译原理》学习笔记(1~4课)
  9. github git clone ssh协议 clone超慢解决方案,提高Github Clone速度
  10. word2vec模型
  11. 【正则表达式】网页上敏感词过滤背后的原理你知道吗?
  12. 服务器装系统bios设置方法,BIOS设置USB启动方法
  13. c语言求球的体积用const,牛客练习赛41 E.球的体积并(计算几何)
  14. vue elementui 将data中数据恢复到初始化状态
  15. python xy 官网_pythonxy 安装
  16. 线性方程组什么时候无解?多个解?唯一解?
  17. 限电阴霾下的东北小商户
  18. Netty实现连接西门子PLC
  19. Centos7.5- Linux网络管理技术
  20. PMP证书的必要性、培训机构选择以及其他相关问题整理

热门文章

  1. 7004.vue脚手架快速生成项目
  2. idea mac 查找文件快捷键_idea for mac 最全快捷键整理
  3. 从程序员到项目经理(二)
  4. DECLARE_GLOBAL_DATA_PTR
  5. ARM汇编指令:LDR伪指令和LDR装载指令的区别
  6. 嵌入式Linux系统编程学习之十四signal信号处理机制
  7. 嵌入式Linux系统编程学习之七gdb调试器
  8. java中文本框如何表示为空值_去jsp页面中文本框有NULL值的代码
  9. 【LeetCode】剑指 Offer 25. 合并两个排序的链表
  10. Python基础19(random模块)