Flask-WTF を使ったフォームの作り方

Python3CS50,Flask

Flask-WTF の使い方。事前に入力項目を定義すると、自動で HTML の入力欄が生成され、入力値のバリデーションチェックを行ってくれる。さらに CSRF ( Cross-site Request Forgery ) 対策がなされている。 Flask-WTF は、 WTForms の Flask 用ラッパー。つまり WTForms の Flask 版。

特徴

  • 入力欄を自動生成
  • バリデーション機能
  • CSRF 対策
  • reCAPTCHA 対応
  • ファイルアップロード機能

インストール

$ pip install Flask-WTF

使い方

  1. form.py で入力欄 (フォーム) の定義
  2. view.py で 1 で作ったフォーム情報 を template に渡す
  3. template で入力欄の生成
  4. view.py でバリデーションチェック

ログイン画面を作ってみる。

1. form.py ファイルを作成し、ここに必要な入力項目を定義していく。

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Length, Email

class LoginForm(FlaskForm):
    email = StringField('Email address', validators=[DataRequired(), Email()])
    password = PasswordField('Password', validators=[DataRequired(), Length(min=6)])   
    submit = SubmitField('Login')

text 型の入力フォームなら StringField()textarea 型なら TextAreaField() などを定義する。第一引数はラベル名、第二引数はバリデーションのタイプを指定する。

Field のパラメータの詳細
https://wtforms.readthedocs.io/en/stable/fields/#the-field-base-class

Remember me 機能をつけることもできる。
remember = BooleanField('Remember me')

2, 4. view.py で LoginForm インスタンスを作成し、 GET リクエストだったら、 render_template() の第二引数に指定し template に渡す。 POST リクエストだったら、form.validate_on_submit() で送られてきた入力値のバリデーションチェックを行う。チェックが通らないと、入力画面にエラーメッセージが表示される。( form.py でエラーメッセージを指定しないとデフォルトのメッセージとなる。)

@auth.route('/login', methods=['GET', 'POST'])
def login():
    # LoginForm インスタンスの作成
    form = LoginForm()

    if request.method == 'POST':
        # バリデーション
        if form.validate_on_submit():
	    # ログイン後の処理
	    return redirect(url_for('main.index'))

    return render_template('auth/login.html', form=form)

3. templates/login.html ファイルを作る。
{{ form.csrf_token }} は CSRF 対策のためなので記述しておく。

<form action="{{ url_for('auth.login') }}" method="POST">
    {{ form.csrf_token }}
    {{ form.email.label }} {{ form.email }}
    <div>
      {% for error in form.email.errors %}
          <span style="color: red;">{{ error }}</span>
      {% endfor %}
    </div>
    {{ form.password.label }} {{ form.password }}
    <div>
      {% for error in form.password.errors %}
          <span style="color: red;">{{ error }}</span>
      {% endfor %}
    </div>
    {{ form.submit }}
</form>

バリデーション

種類

https://wtforms.readthedocs.io/en/stable/validators/

DataRequired():必須
Email():メールアドレスに使える文字のみ
Length(min, max):文字数制限
NumberRange(min, max):数値制限
EqualTo(fieldname):2 つのフィールドの入力値が同一か(パスワードと確認用パスワードとか)

password = PasswordField('Password', validators=[DataRequired()])  
confirm = PasswordField('Confirm Password',  
                                     validators=[DataRequired(), EqualTo('confirm')])

自分でバリデーションを作ることもできる。
https://wtforms.readthedocs.io/en/stable/validators/#custom-validators

エラーメッセージを変える

DataRequired() のデフォルトエラーメッセージは、「 This field is required. 」となっている。
エラーメッセージを「 Password is required.  」に変える場合には、

password = PasswordField('Password', validators=[DataRequired(message='Password is required.')])  

CSRF 対策

この対策を有効にするには、シークレットキーが必要となるので、 config.py で設定した。

# config.py
WTF_CSRF_ENABLED = True
SECRET_KEY = 'secret key'
# WTF_CSRF_SECRET_KEY= 'wtf secret key'

create_app() で設定することもできる。

app.config['SECRET_KEY'] = 'secret key'
app.config['WTF_CSRF_ENABLED'] = True

デフォルトでは、Flask アプリケーション自体のシークレットキーが使用される。 Flask-WTF ( CSRF ) 用と分けたい場合には、 WTF_CSRF_SECRET_KEY で設定する。

pytest する際は WTF_CSRF_ENABLED = False とする。
 POST 送信時にデータに  csrf_token=g.csrf_token のようにしてトークンを template に送ってもテスト可能なよう。
https://blog.ikappio.com/handling-csrf-tokens-in-flask-wtf-tests/

template に書いた {{ form.csrf_token }}hidden として出力される。

Posted by Agopeanuts