[python]アプリケーション開発入門 WTFフォーム作成
Webフォームはアプリケーションにとって重要です。ユーザーが入力したデータをアプリケーションが受け取れるようになります。
投稿やお問い合わせなどフォームを利用することで、開発できる機能の幅がグッと広がります。
フォームはHTMLで以下のようなコードが必要になります。
<form method='POST'>
<input name>
<button type='submit'>送信する</button>
</form>
サーバー側では、以下の処理が必要になります。
Flask-WTFを利用することで、以下のことが楽にできるます。
・フォームのHTMLコードの生成
・入力されたデータのバリデーション
インストール
> pip install flask-wtf
シークレットキー
WTF-Formにはシークレットキーの設定が必要です。
シークレットキーはユニークな文字列にしてください。
ウェブフォームにはCSRF(クロスサイトリクエストフォージェリ)攻撃のリスクがあります。
これを防ぐために、シークレットキーからユニークな文字列を生成し、外部からのフォームリクエストを除外します。
app = Flask(__name__)
app.config['SECRET_KEY'] = 'my_long_secret_key'
CSRF(クロスサイトリクエストフォージェリ)
悪意のある外部サイトのリンクをクリックすると、ユーザーが意図しないリスエストを送信させる。SNSサイトなどのログイン状態が狙われ、本人がリクエストしたとサービスに勘違いさせる。
フォームクラス
FlaskFormを継承してフォームクラスを作成します
フォームのフィールドはフォームクラスのクラスメンバーとして定義します。フィールドは入力タイプに対応したものが、wtformsからインポートできます。
ユーザー情報を入力するUserFormを定義します。
# form.py
from flask_wtf import FlaskForm
from wtforms import StringField, IntegerField, RadioField, SubmitField
from wtforms.validators import DataRequired, NumberRange
class UserForm(FlaskForm):
name = StringField('名前', validators=[DataRequired(message='名前を入力してください')])
age = IntegerField('年齢', validators=[DataRequired(), NumberRange(min=10, message='10才未満はご利用できません(現在)')])
sex = RadioField('性別', choices=[('0', '男性'), ('1', '女性')], default='0')
submit = SubmitField('送信')
# GETの場合の、form.data
{'name': None, 'age': None, 'sex': '0', 'submit': False, 'csrf_token': None}
# POSTの場合の、form.data (ユーザーの入力データ)
{'name': 'john', 'age': 11, 'sex': '0', 'submit': True, 'csrf_token': 'IjBhMmE1ODYyMjdjNzlmMjAxNmFhMDVjMjk3NTgyNmY4NTYwZjNkNGQi.YPmYGA.p8O6Ntc1Y0IAJkmfA8JwaNckKig'}
Flask-Bootstrapのインストール
フォームを生成させる前に、フロントエンドライブラリ BootstrapをFlaskで使えるように、Flask-Bootstrapをインストールします。
Bootstrapを使用すると見た目を簡単に整えることができます。
> pip install flask-bootstraps
Flask-Bootstrapの初期化
Flask-Bootstrapを使用するには初期が必要です。Flaskアプリケーションインスタンス生成時に、Bootstrapも初期化するばOKです。
from flask_bootstrap import Bootstrap
app = Flask(__name__)
bootstrap = Bootstrap(app)
フォームのレンダリング
UserFormのレンダリングするテンプレートファイルです。
formはUserFormのインスタンスです。
{% extends "layout.html" %}
{% block title %}ユーザー情報入力{% endblock %}
{% block page_content %}
<div class="container">
<div class="page-header">ユーザー情報を入力してください</div>
<form method="POST">
{{ form.csrf_token }}
<table class="table">
<tr>
<th scope="col">{{ form.name.label }}</th>
<td>{{ form.name() }}</td>
</tr>
<tr>
<th scope="col">{{ form.age.label }}</th>
<td>{{ form.age() }}</td>
</tr>
<tr>
<th scope="col">{{ form.sex.label }}</th>
<td>{{ form.sex() }}</td>
</tr>
{% for _ , v in form.errors.items() %}
<div class="bg-danger">{{ ", ".join(v) }}</div>
{% endfor %}
</table>
{{ form.submit() }}
</form>
</div>
{% endblock %}
{% block styles %}
{{ super() }}
<style>
#sex {
list-style: none;
display: flex;
}
#sex li {
width: 120px;
text-align: center;
text-decoration: none;
}
</style>
{% endblock %}
csrfを防ぐcsrf_tokenを埋め込むので必須です。
csrf_tokenの代わりに、hiddenFieldをまとめてレンダリングするhidden_tag()でも良いです。
{{ form.フィールドメンバ() }}で、フィールドのhtmlコードを埋め込みます。
print()すると、どのような出力がされるかわかります。
print(form.name())
=> <input id="name" name="name" required type="text" value="">
クラスメンバーをコールしていて変に感じますが、これは親クラスのFieldが特殊メソッド__call__を実装しているからです。
__call__メソッドを実装するとインスタンスをメソッドのようにコールすることができます。
ユーザー情報入力フォーム画面
Flask-Bootstrapのwtf.quick_form(form)を使うと、より簡単にBootstrapスタイルでフォームがレンダリングされます。
{% extends "layout.html" %}
{% import "bootstrap/wtf.html" as wtf %}
{% block title %}ユーザー情報入力{% endblock %}
{% block page_content %}
<div class="container">
<div class="page-header">ユーザー情報を入力してください</div>
{{ wtf.quick_form(form) }}
</div>
{% endblock %}
フォーム処理するビュー関数
ユーザー情報のフォームを受け取るAPIを追加します。
from flask import Flask, render_template, request
from form import UserForm
# application インスタンスの生成
app = Flask(__name__)
# secret keyの設定
app.config['SECRET_KEY'] = 'my_long_secret_key'
# 追加API
@app.route('/name', methods=['GET', 'POST'])
def name():
form = UserForm()
if request.method == 'GET':
return render_template('user.html', form=form)
if request.method == 'POST':
if form.validate_on_submit():
return render_template('user-complete.html',
name=form.name.data,
age=form.age.data,
sex='男性' if form.sex.data == '0' else '女性')
return render_template('user.html', form=form)
HTTPリクエストがGETの場合、formをテンプレートに渡しています。
HTTPリクエストがPOSTの場合、formには入力値が入っていて、その後の処理をする前に、値を検証する必要があります。
validate_on_submit()はフォームにセットされた値をFormFieldに設定したをvalidatorに従って検証します。
検証に失敗するとFlaseが返り、form.errorsにエラーがセットされます。
検証に成功したら、フォーム値を登録成功画面に渡します。
{% extends "layout.html" %}
{% block title %}ユーザー情報入力{% endblock %}
{% block page_content %}
<div class="container">
<h1 class="page-header">こんにちは、{{ name }}さん!</h1>
<div class="bg-success">下記の内容で登録しました</div>
<table class="table">
<tr>
<th scope="col">名前</th>
<td>{{ name }}</td>
</tr>
<tr>
<th scope="col">年齢</th>
<td>{{ age }}</td>
</tr>
<tr>
<th scope="col">性別</th>
<td>{{ sex }}</td>
</tr>
</table>
</div>
{% endblock %}
検証成功後の画面
検証失敗の画面
まとめ
・FlaskでFormを作成するにはFlaskWTFFormを使う
・WTFormを使うにはCSRF対策としてシークレットキーを設定する
・FlaskFormフォームを継承してフォームクラスを作る
・フォームインスタンスからフィールドのHTMLコードを生成する
・POSTされたフォーム値の検証には、validate_on_submit()を使う
参考
CSRF(クロスサイトリクエストフォージェリ)の意味とその対策
FlaskWTForm
Flask Web Development: Developing Web Applications with Python (English Edition)
この記事が気に入ったらサポートをしてみませんか?