天天看點

django 1.8 官方文檔翻譯:5-1-4 内建的WidgetWidgets

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

可能調用多次,是以如果你自定義并添加額外的耗時處理時,你應該自己實作一些緩存機制。

MultiWidget

(widgets, attrs=None)

由多個Widget 組合而成的Widget。

MultiWidget

始終與

MultiValueField

聯合使用。

MultiWidget

具有一個必選參數:

widgets

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

以及一個必需的方法:

decompress

(value)

這個方法接受來自字段的一個“壓縮”的值,并傳回“解壓”的值的一個清單。可以假設輸入的值是合法的,但不一定是非空的。

子類必須實作 這個方法,而且因為值可能為空,實作必須要防衛這點。

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

有個例子是,

SplitDateTimeWidget

datetime

值分離成兩個獨立的值分别表示日期和時間:

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

這個方法中的 

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

構造器在一個元組中建立了多個

Select

widget。

類使用這個元組來啟動widget。

format_output()

方法相當于在這裡沒有幹什麼新的事情(實際上,它和

MultiWidget

中預設實作的東西相同),但是這個想法是,你可以以自己的方式在widget之間添加自定義的HTML。

必需的

decompress()

方法将

datetime.date

值拆成年、月和日的值,對應每個widget。注意這個方法如何處理

value

None

的情況。

value_from_datadict()

的預設實作會傳回一個清單,對應每一個

Widget

。當和

MultiValueField

一起使用

MultiWidget

的時候,這樣會非常合理,但是由于我們想要和擁有單一值得

DateField

一起使用這個widget,我們必須覆寫這一方法,将所有子widget的資料組裝成

datetime.date

。這個方法從

POST

字典中擷取資料,并且構造和驗證日期。如果日期有效,會傳回它的字元串,否則會傳回一個空字元串,它會使

form.is_valid

傳回

False

Django 提供所有基本的HTML Widget,并在

django.forms.widgets

子產品中提供一些常見的Widget 組,包括

文本的輸入

各種選擇框 檔案上傳 多值輸入

處理文本輸入的Widget

這些Widget 使用HTML 元素

input

textarea

TextInput

TextInput

文本輸入:

<input type="text" ...>

NumberInput

NumberInput

<input type="number" ...>

注意,不是所有浏覽器的

number

輸入類型都支援輸入本地化的數字。Django 将字段的

localize

屬性設定為

True

以避免字段使用它們。

EmailInput

EmailInput

<input type="email" ...>

URLInput

URLInput

<input type="url" ...>

PasswordInput

PasswordInput

密碼輸入:

<input type='password' ...>

接收一個可選的參數:

render_value

決定在驗證錯誤後重新顯示表單時,Widget 是否填充(預設為

False

)。

HiddenInput

HiddenInput

隐藏的輸入:

<input type='hidden' ...>

注意,還有一個

MultipleHiddenInput

Widget,它封裝一組隐藏的輸入元素。

DateInput

DateInput

日期以普通的文本框輸入:

<input type='text' ...>

接收的參數與

TextInput

相同,但是帶有一些可選的參數:

format

字段的初始值應該顯示的格式。

如果沒有提供

format

參數,預設的格式為參考

本地化格式

DATE_INPUT_FORMATS

中找到的第一個格式。

DateTimeInput

DateTimeInput

日期/時間以普通的文本框輸入:

<input type='text' ...>

TextInput

format

format

DATETIME_INPUT_FORMATS

TimeInput

TimeInput

時間以普通的文本框輸入:

<input type='text' ...>

TextInput

format

format

TIME_INPUT_FORMATS

Textarea

Textarea

文本區域:

<textarea>...</textarea>

選擇和複選框Widget

CheckboxInput

CheckboxInput

複選框:

<input type='checkbox' ...>

接受一個可選的參數:

check_test

一個可調用的對象,接收

CheckboxInput

的值并如果複選框應該勾上傳回

True

Select

Select

Select widget:

<select><option ...>...</select>

choices

當表單字段沒有

choices

屬性時,該屬性是随意的。如果字段有choice 屬性,當

字段

的該屬性更新時,它将覆寫你在這裡的任何設定。

NullBooleanSelect

NullBooleanSelect

Select Widget,選項為‘Unknown’、‘Yes’ 和‘No’。

SelectMultiple

SelectMultiple

類似

Select

,但是允許多個選擇:

<select multiple='multiple'>...</select>

RadioSelect

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>

标簽。你可以使用單選按鈕的

tag

choice_label

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

當疊代單選按鈕時,

label

input

标簽分别包含

for

id

屬性。每個單項按鈕具有一個

id_for_label

屬性來輸出元素的ID。

CheckboxSelectMultiple

CheckboxSelectMultiple

SelectMultiple

,但是渲染成一個複選框清單:

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

<ul>

具有定義在Widget 上的

id

RadioSelect

,你可以疊代清單的每個複選框。更多細節參見

RadioSelect

的文檔。

label

input

for

id

屬性。 每個單項按鈕具有一個

id_for_label

檔案上傳Widget

FileInput

FileInput

檔案上傳輸入:

<input type='file' ...>

ClearableFileInput

ClearableFileInput

<input type='file' ...>

,帶有一個額外的複選框,如果該字段不是必選的且有初始的資料,可以清除字段的值。

複合Widget

MultipleHiddenInput

MultipleHiddenInput

多個

<input type='hidden' ...>

一個處理多個隐藏的Widget 的Widget,用于值為一個清單的字段。

choices

choices

屬性時,這個屬性是可選的。如果字段有choice 屬性,當

字段

SplitDateTimeWidget

SplitDateTimeWidget

封裝(使用

MultiWidget

)兩個Widget:

DateInput

用于日期,

TimeInput

用于時間。

SplitDateTimeWidget

有兩個可選的屬性:

date_format

DateInput.format

time_format

TimeInput.format

SplitHiddenDateTimeWidget

SplitHiddenDateTimeWidget

SplitDateTimeWidget

,但是日期和時間都使用

HiddenInput

SelectDateWidget

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 協定釋出,轉載請保留作者署名和文章出處。 人手緊缺,有興趣的朋友可以加入我們,完全公益性質。交流群:467338606。