WTFormsを使ったフォーム検証 Form Validation with WTForms

ブラウザ用のviewから提出されるフォームのデータを使って作業する必要があるときは、コードは途端にとても読みづらくなります。このプロセスをより管理しやすくするためにデザインされたライブラリが公開されています。そのなかのひとつが、以下で扱うWTFormsです。もしたくさんのフォームがある状況に自分がいることを見つけた場合、それを試してみたくなるでしょう。 When you have to work with form data submitted by a browser view, code quickly becomes very hard to read. There are libraries out there designed to make this process easier to manage. One of them is `WTForms`_ which we will handle here. If you find yourself in the situation of having many forms, you might want to give it a try.

WTFormsを使って作業するとき、最初に、フォームをクラスとして定義する必要があります。そのために、アプリケーションを複数のモジュールに分解し(パッケージにした大きなアプリケーション(Large Applications as Packages))、フォーム用に区別されたモジュールを追加することをお勧めします。 When you are working with WTForms you have to define your forms as classes first. I recommend breaking up the application into multiple modules (:doc:`packages`) for that and adding a separate module for the forms.

Flask拡張を使ったWTFormsの最大限の活用 Getting the most out of WTForms with an Extension

Flask-WTF拡張はこのパターンを拡張して、フォームとFlaskを使った作業をもっと楽しくするヘルパーをいくつか追加します。Flask-WTFはPyPIから取得できます。 The `Flask-WTF`_ extension expands on this pattern and adds a few little helpers that make working with forms and Flask more fun. You can get it from `PyPI <https://pypi.org/project/Flask-WTF/>`_.

The Forms

以下は、典型的な登録ページ用のフォームの例です: This is an example form for a typical registration page::

from wtforms import Form, BooleanField, StringField, PasswordField, validators

class RegistrationForm(Form):
    username = StringField('Username', [validators.Length(min=4, max=25)])
    email = StringField('Email Address', [validators.Length(min=6, max=35)])
    password = PasswordField('New Password', [
        validators.DataRequired(),
        validators.EqualTo('confirm', message='Passwords must match')
    ])
    confirm = PasswordField('Repeat Password')
    accept_tos = BooleanField('I accept the TOS', [validators.DataRequired()])

Viewの中 In the View

view関数の中では、このフォームの使い方は以下のようになります: In the view function, the usage of this form looks like this::

@app.route('/register', methods=['GET', 'POST'])
def register():
    form = RegistrationForm(request.form)
    if request.method == 'POST' and form.validate():
        user = User(form.username.data, form.email.data,
                    form.password.data)
        db_session.add(user)
        flash('Thanks for registering')
        return redirect(url_for('login'))
    return render_template('register.html', form=form)

ここでは暗黙的にviewがsqlalchemy(Flask内でのSQLAlchemy(SQLAlchemy in Flask))を使っていますが、もちろんそれは(WTFormsの)要件ではないことに注意してください。必要に応じてコードを調整してください。 Notice we're implying that the view is using SQLAlchemy here (:doc:`sqlalchemy`), but that's not a requirement, of course. Adapt the code as necessary.

覚えておくべきこと: Things to remember:

  1. データがHTTPのPOSTメソッドで提出された場合には、リクエストのform属性の値から、データがGETメソッド(のURLの一部)で提出された場合には、リクエストのargs属性から、フォームを作成します。 create the form from the request :attr:`~flask.request.form` value if the data is submitted via the HTTP ``POST`` method and :attr:`~flask.request.args` if the data is submitted as ``GET``.

  2. データを検証するには、データ検証できた場合にはTrueを、その他の場合にはFalseを返すvalidate()メソッドを呼びます。 to validate the data, call the :func:`~wtforms.form.Form.validate` method, which will return ``True`` if the data validates, ``False`` otherwise.

  3. フォームから個々の値へアクセスするには、form.<NAME>.dataへアクセスします。 to access individual values from the form, access `form.<NAME>.data`.

テンプレートの中のフォーム Forms in Templates

それではテンプレート側にうつります。フォームをテンプレートへ渡した場合、テンプレートでフォームを容易に表示できます。これがどれくらい容易であるか見るには、以下のテンプレート例を調べてください。既にWTFormsは我々のためにフォーム生成の半分は済ませています。より好ましいものにするために、ラベルを使ってフィールドを表示し、もしエラーがあればそのそのリストを表示するマクロを書くことができます。 Now to the template side. When you pass the form to the templates, you can easily render them there. Look at the following example template to see how easy this is. WTForms does half the form generation for us already. To make it even nicer, we can write a macro that renders a field with label and a list of errors if there are any.

以下は、そのようなマクロを使う例である_formhelpers.htmlテンプレートです: Here's an example :file:`_formhelpers.html` template with such a macro:

{% macro render_field(field) %}
  <dt>{{ field.label }}
  <dd>{{ field(**kwargs)|safe }}
  {% if field.errors %}
    <ul class=errors>
    {% for error in field.errors %}
      <li>{{ error }}</li>
    {% endfor %}
    </ul>
  {% endif %}
  </dd>
{% endmacro %}

このマクロは、我々のためにフィールドを表示する、WTFormsのfield関数へ転送するための、2つ1組のキーワード引数(a couple of keyword arguments)を受け付けます。キーワード引数はHTML属性として挿入されます。そこで、例えば、(HTMLの)input要素へクラスを追加するにはrender_field(form.username, class='username')と呼び出せます。WTFormsは標準的なPython文字列を返すため、このデータは既にHTML上エスケープされていることを、|safeフィルターを使ってJinja2に伝える必要があることに注意してください。 This macro accepts a couple of keyword arguments that are forwarded to WTForm's field function, which renders the field for us. The keyword arguments will be inserted as HTML attributes. So, for example, you can call ``render_field(form.username, class='username')`` to add a class to the input element. Note that WTForms returns standard Python strings, so we have to tell Jinja2 that this data is already HTML-escaped with the ``|safe`` filter.

以下は、上述の説明で使用している関数(訳注: このページの前半で例示されているview関数のregister関数)用のregister.htmlテンプレートであり、_formhelpers.htmlテンプレートを活用しています: Here is the :file:`register.html` template for the function we used above, which takes advantage of the :file:`_formhelpers.html` template:

{% from "_formhelpers.html" import render_field %}
<form method=post>
  <dl>
    {{ render_field(form.username) }}
    {{ render_field(form.email) }}
    {{ render_field(form.password) }}
    {{ render_field(form.confirm) }}
    {{ render_field(form.accept_tos) }}
  </dl>
  <p><input type=submit value=Register>
</form>

WTFormsに関するさらなる情報は、WTForms websiteを調べてください。 For more information about WTForms, head over to the `WTForms website`_.