你打了个响指, 而后开始致力于你想写的棒极了的 Python 网络应用程序. 你写完一些页面, 最后你须要着手处理使人讨厌的任务:处理和验证表单输入. 进入(开始)WTForms.css
可是, 为何我须要另外一个框架呢?好吧, 一些网络应用程序框架采用数据库模型和表单处理相结合的方法. 同时这对很基本的建立/更新视图函数是很方便的, 可能性是否是每一个你须要的表单都能直接映射到一个数据库模型. 也许你已经使用通用的表单处理框架, 只是你想要定制这些表单域的 HTML 代码生成, 而且定义属于你的验证器.html
使用 WTForms, 你能生成属于你的表单域的 HTML 代码, 此外咱们容许你在模板中定制它. 这容许你维持独立的代码和展示, 并把这些凌乱的参数保留在 Python 代码以外. 由于咱们争取松耦合, 一样的, 你应该能够在你喜欢的任意的模板引擎中这么作.python
备注: 本人未过四级, 翻译水平有限, 欢迎指正.git
安装 WTForms 最简单的方式是使用 easy_install
和 pip
:github
basheasy_install WTForms # or pip install WTForms
你能够从 PyPI 手动 下载 WTForms 而后运行 python setup.py install
.数据库
若是你是那种喜欢这一切风险的人, 就运行来自 Git 的最新版本, 你可以获取最新变动集的 打包版本, 或者前往 项目主页 克隆代码仓库.django
Forms
类是 WTForms 的核心容器. 表单(Forms)表示域(Fields)的集合, 域能经过表单的字典形式或者属性形式访问.flask
Fields
(域)作最繁重的工做. 每一个域(field)表明一个数据类型, 而且域操做强制表单输入为那个数据类型. 例如, InputRequired
和 StringField
表示两种不一样的数据类型. 域除了包含的数据(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
传送一个新值, 验证表单是足够了.
除了使用前两个参数(formdata
和obj
)提供数据以外, 你能够传送关键词参数来填充表单. 请注意一些参数名是被保留的: formdata
, obj
, prefix
.
formdata
比obj
优先级高, 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
域:
若是表单被提交(request.POST
非空), 则处理表单输入. 实践中, 即便这个域没有 表单输入, 而若是存在任何种类的表单输入, 那么咱们会处理表单输入.
若是没有表单输入, 则按下面的顺序尝试:
user
是否有一个名为 username
的属性.username
的关键词参数.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/