Rumah pembangunan bahagian belakang Tutorial Python Python的Flask中使用WTForms表单框架

Python的Flask中使用WTForms表单框架

Mar 03, 2017 pm 02:17 PM

下载和安装

安装 WTForms 最简单的方式是使用 easy_install 和 pip:

easy_install WTForms
# or
pip install WTForms
Salin selepas log masuk

你可以从 PyPI 手动 下载 WTForms 然后运行 python setup.py install .

如果你是那种喜欢这一切风险的人, 就运行来自 Git 的最新版本, 你能够获取最新变更集的 打包版本, 或者前往 项目主页 克隆代码仓库.

主要概念
Forms 类是 WTForms 的核心容器. 表单(Forms)表示域(Fields)的集合, 域能通过表单的字典形式或者属性形式访问.
Fields(域)做最繁重的工作. 每个域(field)代表一个数据类型, 并且域操作强制表单输入为那个数据类型. 例如, InputRequired 和 StringField 表示两种不同的数据类型. 域除了包含的数据(data)之外, 还包含大量有用的属性, 例如标签、描述、验证错误的列表.
每个域(field)拥有一个Widget(部件)实例. Widget 的工作是渲染域(field)的HTML表示. 每个域可以指定Widget实例, 但每个域默认拥有一个合理的widget. 有些域是简单方便的, 比如 TextAreaField 就仅仅是默认部件(widget) 为 TextArea 的
StringField.
为了指定验证规则, 域包含验证器(Validators)列表.
开始
让我们直接进入正题并定义我们的第一个表单::

from 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()])
Salin selepas log masuk

当你创建一个表单(form), 你定义域(field)的方法类似于很多ORM定义它们的列(columns):通过定义类变量, 即域的实例.

因为表单是常规的 Python 类, 你可以很容易地把它们扩展成为你期望的::

class 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)])
Salin selepas log masuk

通过子类, AdminProfileForm 类获得了已经定义的 ProfileForm 类的所有域. 这允许你轻易地在不同表单之间共享域的共同子集, 例如上面的例子, 我们增加 admin-only 的域到 ProfileForm.

使用表单
使用表单和实例化它一样简单. 想想下面这个django风格的视图函数, 它使用之前定义的 RegistrationForm 类::

def 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)
Salin selepas log masuk

首先, 我们实例化表单, 给它提供一些 request.POST 中可用的数据. 然后我们检查请求(request)是不是使用 POST 方式, 如果它是, 我们就验证表单, 并检查用户遵守这些规则. 如果成功了, 我们创建新的 User 模型, 并从已验证的表单分派数据给它, 最后保存它.

编辑现存对象

我们之前的注册例子展示了如何为新条目接收输入并验证, 只是如果我们想要编辑现有对象怎么办?很简单::

def 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)
Salin selepas log masuk

这里, 我们通过给表单同时提供 request.POST 和用户(user)对象来实例化表单. 通过这样做, 表单会从 user 对象得到在未在提交数据中出现的任何数据.

我们也使用表单的populate_obj方法来重新填充用户对象, 用已验证表单的内容. 这个方法提供便利, 用于当域(field)名称和你提供数据的对象的名称匹配时. 通常的, 你会想要手动分配值, 但对于这个简单例子, 它是最好的. 它也可以用于CURD和管理(admin)表单.

在控制台中探索

WTForms 表单是非常简单的容器对象, 也许找出表单中什么对你有用的最简单的方法就是在控制台中玩弄表单:

>>> 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&#39;test&#39;
>>> form.validate()
False
>>> form.errors
{&#39;username&#39;: [u&#39;Field must be at least 5 characters long.&#39;]}
Salin selepas log masuk

我们看到的是当你实例化一个表单的时候, 表单包含所有域的实例, 访问域可以通过字典形式或者属性形式. 这些域拥有它们自己的属性, 就和封闭的表单一样.

当我们验证表单, 它返回逻辑假, 意味着至少一个验证规则不满足. form.errors 会给你一个所有错误的概要.

>>> form2 = UsernameForm(username=u&#39;Robert&#39;)
>>> form2.data
{&#39;username&#39;: u&#39;Robert&#39;}
>>> form2.validate()
True
Salin selepas log masuk

这次, 我们实例化 UserForm 时给 username 传送一个新值, 验证表单是足够了.

表单如何获取数据
除了使用前两个参数(formdata和obj)提供数据之外, 你可以传送关键词参数来填充表单. 请注意一些参数名是被保留的: formdata, obj, prefix.

formdata比obj优先级高, obj比关键词参数优先级高. 例如:

def change_username(request):
 user = request.current_user
 form = ChangeUsernameForm(request.POST, user, username=&#39;silly&#39;)
 if request.method == &#39;POST&#39; and form.validate():
  user.username = form.username.data
  user.save()
  return redirect(&#39;change_username&#39;)
 return render_response(&#39;change_username.html&#39;, form=form)
Salin selepas log masuk

虽然你在实践中几乎从未一起使用所有3种方式, 举例说明WTForms是如何查找 username 域:

如果表单被提交(request.POST非空), 则处理表单输入. 实践中, 即使这个域没有 表单输入, 而如果存在任何种类的表单输入, 那么我们会处理表单输入.
如果没有表单输入, 则按下面的顺序尝试:

  • 检查 user 是否有一个名为 username 的属性.

  • 检查是否提供一个名为 username 的关键词参数.

  • 最后, 如果都失败了, 使用域的默认值, 如果有的话.

验证器

WTForms中的验证器(Validators)为域(field)提供一套验证器, 当包含域的表单进行验证时运行. 你提供验证器可通过域构造函数的第二个参数validators:

class ChangeEmailForm(Form):
 email = StringField(&#39;Email&#39;, [validators.Length(min=6, max=120), validators.Email()])
Salin selepas log masuk

你可以为一个域提供任意数量的验证器. 通常, 你会想要提供一个定制的错误消息:

class ChangeEmailForm(Form):
 email = StringField(&#39;Email&#39;, [
  validators.Length(min=6, message=_(u&#39;Little short for an email address?&#39;)),
  validators.Email(message=_(u&#39;That\&#39;s not a valid email address.&#39;))
 ])
Salin selepas log masuk

这通常更好地提供你自己的消息, 作为必要的默认消息是通用的. 这也是提供本地化错误消息的方法.

对于内置的验证器的列表, 查阅 Validators.

渲染域
渲染域和强制它为字符串一样简单:

>>> from wtforms import Form, StringField
>>> class SimpleForm(Form):
... content = StringField(&#39;content&#39;)
...
>>> form = SimpleForm(content=&#39;foobar&#39;)
>>> str(form.content)
&#39;<input id="content" name="content" type="text" value="foobar" />&#39;
>>> unicode(form.content)
u&#39;<input id="content" name="content" type="text" value="foobar" />&#39;
Salin selepas log masuk

然而, 渲染域的真正力量来自于它的 __call__() 方法. 调用(calling)域, 你可以提供关键词参数, 它们会在输出中作为HTML属性注入.

>>> form.content(style="width: 200px;", class_="bar")
u&#39;<input class="bar" id="content" name="content" style="width: 200px;" type="text" value="foobar" />&#39;
Salin selepas log masuk

现在, 让我们应用这个力量在 Jinja 模板中渲染表单. 首先, 我们的表单:

class LoginForm(Form):
 username = StringField(&#39;Username&#39;)
 password = PasswordField(&#39;Password&#39;)

form = LoginForm()
Salin selepas log masuk

然后是模板文件:

<form method="POST" action="/login">
 <p>{{ form.username.label }}: {{ form.username(class="css_class") }}</p>
 <p>{{ form.password.label }}: {{ form.password() }}</p>
</form>
Salin selepas log masuk

相同的, 如果你使用 Django 模板, 当你想要传送关键词参数时, 你可以使用我们在Django扩展中提供的模板标签form_field:

{% load wtforms %}
<form method="POST" action="/login">
 <p>
  {{ form.username.label }}:
  {% form_field form.username class="css_class" %}
 </p>
 <p>
  {{ form.password.label }}:
  {{ form.password }}
 </p>
</form>
Salin selepas log masuk

这两个将会输出:

<form method="POST" action="/login">
 <p>
  <label for="username">Username</label>:
  <input class="css_class" id="username" name="username" type="text" value="" />
 </p>
 <p>
  <label for="password">Password</label>:
  <input id="password" name="password" type="password" value="" />
 </p>
</form>
Salin selepas log masuk

WTForms是模板引擎不可知的, 同时会和任何允许属性存取、字符串强制(string coercion)、函数调用的引擎共事. 在 Django 模板中, 当你不能传送参数时, 模板标签 form_field 提供便利.

显示错误消息
现在我们的表单拥有一个模板, 让我们增加错误消息::

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

 <p>{{ form.password.label }}: {{ form.password() }}</p>
 {% if form.password.errors %}
  <ul class="errors">{% for error in form.password.errors %}<li>{{ error }}</li>{% endfor %}</ul>
 {% endif %}
</form>
Salin selepas log masuk

如果你喜欢在顶部显示大串的错误消息, 也很简单:

{% 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 %}
Salin selepas log masuk

由于错误处理会变成相当冗长的事情, 在你的模板中使用 Jinja 宏(macros, 或者相同意义的) 来减少引用是更好的. (例子)

定制验证器
这有两种方式定制的验证器. 通过定义一个定制的验证器并在域中使用它:

from wtforms.validators import ValidationError

def is_42(form, field):
 if field.data != 42:
  raise ValidationError(&#39;Must be 42&#39;)

class FourtyTwoForm(Form):
 num = IntegerField(&#39;Number&#39;, [is_42])
Salin selepas log masuk

或者通过提供一个在表单内的特定域(in-form field-specific)的验证器:

class FourtyTwoForm(Form):
 num = IntegerField(&#39;Number&#39;)

 def validate_num(form, field):
  if field.data != 42:
   raise ValidationError(u&#39;Must be 42&#39;)
Salin selepas log masuk

编写WTForm扩展示例

class TagListField(Field):
 widget = TextInput()

 def _value(self):
  if self.data:
   return u&#39;, &#39;.join(self.data)
  else:
   return u&#39;&#39;

 def process_formdata(self, valuelist):
  if valuelist:
   self.data = [x.strip() for x in valuelist[0].split(&#39;,&#39;)]
  else:
   self.data = []
Salin selepas log masuk

根据上面的代码,将TagListField中的字符串转为models.py中定义的Tag对象即可:

class TagListField(Field):
 widget = TextInput()

 def __init__(self, label=None, validators=None,
     **kwargs):
  super(TagListField, self).__init__(label, validators, **kwargs)

 def _value(self):
  if self.data:
   r = u&#39;&#39;
   for obj in self.data:
    r += self.obj_to_str(obj)
   return u&#39;&#39;
  else:
   return u&#39;&#39;

 def process_formdata(self, valuelist):
  print &#39;process_formdata..&#39;
  print valuelist
  if valuelist:
   tags = self._remove_duplicates([x.strip() for x in valuelist[0].split(&#39;,&#39;)])
   self.data = [self.str_to_obj(tag) for tag in tags]
  else:
   self.data = None

 def pre_validate(self, form):
  pass

 @classmethod
 def _remove_duplicates(cls, seq):
  """去重"""
  d = {}
  for item in seq:
   if item.lower() not in d:
    d[item.lower()] = True
    yield item

 @classmethod
 def str_to_obj(cls, tag):
  """将字符串转换位obj对象"""
  tag_obj = Tag.query.filter_by(name=tag).first()
  if tag_obj is None:
   tag_obj = Tag(name=tag)
  return tag_obj

 @classmethod
 def obj_to_str(cls, obj):
  """将对象转换为字符串"""
  if obj:
   return obj.name
  else:
   return u&#39;&#39;

class TagListField(Field):
 widget = TextInput()
 
 def __init__(self, label=None, validators=None,
     **kwargs):
  super(TagListField, self).__init__(label, validators, **kwargs)
 
 def _value(self):
  if self.data:
   r = u&#39;&#39;
   for obj in self.data:
    r += self.obj_to_str(obj)
   return u&#39;&#39;
  else:
   return u&#39;&#39;
 
 def process_formdata(self, valuelist):
  print &#39;process_formdata..&#39;
  print valuelist
  if valuelist:
   tags = self._remove_duplicates([x.strip() for x in valuelist[0].split(&#39;,&#39;)])
   self.data = [self.str_to_obj(tag) for tag in tags]
  else:
   self.data = None
 
 def pre_validate(self, form):
  pass
 
 @classmethod
 def _remove_duplicates(cls, seq):
  """去重"""
  d = {}
  for item in seq:
   if item.lower() not in d:
    d[item.lower()] = True
    yield item
 
 @classmethod
 def str_to_obj(cls, tag):
  """将字符串转换位obj对象"""
  tag_obj = Tag.query.filter_by(name=tag).first()
  if tag_obj is None:
   tag_obj = Tag(name=tag)
  return tag_obj
 
 @classmethod
 def obj_to_str(cls, obj):
  """将对象转换为字符串"""
  if obj:
   return obj.name
  else:
   return u&#39;&#39;
Salin selepas log masuk

主要就是在process_formdata这一步处理表单的数据,将字符串转换为需要的数据。最终就可以在forms.py中这样定义表单了:

...
class ArticleForm(Form):
 """编辑文章表单"""

 title = StringField(u&#39;标题&#39;, validators=[Required()])
 category = QuerySelectField(u&#39;分类&#39;, query_factory=get_category_factory([&#39;id&#39;, &#39;name&#39;]), get_label=&#39;name&#39;)
 tags = TagListField(u&#39;标签&#39;, validators=[Required()])
 content = PageDownField(u&#39;正文&#39;, validators=[Required()])
 submit = SubmitField(u&#39;发布&#39;)
...

...
class ArticleForm(Form):
 """编辑文章表单"""
 
 title = StringField(u&#39;标题&#39;, validators=[Required()])
 category = QuerySelectField(u&#39;分类&#39;, query_factory=get_category_factory([&#39;id&#39;, &#39;name&#39;]), get_label=&#39;name&#39;)
 tags = TagListField(u&#39;标签&#39;, validators=[Required()])
 content = PageDownField(u&#39;正文&#39;, validators=[Required()])
 submit = SubmitField(u&#39;发布&#39;)
...
在views.py中处理表单就很方便了:


def edit_article():
 """编辑文章"""

 form = ArticleForm()
 if form.validate_on_submit():
  article = Article(title=form.title.data, content=form.content.data)
  article.tags = form.tags.data
  article.category = form.category.data
  try:
   db.session.add(article)
   db.session.commit()
  except:
   db.session.rollback()
 return render_template(&#39;dashboard/edit.html&#39;, form=form)

def edit_article():
 """编辑文章"""
 
 form = ArticleForm()
 if form.validate_on_submit():
  article = Article(title=form.title.data, content=form.content.data)
  article.tags = form.tags.data
  article.category = form.category.data
  try:
   db.session.add(article)
   db.session.commit()
  except:
   db.session.rollback()
 return render_template(&#39;dashboard/edit.html&#39;, form=form)
Salin selepas log masuk

代码是不是很简洁了?^_^。。。

当然了写一个完整的WTForms扩展还是很麻烦的。这里只是刚刚入门。可以看官方扩展QuerySelectField的源码。。。
效果:

Python的Flask中使用WTForms表单框架

更多Python的Flask中使用WTForms表单框架相关文章请关注PHP中文网!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Arahan sembang dan cara menggunakannya
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Bagaimana untuk menyelesaikan masalah kebenaran yang dihadapi semasa melihat versi Python di Terminal Linux? Bagaimana untuk menyelesaikan masalah kebenaran yang dihadapi semasa melihat versi Python di Terminal Linux? Apr 01, 2025 pm 05:09 PM

Penyelesaian kepada Isu Kebenaran Semasa Melihat Versi Python di Terminal Linux Apabila anda cuba melihat versi Python di Terminal Linux, masukkan Python ...

Bagaimana Mengajar Asas Pengaturcaraan Pemula Komputer Dalam Kaedah Projek dan Masalah Dikemukakan Dalam masa 10 Jam? Bagaimana Mengajar Asas Pengaturcaraan Pemula Komputer Dalam Kaedah Projek dan Masalah Dikemukakan Dalam masa 10 Jam? Apr 02, 2025 am 07:18 AM

Bagaimana Mengajar Asas Pengaturcaraan Pemula Komputer Dalam masa 10 jam? Sekiranya anda hanya mempunyai 10 jam untuk mengajar pemula komputer beberapa pengetahuan pengaturcaraan, apa yang akan anda pilih untuk mengajar ...

Bagaimana cara menyalin seluruh lajur satu data ke dalam data data lain dengan struktur yang berbeza di Python? Bagaimana cara menyalin seluruh lajur satu data ke dalam data data lain dengan struktur yang berbeza di Python? Apr 01, 2025 pm 11:15 PM

Apabila menggunakan Perpustakaan Pandas Python, bagaimana untuk menyalin seluruh lajur antara dua data data dengan struktur yang berbeza adalah masalah biasa. Katakan kita mempunyai dua DAT ...

Bagaimana untuk mengelakkan dikesan oleh penyemak imbas apabila menggunakan fiddler di mana-mana untuk membaca lelaki-dalam-tengah? Bagaimana untuk mengelakkan dikesan oleh penyemak imbas apabila menggunakan fiddler di mana-mana untuk membaca lelaki-dalam-tengah? Apr 02, 2025 am 07:15 AM

Cara mengelakkan dikesan semasa menggunakan fiddlerevery di mana untuk bacaan lelaki-dalam-pertengahan apabila anda menggunakan fiddlerevery di mana ...

Apakah ungkapan biasa? Apakah ungkapan biasa? Mar 20, 2025 pm 06:25 PM

Ekspresi biasa adalah alat yang berkuasa untuk memadankan corak dan manipulasi teks dalam pengaturcaraan, meningkatkan kecekapan dalam pemprosesan teks merentasi pelbagai aplikasi.

Bagaimanakah uvicorn terus mendengar permintaan http tanpa serving_forever ()? Bagaimanakah uvicorn terus mendengar permintaan http tanpa serving_forever ()? Apr 01, 2025 pm 10:51 PM

Bagaimanakah Uvicorn terus mendengar permintaan HTTP? Uvicorn adalah pelayan web ringan berdasarkan ASGI. Salah satu fungsi terasnya ialah mendengar permintaan HTTP dan teruskan ...

Bagaimana secara dinamik membuat objek melalui rentetan dan panggil kaedahnya dalam Python? Bagaimana secara dinamik membuat objek melalui rentetan dan panggil kaedahnya dalam Python? Apr 01, 2025 pm 11:18 PM

Di Python, bagaimana untuk membuat objek secara dinamik melalui rentetan dan panggil kaedahnya? Ini adalah keperluan pengaturcaraan yang biasa, terutamanya jika perlu dikonfigurasikan atau dijalankan ...

Apakah beberapa perpustakaan Python yang popular dan kegunaan mereka? Apakah beberapa perpustakaan Python yang popular dan kegunaan mereka? Mar 21, 2025 pm 06:46 PM

Artikel ini membincangkan perpustakaan Python yang popular seperti Numpy, Pandas, Matplotlib, Scikit-Learn, Tensorflow, Django, Flask, dan Permintaan, memperincikan kegunaan mereka dalam pengkomputeran saintifik, analisis data, visualisasi, pembelajaran mesin, pembangunan web, dan h

See all articles