ホームページ  >  記事  >  バックエンド開発  >  Python の Flask で WTForms フォーム フレームワークを使用する

Python の Flask で WTForms フォーム フレームワークを使用する

高洛峰
高洛峰オリジナル
2017-03-03 14:17:112115ブラウズ

ダウンロードしてインストールします

WTForms をインストールする最も簡単な方法は、easy_install と pip を使用することです:

easy_install WTForms
# or
pip install WTForms

WTForms を PyPI から手動でダウンロードし、python setup.py install を実行できます。あらゆるリスクを許容する場合は、Git から最新バージョンを実行するか、最新の変更セットのパッケージ バージョンを入手するか、プロジェクトのホームページにアクセスしてコード リポジトリを複製することができます。

主な概念

Forms クラス。 WTForms のコア コンテナです。フォーム (フォーム) は、フォームの辞書フォームまたは属性フォームを通じてアクセスできるフィールドのコレクションを表します。各フィールド (フィールド) は最も重要な作業を行います。たとえば、InputRequired と StringField は、フィールドに含まれるデータに加えて、ラベル、説明、フィールドなどの便利なプロパティも多数含みます。検証エラーのリスト
各フィールド (フィールド) はウィジェット (パーツ) インスタンスを所有します。ウィジェットの役割は、フィールド (フィールド) の HTML 表現をレンダリングすることですが、各フィールドにはウィジェット インスタンスがあります。一部のフィールドはシンプルで便利です。たとえば、TextAreaField は、デフォルトのウィジェットが TextArea である単なる
StringField です。検証ルールを指定するために、フィールドには

Start

を含めます。早速本題に入り、最初のフォームを定義します: :

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

フォームを作成するとき、フィールドを定義する方法は、多くの ORM が列を定義する方法と似ています。つまり、クラス変数、つまりフィールドのインスタンスを定義します。

Forms は通常の 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 クラスのすべてのフィールドを取得できます。簡単に異なるフォーム間で共通のフィールドのサブセットを共有するには、ProfileForm に管理者専用フィールドを追加します。

フォームの使用

フォームの使用は、次の Django をインスタンス化するのと同じくらい簡単です。 -style View 関数。以前に定義した 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 で利用可能なデータを提供します。次に、リクエストが POST メソッドを使用しているかどうかを確認します。そうであれば、フォームを検証し、ユーザーがこれらのルールに従っていることを確認します。成功した場合は、新しいユーザー モデルを作成し、検証されたフォームからデータを割り当て、最後に

既存のオブジェクトを編集します

前の登録例では、入力を受け取り、新しいエントリを検証する方法を示しましたが、既存のオブジェクトを編集したい場合はどうすればよいでしょうか?非常に単純です::

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 とユーザー オブジェクトの両方をフォームに提供することでフォームをインスタンス化します。これにより、フォームは、フォームに表示されないユーザー オブジェクトからデータを取得します。送信されたデータ

また、フォームの Populate_obj メソッドを使用して、検証されたフォームの内容をユーザー オブジェクトに再設定します。このメソッドは、フィールド名がデータを提供するオブジェクトの名前と一致する場合に便利です。手動で値を割り当てたいと思うかもしれませんが、この単純な例では、それが最適です。

コンソールで探索してください

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

フォームをインスタンス化すると、フォームにはフィールドのインスタンスがすべて含まれていることがわかります。フィールドへのアクセスは、辞書形式または属性形式で行うことができます。

フォームを検証すると、少なくとも 1 つの検証ルールが満たされていないことを意味する独自のプロパティが返されます。 form.errors はすべてのエラーの概要を示します。

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

今回は、UserForm をインスタンス化するときに新しい値を username に渡します。これでフォームを確認するのに十分です。

フォームの取得方法データ

例外 最初の 2 つのパラメーター (formdata と obj) を使用してデータを提供することに加えて、フォームに入力するためにキーワード パラメーターを渡すこともできます。formdata、obj、prefix などの一部のパラメーター名は予約されていることに注意してください。

formdata は次のようになります。例:

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 がユーザー名フィールドを検索する方法の例を次に示します。フォームが送信された場合 (リクエスト .POST が空でない場合)、実際には、このフィールドにフォーム入力がなくても、何らかのフォーム入力があればフォーム入力が処理されます。

フォーム入力がない場合は、次の順序で試してください:

ユーザーに username という名前の属性があるかどうかを確認します。


    username という名前のキーワード引数が指定されているかどうかを確認します。
  • 最後に、他のすべてが失敗した場合は、ドメインのデフォルト値を使用してください。
  • validator

    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 までご連絡ください。