鍍金池/ 教程/ Python/ Widgets
點擊劫持保護
安全問題歸檔
Model 類參考
將遺留數(shù)據(jù)庫整合到Django
關(guān)聯(lián)對象參考
內(nèi)建基于類的視圖的API
聚合
Django 中的用戶認證
django.contrib.humanize
Django管理文檔生成器
分頁
使用Django輸出CSV
加密簽名
文件儲存API
安全
Django中的測試
國際化和本地化
為Django編寫首個補丁
條件表達式
日志
模型元選項
部署靜態(tài)文件
執(zhí)行查詢
使用Django認證系統(tǒng)
基于類的視圖
中間件
編寫自定義的django-admin命令
Django 的設(shè)置
格式本地化
數(shù)據(jù)庫訪問優(yōu)化
錯誤報告
基于類的內(nèi)建通用視圖
編寫自定義存儲系統(tǒng)
編寫你的第一個 Django 程序 第3部分
編寫數(shù)據(jù)庫遷移
使用表單
編寫你的第一個 Django 程序 第2部分
編寫你的第一個 Django 程序 第1部分
如何使用會話
系統(tǒng)檢查框架
新手入門
信號
編寫視圖
如何使用WSGI 部署
編寫你的第一個Django應(yīng)用,第6部分
常見的網(wǎng)站應(yīng)用工具
Widgets
內(nèi)建的視圖
模型實例參考
視圖層
Django中的密碼管理
高級教程:如何編寫可重用的應(yīng)用
國際化和本地化
"本地特色"附加功能
TemplateResponse 和 SimpleTemplateResponse
模式編輯器
文件上傳
快速安裝指南
部署 Django
表單 API
表單素材 ( <code>Media</code> 類)
管理文件
其它核心功能
查找 API 參考
表單
Admin
數(shù)據(jù)庫函數(shù)
自定義查找
使用基于類的視圖處理表單
管理操作
開發(fā)過程
編寫你的第一個Django應(yīng)用,第5部分
進行原始的sql查詢
模型層
多數(shù)據(jù)庫
編寫你的第一個 Django 程序 第4部分
Django安全
Django 初探
Django異常
重定向應(yīng)用
按需內(nèi)容處理
管理器
視圖裝飾器
驗證器
使用Django輸出PDF
File對象
Django 的快捷函數(shù)
基于類的通用視圖 —— 索引
為模型提供初始數(shù)據(jù)
模板層
URL調(diào)度器
中間件
模型

Widgets

Widget 是Django 對HTML 輸入元素的表示。Widget 負責渲染HTML和提取GET/POST 字典中的數(shù)據(jù)。

小貼士

不要將Widget 與表單字段搞混淆。表單字段負責驗證輸入并直接在模板中使用。Widget 負責渲染網(wǎng)頁上HTML 表單的輸入元素和提取提交的原始數(shù)據(jù)。但是,Widget 需要賦值給表單的字段。

指定Widget

每當你指定表單的一個字段的時候,Django 將使用適合其數(shù)據(jù)類型的默認Widget。若要查找每個字段使用的Widget,參見內(nèi)建的字段文檔。

然而,如果你想要使用一個不同的Widget,你可以在定義字段時使用widget 參數(shù)。例如:

from django import forms

class CommentForm(forms.Form):
    name = forms.CharField()
    url = forms.URLField()
    comment = forms.CharField(widget=forms.Textarea)

這將使用一個Textarea Widget來設(shè)置表單的評論 ,而不是默認的TextInput Widget。

設(shè)置Widget 的參數(shù)

很多Widget 都有可選的參數(shù);它們可以在定義字段的Widget 時設(shè)置。在下面的示例中,設(shè)置了SelectDateWidgetyears 屬性:

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 以及它們接收的參數(shù),參見內(nèi)建的Widget。

繼承自Select 的Widget

繼承自Select 的Widget 負責處理HTML 選項。它們呈現(xiàn)給用戶一個可以選擇的選項列表。不同的Widget 以不同的方式呈現(xiàn)選項;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 —— 當選項與模型有關(guān)而不只是Widget 時,建議使用基于ChoiceField 的字段。

自定義Widget 的實例

當Django 渲染W(wǎng)idget 成HTML 時,它只渲染最少的標記 —— Django 不會添加class 的名稱和特定于Widget 的其它屬性。這表示,網(wǎng)頁上所有TextInput 的外觀是一樣的。

有兩種自定義Widget 的方式:基于每個Widget 實例和基于每個Widget 類

設(shè)置Widget 實例的樣式

如果你想讓某個Widget 實例與其它Widget 看上去不一樣,你需要在Widget 對象實例化并賦值給一個表單字段時指定額外的屬性(以及可能需要在你的CSS 文件中添加一些規(guī)則)。

例如下面這個簡單的表單:

from django import forms

class CommentForm(forms.Form):
    name = forms.CharField()
    url = forms.URLField()
    comment = forms.CharField()

這個表單包含三個默認的TextInput Widget,以默認的方式渲染 —— 沒有CSS 類、沒有額外的屬性。這表示每個Widget 的輸入框?qū)秩镜靡荒R粯樱?/p>

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

在真正得網(wǎng)頁中,你可能不想讓每個Widget 看上去都一樣。你可能想要給comment 一個更大的輸入元素,你可能想讓‘name’ Widget 具有一些特殊的CSS 類。可以指定‘type’ 屬性來利用新式的HTML5 輸入類型。在創(chuàng)建Widget 時使用Widget.attrs 參數(shù)可以實現(xiàn):

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 設(shè)置HTML id。參見BoundField.id_for_label 示例。

設(shè)置Widget 類的樣式

可以添加(cssjavascript)給Widget,以及深度定制它們的外觀和行為。

概況來講,你需要子類化Widget 并定義一個“Media” 內(nèi)聯(lián)類創(chuàng)建一個“media” 屬性。

這些方法涉及到Python 高級編程,詳細細節(jié)在表單Assets 主題中講述。

Widget 的基類

WidgetMultiWidget 是所有內(nèi)建Widget 的基類,并可用于自定義Widget 的基類。

class Widget(attrs=None)

這是個抽象類,它不可以渲染,但是提供基本的屬性attrs。你可以在自定義的Widget 中實現(xiàn)或覆蓋render() 方法。

attrs

包含渲染后的Widget 將要設(shè)置的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 字符串。子類必須實現(xiàn)這個方法,否則將引發(fā)NotImplementedError。

它不會確保給出的‘value’ 是一個合法的輸入,因此子類的實現(xiàn)應(yīng)該防衛(wèi)式地編程。

value_from_datadict(data, files, name)

根據(jù)一個字典和該Widget 的名稱,返回該Widget 的值。files may contain data coming from request.FILES. 如果沒有提供value,則返回None。 在處理表單數(shù)據(jù)的過程中,value_from_datadict 可能調(diào)用多次,所以如果你自定義并添加額外的耗時處理時,你應(yīng)該自己實現(xiàn)一些緩存機制。

class MultiWidget(widgets, attrs=None)

由多個Widget 組合而成的Widget。MultiWidget 始終與MultiValueField 聯(lián)合使用。

MultiWidget 具有一個必選參數(shù):

widgets

一個包含需要的Widget 的可迭代對象。

以及一個必需的方法:

decompress(value)

這個方法接受來自字段的一個“壓縮”的值,并返回“解壓”的值的一個列表??梢约僭O(shè)輸入的值是合法的,但不一定是非空的。

子類必須實現(xiàn) 這個方法,而且因為值可能為空,實現(xiàn)必須要防衛(wèi)這點。

“解壓”的基本原理是需要“分離”組合的表單字段的值為每個Widget 的值。

有個例子是,SplitDateTimeWidgetdatetime 值分離成兩個獨立的值分別表示日期和時間:

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參數(shù)的處理方式與Widget子類不同,因為需要弄清楚如何為了在不同widget中展示分割單一值。

渲染中使用的value參數(shù)可以是二者之一:

  • 一個列表。
  • 一個單一值(比如字符串),它是列表的“壓縮”表現(xiàn)形式。

如果value是個列表,render()的輸出會是一系列渲染后的子widget。如果value不是一個列表,首先會通過decompress()方法來預處理,創(chuàng)建列表,之后再渲染。

render()方法執(zhí)行HTML渲染時,列表中的每個值都使用相應(yīng)的widget來渲染 -- 第一個值在第一個widget中渲染,第二個值在第二個widget中渲染,以此類推。

不像單一值的widget,render() 方法并不需要在子類中實現(xiàn)。

format_output(_renderedwidgets)

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

這個鉤子允許你以任何你想要的方式,格式化widget的HTML設(shè)計。

下面示例中的Widget 繼承MultiWidget 以在不同的選擇框中顯示年、月、日。這個Widget 主要想用于DateField 而不是MultiValueField,所以我們實現(xiàn)了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)

構(gòu)造器在一個元組中創(chuàng)建了多個Select widget。類使用這個元組來啟動widget。

format_output()方法相當于在這里沒有干什么新的事情(實際上,它和MultiWidget中默認實現(xiàn)的東西相同),但是這個想法是,你可以以自己的方式在widget之間添加自定義的HTML。

必需的decompress()方法將datetime.date 值拆成年、月和日的值,對應(yīng)每個widget。注意這個方法如何處理valueNone的情況。

value_from_datadict()的默認實現(xiàn)會返回一個列表,對應(yīng)每一個Widget。當和MultiValueField一起使用MultiWidget的時候,這樣會非常合理,但是由于我們想要和擁有單一值得DateField一起使用這個widget,我們必須覆寫這一方法,將所有子widget的數(shù)據(jù)組裝成datetime.date。這個方法從POST 字典中獲取數(shù)據(jù),并且構(gòu)造和驗證日期。如果日期有效,會返回它的字符串,否則會返回一個空字符串,它會使form.is_valid返回False。

內(nèi)建的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輸入類型都支持輸入本地化的數(shù)字。Django 將字段的localize 屬性設(shè)置為True 以避免字段使用它們。

EmailInput

class EmailInput

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

URLInput

class URLInput

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

PasswordInput

class PasswordInput

密碼輸入:<input type='password' ...>

接收一個可選的參數(shù):

render_value

決定在驗證錯誤后重新顯示表單時,Widget 是否填充(默認為False)。

HiddenInput

class HiddenInput

隱藏的輸入:<input type='hidden' ...>

注意,還有一個MultipleHiddenInput Widget,它封裝一組隱藏的輸入元素。

DateInput

class DateInput

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

接收的參數(shù)與TextInput 相同,但是帶有一些可選的參數(shù):

format

字段的初始值應(yīng)該顯示的格式。

如果沒有提供format 參數(shù),默認的格式為參考本地化格式DATE_INPUT_FORMATS 中找到的第一個格式。

DateTimeInput

class DateTimeInput

日期/時間以普通的文本框輸入:<input type='text' ...>

接收的參數(shù)與TextInput 相同,但是帶有一些可選的參數(shù):

format

字段的初始值應(yīng)該顯示的格式。

如果沒有提供format 參數(shù),默認的格式為參考本地化格式DATETIME_INPUT_FORMATS 中找到的第一個格式。

TimeInput

class TimeInput

時間以普通的文本框輸入:<input type='text' ...>

接收的參數(shù)與TextInput 相同,但是帶有一些可選的參數(shù):

format

字段的初始值應(yīng)該顯示的格式。

如果沒有提供format 參數(shù),默認的格式為參考本地化格式TIME_INPUT_FORMATS 中找到的第一個格式。

Textarea

class Textarea

文本區(qū)域:<textarea>...</textarea>

選擇和復選框Widget

CheckboxInput

class CheckboxInput

復選框:<input type='checkbox' ...>

接受一個可選的參數(shù):

check_test

一個可調(diào)用的對象,接收CheckboxInput 的值并如果復選框應(yīng)該勾上返回True

Select

class Select

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

choices

當表單字段沒有choices 屬性時,該屬性是隨意的。如果字段有choice 屬性,當字段的該屬性更新時,它將覆蓋你在這里的任何設(shè)置。

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。假設(shè)表單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> 標簽。你可以使用單選按鈕的tag、choice_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:

當?shù)鷨芜x按鈕時,labelinput 標簽分別包含forid 屬性。每個單項按鈕具有一個id_for_label 屬性來輸出元素的ID。

CheckboxSelectMultiple

class CheckboxSelectMultiple

類似SelectMultiple,但是渲染成一個復選框列表:

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

外層的<ul> 具有定義在Widget 上的id 屬性。

類似RadioSelect,你可以迭代列表的每個復選框。更多細節(jié)參見RadioSelect 的文檔。

Changed in Django 1.7:

當?shù)鷨芜x按鈕時,labelinput 標簽分別包含forid 屬性。 每個單項按鈕具有一個id_for_label 屬性來輸出元素的ID。

文件上傳Widget

FileInput

class FileInput

文件上傳輸入:<input type='file' ...>

ClearableFileInput

class ClearableFileInput

文件上傳輸入:<input type='file' ...>,帶有一個額外的復選框,如果該字段不是必選的且有初始的數(shù)據(jù),可以清除字段的值。

復合Widget

MultipleHiddenInput

class MultipleHiddenInput

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

一個處理多個隱藏的Widget 的Widget,用于值為一個列表的字段。

choices

當表單字段沒有choices 屬性時,這個屬性是可選的。如果字段有choice 屬性,當字段的該屬性更新時,它將覆蓋你在這里的任何設(shè)置。

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 位于不同的文件中。

接收一個可選的參數(shù):

years

一個可選的列表/元組,用于”年“選擇框。默認為包含當前年份和未來9年的一個列表。

months

New in Django 1.7.

一個可選的字典,用于”月“選擇框。

字典的鍵對應(yīng)于月份的數(shù)字(從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個字符串元素的列表元組,每個選擇框?qū)⒕哂兴鼈冏远x的空選項??者x項應(yīng)該按這個順序('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 文檔協(xié)作翻譯小組,原文:Built-in widgets

本文以 CC BY-NC-SA 3.0 協(xié)議發(fā)布,轉(zhuǎn)載請保留作者署名和文章出處。

Django 文檔協(xié)作翻譯小組人手緊缺,有興趣的朋友可以加入我們,完全公益性質(zhì)。交流群:467338606。

上一篇:聚合下一篇:部署靜態(tài)文件