【譯】WTForms 2 中文入門教程(速成課程)

你打了個響指, 而後開始致力於你想寫的棒極了的 Python 網絡應用程序. 你寫完一些頁面, 最後你須要着手處理使人討厭的任務:處理和驗證表單輸入. 進入(開始)WTForms.css

可是, 爲何我須要另外一個框架呢?好吧, 一些網絡應用程序框架採用數據庫模型和表單處理相結合的方法. 同時這對很基本的建立/更新視圖函數是很方便的, 可能性是否是每一個你須要的表單都能直接映射到一個數據庫模型. 也許你已經使用通用的表單處理框架, 只是你想要定製這些表單域的 HTML 代碼生成, 而且定義屬於你的驗證器.html

使用 WTForms, 你能生成屬於你的表單域的 HTML 代碼, 此外咱們容許你在模板中定製它. 這容許你維持獨立的代碼和展示, 並把這些凌亂的參數保留在 Python 代碼以外. 由於咱們爭取鬆耦合, 一樣的, 你應該能夠在你喜歡的任意的模板引擎中這麼作.python

備註: 本人未過四級, 翻譯水平有限, 歡迎指正.git

下載和安裝

安裝 WTForms 最簡單的方式是使用 easy_installpip:github

basheasy_install WTForms
# or
pip install WTForms

你能夠從 PyPI 手動 下載 WTForms 而後運行 python setup.py install .數據庫

若是你是那種喜歡這一切風險的人, 就運行來自 Git 的最新版本, 你可以獲取最新變動集的 打包版本, 或者前往 項目主頁 克隆代碼倉庫.django

主要概念

  • Forms 類是 WTForms 的核心容器. 表單(Forms)表示域(Fields)的集合, 域能經過表單的字典形式或者屬性形式訪問.flask

  • Fields(域)作最繁重的工做. 每一個域(field)表明一個數據類型, 而且域操做強制表單輸入爲那個數據類型. 例如, InputRequiredStringField 表示兩種不一樣的數據類型. 域除了包含的數據(data)以外, 還包含大量有用的屬性, 例如標籤、描述、驗證錯誤的列表.bash

  • 每一個域(field)擁有一個Widget(部件)實例. Widget 的工做是渲染域(field)的HTML表示. 每一個域能夠指定Widget實例, 但每一個域默認擁有一個合理的widget. 有些域是簡單方便的, 好比 TextAreaField 就僅僅是默認部件(widget) 爲 TextArea
    StringField.網絡

  • 爲了指定驗證規則, 域包含驗證器(Validators)列表.

開始

讓咱們直接進入正題並定義咱們的第一個表單::

pythonfrom wtforms import Form, BooleanField, StringField, validators

class RegistrationForm(Form):
    username     = StringField('Username', [validators.Length(min=4, max=25)])
    email        = StringField('Email Address', [validators.Length(min=6, max=35)])
    accept_rules = BooleanField('I accept the site rules', [validators.InputRequired()])

當你建立一個表單(form), 你定義域(field)的方法相似於不少ORM定義它們的列(columns):經過定義類變量, 即域的實例.

由於表單是常規的 Python 類, 你能夠很容易地把它們擴展成爲你指望的::

pythonclass ProfileForm(Form):
    birthday  = DateTimeField('Your Birthday', format='%m/%d/%y')
    signature = TextAreaField('Forum Signature')

class AdminProfileForm(ProfileForm):
    username = StringField('Username', [validators.Length(max=40)])
    level    = IntegerField('User Level', [validators.NumberRange(min=0, max=10)])

經過子類, AdminProfileForm 類得到了已經定義的 ProfileForm 類的全部域. 這容許你輕易地在不一樣表單之間共享域的共同子集, 例如上面的例子, 咱們增長 admin-only 的域到 ProfileForm.

使用表單

使用表單和實例化它同樣簡單. 想一想下面這個django風格的視圖函數, 它使用以前定義的 RegistrationForm 類::

pythondef register(request):
    form = RegistrationForm(request.POST)
    if request.method == 'POST' and form.validate():
        user = User()
        user.username = form.username.data
        user.email = form.email.data
        user.save()
        redirect('register')
    return render_response('register.html', form=form)

首先, 咱們實例化表單, 給它提供一些 request.POST 中可用的數據. 而後咱們檢查請求(request)是否是使用 POST 方式, 若是它是, 咱們就驗證表單, 並檢查用戶遵照這些規則. 若是成功了, 咱們建立新的 User 模型, 並從已驗證的表單分派數據給它, 最後保存它.

編輯現存對象

咱們以前的註冊例子展現瞭如何爲新條目接收輸入並驗證, 只是若是咱們想要編輯現有對象怎麼辦?很簡單::

pythondef edit_profile(request):
    user = request.current_user
    form = ProfileForm(request.POST, user)
    if request.method == 'POST' and form.validate():
        form.populate_obj(user)
        user.save()
        redirect('edit_profile')
    return render_response('edit_profile.html', form=form)

這裏, 咱們經過給表單同時提供 request.POST 和用戶(user)對象來實例化表單. 經過這樣作, 表單會從 user 對象獲得在未在提交數據中出現的任何數據.

咱們也使用表單的populate_obj方法來從新填充用戶對象, 用已驗證表單的內容. 這個方法提供便利, 用於當域(field)名稱和你提供數據的對象的名稱匹配時. 一般的, 你會想要手動分配值, 但對於這個簡單例子, 它是最好的. 它也能夠用於CURD和管理(admin)表單.

在控制檯中探索

WTForms 表單是很是簡單的容器對象, 也許找出表單中什麼對你有用的最簡單的方法就是在控制檯中玩弄表單:

python>>> from wtforms import Form, StringField, validators
>>> class UsernameForm(Form):
...     username = StringField('Username', [validators.Length(min=5)], default=u'test')
...
>>> form = UsernameForm()
>>> form['username']
<wtforms.fields.StringField object at 0x827eccc>
>>> form.username.data
u'test'
>>> form.validate()
False
>>> form.errors
{'username': [u'Field must be at least 5 characters long.']}

咱們看到的是當你實例化一個表單的時候, 表單包含全部域的實例, 訪問域能夠經過字典形式或者屬性形式. 這些域擁有它們本身的屬性, 就和封閉的表單同樣.

當咱們驗證表單, 它返回邏輯假, 意味着至少一個驗證規則不知足. form.errors 會給你一個全部錯誤的概要.

python>>> form2 = UsernameForm(username=u'Robert')
>>> form2.data
{'username': u'Robert'}
>>> form2.validate()
True

此次, 咱們實例化 UserForm 時給 username 傳送一個新值, 驗證表單是足夠了.

表單如何獲取數據

除了使用前兩個參數(formdataobj)提供數據以外, 你能夠傳送關鍵詞參數來填充表單. 請注意一些參數名是被保留的: formdata, obj, prefix.

formdataobj優先級高, obj比關鍵詞參數優先級高. 例如:

pythondef change_username(request):
    user = request.current_user
    form = ChangeUsernameForm(request.POST, user, username='silly')
    if request.method == 'POST' and form.validate():
        user.username = form.username.data
        user.save()
        return redirect('change_username')
    return render_response('change_username.html', form=form)

雖然你在實踐中幾乎從未一塊兒使用全部3種方式, 舉例說明WTForms是如何查找 username 域:

  1. 若是表單被提交(request.POST非空), 則處理表單輸入. 實踐中, 即便這個域沒有 表單輸入, 而若是存在任何種類的表單輸入, 那麼咱們會處理表單輸入.

  2. 若是沒有表單輸入, 則按下面的順序嘗試:

    1. 檢查 user 是否有一個名爲 username 的屬性.
    2. 檢查是否提供一個名爲 username 的關鍵詞參數.
    3. 最後, 若是都失敗了, 使用域的默認值, 若是有的話.

驗證器

WTForms中的驗證器(Validators)爲域(field)提供一套驗證器, 當包含域的表單進行驗證時運行. 你提供驗證器可經過域構造函數的第二個參數validators:

pythonclass ChangeEmailForm(Form):
    email = StringField('Email', [validators.Length(min=6, max=120), validators.Email()])

你能夠爲一個域提供任意數量的驗證器. 一般, 你會想要提供一個定製的錯誤消息:

pythonclass ChangeEmailForm(Form):
    email = StringField('Email', [
        validators.Length(min=6, message=_(u'Little short for an email address?')),
        validators.Email(message=_(u'That\'s not a valid email address.'))
    ])

這一般更好地提供你本身的消息, 做爲必要的默認消息是通用的. 這也是提供本地化錯誤消息的方法.

對於內置的驗證器的列表, 查閱 Validators.

渲染域

渲染域和強制它爲字符串同樣簡單:

python>>> from wtforms import Form, StringField
>>> class SimpleForm(Form):
...   content = StringField('content')
...
>>> form = SimpleForm(content='foobar')
>>> str(form.content)
'<input id="content" name="content" type="text" value="foobar" />'
>>> unicode(form.content)
u'<input id="content" name="content" type="text" value="foobar" />'

然而, 渲染域的真正力量來自於它的 __call__() 方法. 調用(calling)域, 你能夠提供關鍵詞參數, 它們會在輸出中做爲HTML屬性注入.

python>>> form.content(style="width: 200px;", class_="bar")
u'<input class="bar" id="content" name="content" style="width: 200px;" type="text" value="foobar" />'

如今, 讓咱們應用這個力量在 Jinja 模板中渲染表單. 首先, 咱們的表單:

pythonclass LoginForm(Form):
    username = StringField('Username')
    password = PasswordField('Password')

form = LoginForm()

而後是模板文件:

jinja<form method="POST" action="/login">
    <div>{{ form.username.label }}: {{ form.username(class="css_class") }}</div>
    <div>{{ form.password.label }}: {{ form.password() }}</div>
</form>

相同的, 若是你使用 Django 模板, 當你想要傳送關鍵詞參數時, 你可使用咱們在Django擴展中提供的模板標籤form_field:

django{% load wtforms %}
<form method="POST" action="/login">
    <div>
        {{ form.username.label }}:
        {% form_field form.username class="css_class" %}
    </div>
    <div>
        {{ form.password.label }}:
        {{ form.password }}
    </div>
</form>

這兩個將會輸出:

html<form method="POST" action="/login">
    <div>
        <label for="username">Username</label>:
        <input class="css_class" id="username" name="username" type="text" value="" />
    </div>
    <div>
        <label for="password">Password</label>:
        <input id="password" name="password" type="password" value="" />
    </div>
</form>

WTForms是模板引擎不可知的, 同時會和任何容許屬性存取、字符串強制(string coercion)、函數調用的引擎共事. 在 Django 模板中, 當你不能傳送參數時, 模板標籤 form_field 提供便利.

顯示錯誤消息

如今咱們的表單擁有一個模板, 讓咱們增長錯誤消息::

jinja

<form method="POST" action="/login"> <div>{{ form.username.label }}: {{ form.username(class="css_class") }}</div> {% if form.username.errors %} <ul class="errors">{% for error in form.username.errors %}<li>{{ error }}</li>{% endfor %}</ul> {% endif %} <div>{{ form.password.label }}: {{ form.password() }}</div> {% if form.password.errors %} <ul class="errors">{% for error in form.password.errors %}<li>{{ error }}</li>{% endfor %}</ul> {% endif %} </form>

若是你喜歡在頂部顯示大串的錯誤消息, 也很簡單:

jinja{% if form.errors %}
    <ul class="errors">
        {% for field_name, field_errors in form.errors|dictsort if field_errors %}
            {% for error in field_errors %}
                <li>{{ form[field_name].label }}: {{ error }}</li>
            {% endfor %}
        {% endfor %}
    </ul>
{% endif %}

因爲錯誤處理會變成至關冗長的事情, 在你的模板中使用 Jinja 宏(macros, 或者相贊成義的) 來減小引用是更好的. (例子)

定製驗證器

這有兩種方式定製的驗證器. 經過定義一個定製的驗證器並在域中使用它:

pythonfrom wtforms.validators import ValidationError

def is_42(form, field):
    if field.data != 42:
        raise ValidationError('Must be 42')

class FourtyTwoForm(Form):
    num = IntegerField('Number', [is_42])

或者經過提供一個在表單內的特定域(in-form field-specific)的驗證器:

pythonclass FourtyTwoForm(Form):
    num = IntegerField('Number')

    def validate_num(form, field):
        if field.data != 42:
            raise ValidationError(u'Must be 42')

對於更多帶參數的複雜驗證器, 查閱 Custom validators部分.

下一步

速成課程只是粗略地講述如何在你的應用程序中開始使用WTForms處理表單輸入和驗證. 對於更多的信息, 你要查閱下面的:


英文原文:http://pythonhosted.org//WTForms/crash_course.html
譯文原文:http://flask123.sinaapp.com/article/60/

相關文章
相關標籤/搜索