搜尋
首頁後端開發Python教學Python的Flask中使用WTForms表單框架

下載和安裝

安裝WTForms 最簡單的方式是使用easy_install 和pip:

easy_install WTForms
# or
pip install WTForms

#你可以從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()])

當你建立一個表單(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)])

透過子類別, 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)

首先, 我們實例化表單, 給它提供一些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)

這裡, 我們透過給表單同時提供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;]}

我們看到的是當你實例化一個表單的時候, 表單包含所有域的實例, 存取域可以透過字典形式或屬性形式. 這些域擁有它們自己的屬性, 就和封閉的表單一樣.

當我們驗證表單, 它返回邏輯假, 意味著至少一個驗證規則不滿足. form.errors 會給你一個所有錯誤的概要.

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

這次, 我們實例化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)

#雖然你在實踐中幾乎從未一起使用所有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()])

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

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

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

对于内置的验证器的列表, 查阅 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;

然而, 渲染域的真正力量来自于它的 __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;

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

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

form = LoginForm()

然后是模板文件:

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

相同的, 如果你使用 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>

这两个将会输出:

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

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>

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

{% 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, 或者相同意义的) 来减少引用是更好的. (例子)

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

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

或者通过提供一个在表单内的特定域(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;)

编写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 = []

根据上面的代码,将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;

主要就是在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)

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

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

Python的Flask中使用WTForms表單框架

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

陳述
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
Python:遊戲,Guis等Python:遊戲,Guis等Apr 13, 2025 am 12:14 AM

Python在遊戲和GUI開發中表現出色。 1)遊戲開發使用Pygame,提供繪圖、音頻等功能,適合創建2D遊戲。 2)GUI開發可選擇Tkinter或PyQt,Tkinter簡單易用,PyQt功能豐富,適合專業開發。

Python vs.C:申請和用例Python vs.C:申請和用例Apr 12, 2025 am 12:01 AM

Python适合数据科学、Web开发和自动化任务,而C 适用于系统编程、游戏开发和嵌入式系统。Python以简洁和强大的生态系统著称,C 则以高性能和底层控制能力闻名。

2小時的Python計劃:一種現實的方法2小時的Python計劃:一種現實的方法Apr 11, 2025 am 12:04 AM

2小時內可以學會Python的基本編程概念和技能。 1.學習變量和數據類型,2.掌握控制流(條件語句和循環),3.理解函數的定義和使用,4.通過簡單示例和代碼片段快速上手Python編程。

Python:探索其主要應用程序Python:探索其主要應用程序Apr 10, 2025 am 09:41 AM

Python在web開發、數據科學、機器學習、自動化和腳本編寫等領域有廣泛應用。 1)在web開發中,Django和Flask框架簡化了開發過程。 2)數據科學和機器學習領域,NumPy、Pandas、Scikit-learn和TensorFlow庫提供了強大支持。 3)自動化和腳本編寫方面,Python適用於自動化測試和系統管理等任務。

您可以在2小時內學到多少python?您可以在2小時內學到多少python?Apr 09, 2025 pm 04:33 PM

兩小時內可以學到Python的基礎知識。 1.學習變量和數據類型,2.掌握控制結構如if語句和循環,3.了解函數的定義和使用。這些將幫助你開始編寫簡單的Python程序。

如何在10小時內通過項目和問題驅動的方式教計算機小白編程基礎?如何在10小時內通過項目和問題驅動的方式教計算機小白編程基礎?Apr 02, 2025 am 07:18 AM

如何在10小時內教計算機小白編程基礎?如果你只有10個小時來教計算機小白一些編程知識,你會選擇教些什麼�...

如何在使用 Fiddler Everywhere 進行中間人讀取時避免被瀏覽器檢測到?如何在使用 Fiddler Everywhere 進行中間人讀取時避免被瀏覽器檢測到?Apr 02, 2025 am 07:15 AM

使用FiddlerEverywhere進行中間人讀取時如何避免被檢測到當你使用FiddlerEverywhere...

Python 3.6加載Pickle文件報錯"__builtin__"模塊未找到怎麼辦?Python 3.6加載Pickle文件報錯"__builtin__"模塊未找到怎麼辦?Apr 02, 2025 am 07:12 AM

Python3.6環境下加載Pickle文件報錯:ModuleNotFoundError:Nomodulenamed...

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

AI Hentai Generator

AI Hentai Generator

免費產生 AI 無盡。

熱門文章

R.E.P.O.能量晶體解釋及其做什麼(黃色晶體)
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳圖形設置
3 週前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您聽不到任何人,如何修復音頻
3 週前By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解鎖Myrise中的所有內容
4 週前By尊渡假赌尊渡假赌尊渡假赌

熱工具

Safe Exam Browser

Safe Exam Browser

Safe Exam Browser是一個安全的瀏覽器環境,安全地進行線上考試。該軟體將任何電腦變成一個安全的工作站。它控制對任何實用工具的訪問,並防止學生使用未經授權的資源。

MantisBT

MantisBT

Mantis是一個易於部署的基於Web的缺陷追蹤工具,用於幫助產品缺陷追蹤。它需要PHP、MySQL和一個Web伺服器。請查看我們的演示和託管服務。

SAP NetWeaver Server Adapter for Eclipse

SAP NetWeaver Server Adapter for Eclipse

將Eclipse與SAP NetWeaver應用伺服器整合。

SublimeText3 英文版

SublimeText3 英文版

推薦:為Win版本,支援程式碼提示!

SublimeText3 Mac版

SublimeText3 Mac版

神級程式碼編輯軟體(SublimeText3)