Flask入門|Flask-WTFの使い方とメリットを学ぶ【チャプター4-07】

ながみえ

一つ前のページではテンプレートマクロについて学習しました。

今回は Flask-WTF について見ていきましょう。

Chapter1:Flask入門編
Chapter2:Jinja2入門編
Chapter3:フィルター編
Chapter4:フォーム編

 ・Chapter4-1:HTTPとは何か
 ・Chapter4-2:フォームの基本を理解しよう
 ・Chapter4-3:WTFormsを理解しよう
 ・Chapter4-4:フォームのバリデーションを理解しよう
 ・Chapter4-5:カスタムバリデータを理解しよう
 ・Chapter4-6:テンプレートマクロを理解しよう
 ・Chapter4-7:Flask-WTFを理解しよう ◁今回はここ
Chapter5:データベース編
Chapter6:Flaskの便利機能編
Chapter7:アプリ開発編

Chapter4では、HTTPやフォーム、バリデーションの基礎を学びながら、Flaskでフォーム入力を処理する方法を段階的に習得してきました。

ここまでの知識を組み合わせれば、ある程度複雑なフォームの処理も可能になりますが、実際のWebアプリ開発では「安全性」や「記述の簡潔さ」も重要です。

そこで登場するのが「Flask-WTF」という拡張ライブラリです。

これを使うことで、ここまでに学んだ処理が ぐっと整理され、効率的かつ安全に なります。

本記事は 有料記事(100円)ですが、現在は期間限定で無料公開中です。

<<前のページ

Flaskの記事一覧

次のページ>>

Flask-WTFとは?基本機能と導入メリットを解説

【Python】勉強猫がノートパソコンを前にして学習を始める様子。記事内の学習スタート用イラスト

Flask-WTFの仕組みと活用ポイント

Flask-WTF とは、Flaskでフォームを扱う際に便利なライブラリ「WTForms」を、Flaskにスムーズに統合するための 拡張機能 です。

  • Flask + WTForms = Flask-WTF
  • 正式には flask_wtf モジュールと呼ばれる
  • WTForms自体はFlask限定のものではない

これを使うことで、次のような利点があります:

  • フォーム用テンプレートが簡潔になる
  • request.form.get() を自分で書かなくてよい
  • バリデーションの自動呼び出し
  • HTMLフォームに必要な CSRFトークン の自動挿入

コード簡略化とCSRF対策を自動化する利点

Chapter4-1~4-6までは、フォームの処理において以下のような手順が必要でした:

  • request.method でGETとPOSTを分岐
  • request.form.get("フィールド名")で値を取得
  • 各値に対してバリデーションを手動で実装
  • テンプレート内でフィールドのラベルや入力タグを手書き
  • セキュリティのためのCSRF対策を自分で考える必要がある

Flask-WTFを使えば、これらが一気に簡単になります。

従来のやり方(手動)Flask-WTFを使ったやり方
フィールドの取得・バリデーションを手動form.validate_on_submit() で一括処理
CSRFトークンを自前で挿入自動で含まれる
HTMLフォームを自分で記述{{ form.フィールド名() }} で簡単表示

Flask-WTFの導入手順と構文の基本

Flask-WTFをインストールしよう

まずは Flask-WTF ライブラリをインストールします。

pip install flask-wtf

Flask-WTF は、WTForms に加えて Flask との連携機能を提供してくれるライブラリです。

CSRFトークンの自動生成や、テンプレートでのフィールドレンダリングなど、初心者がつまづきやすいポイントを補ってくれます。

secret_keyの役割と設定方法

Flask-WTFを使うには、Flaskのアプリケーションに「秘密鍵(secret_key)」を設定する必要があります。

from flask import Flask

app = Flask(__name__)
app.secret_key = "これはCSRFトークンのために必要な秘密鍵"

フォームの中に「CSRFトークン(不正アクセス防止用の特殊な文字列)」が自動的に埋め込まれます。

このトークンの生成に「secret_key」が使われるため、Flask-WTFを使うときは必ず設定しましょう。

FlaskFormを使ったフォーム定義の例

次に、実際に使うフォームのクラスを作成します。

from flask_wtf import FlaskForm # Flask_wtfモジュールのFlaskFormクラスをインポート
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

# FlaskFormを継承したNameFormクラスを定義 ⇒ Flaskアプリで使用可能に
class NameForm(FlaskForm):
    name = StringField("名前", validators=[DataRequired()])
    submit = SubmitField("送信") # 送信ボタンを定義(ラベルは「送信」)
  • FlaskForm はすべてのフォームクラスの基底クラスです。
  • StringField("名前") は、テキスト入力欄の部品で、「名前」というラベルが付きます。
  • SubmitField("送信") は送信ボタンです。
  • DataRequired() は「この欄は必須です」というバリデーション(入力チェック)を担当します。

これで フォームの定義(形・バリデーション) が完了 です。

次はこれを使って、Webアプリとしてどう動かすのかを見ていきましょう。

Flask-WTF導入前後のコード比較で理解を深めよう

【Python】勉強猫がノートパソコンを見ながら考え込む様子。記事内の休憩用イラスト

手動処理でフォームを実装する方法

from flask import Flask, request, render_template
app = Flask(__name__)

@app.route("/hello", methods=["GET", "POST"])
def hello():
    name = ""
    if request.method == "POST":
        name = request.form.get("name")  # フォームから送られてきた値を取り出す
    return render_template("hello.html", name=name)
<form method="POST">
  <input type="text" name="name">  <!-- 入力欄 -->
  <input type="submit" value="送信"> <!-- ボタン -->
</form>

{% if name %}
<p>こんにちは、{{ name }}さん!</p>
{% endif %}
  • 値の取得やチェックを毎回自分で書かないといけない
  • バリデーション(未入力など)も自分で作る必要がある
  • セキュリティ(CSRF対策)も入っていない

Flask-WTFでフォームを簡単かつ安全に処理

from flask import Flask, render_template
from flask_wtf import FlaskForm # Flask_wtfモジュールのFlaskFormクラスをインポート
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

app = Flask(__name__)
app.secret_key = "secret"  # CSRF保護のために必須

class NameForm(FlaskForm): # FlaskFormを継承したNameFormクラスを定義 ⇒ Flaskアプリで使用可能に
    name = StringField("名前", validators=[DataRequired()])
    submit = SubmitField("送信") # 送信ボタンを定義(ラベルは「送信」)

@app.route("/hello", methods=["GET", "POST"])
def hello():
    form = NameForm()
    name = ""
    if form.validate_on_submit():  # POSTかつバリデーション成功時
        name = form.name.data       # 入力された値を取得
    return render_template("hello.html", form=form, name=name)
<form method="POST">
  {{ form.csrf_token }}  <!-- CSRFトークンが自動で出力される -->
  {{ form.name.label }}  <!-- 「名前」というラベル -->
  {{ form.name() }}      <!-- 入力欄(自動で name="name" になる) -->
  {{ form.submit() }}    <!-- 送信ボタン -->
</form>

{% if name %}
<p>こんにちは、{{ name }}さん!</p>
{% endif %}
改善点解説
form.validate_on_submit()「POSTかつバリデーション成功時」の判定を1行で書ける
form.name.data入力された値が自動で格納される
form.name()HTMLの入力タグが自動生成される(name属性なども含む)
form.csrf_tokenセキュリティ対策(CSRF)が自動挿入される
form.submit()HTMLの送信ボタンも自動で表示される
  • フォーム定義もHTMLの表示も部品化されてスッキリ
  • セキュリティも安心
  • エラー処理や再表示にも強い

まとめ|Flask-WTFでフォーム開発をシンプル&安全に

Flask-WTFは、これまで学んできたフォーム処理・バリデーション・CSRF対策をすべてまとめて支援してくれる、非常に強力なツールです。

書くコードが減るだけでなく、セキュリティも高まり、保守性の高いアプリが作れるようになります。

「フォームの扱いが大変そう…」と感じていた人も、Flask-WTFを使えばスッキリ書けて、理解しやすくなるはずです。

練習問題:Flask-WTFでCSRF付きのフォームを作ってみよう

【Python】勉強猫がノートパソコンに向かい、練習問題に挑戦する様子。記事内の休憩用イラスト

Flask-WTFを使うことで、Flask上でフォームを簡単かつ安全に扱うことができます。

この問題では、好きなフルーツの名前をユーザーに入力してもらい、それを画面上に表示するWebアプリを作成してください。

フォームにはバリデーション(未入力エラー)をつけ、送信後は「〇〇は美味しそうですね!」という日本語のメッセージを表示するようにします。

テンプレートファイル(HTML)は↓↓のコードを使用し、アプリファイル(Python)を作成してください。

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>フルーツフォーム</title>
</head>
<body>
  <h1>好きなフルーツを教えてください</h1>

  <!-- フォーム開始。POSTメソッドで送信 -->
  <form method="POST">
    {{ form.csrf_token }}  <!-- CSRF対策トークン(Flask-WTFが自動で挿入) -->

    <p>
      {{ form.fruit_name.label }}<br>  <!-- ラベル表示 -->
      {{ form.fruit_name(size=30) }}   <!-- 入力フィールドのHTML表示 -->
    </p>

    <p>
      {{ form.submit() }}  <!-- 送信ボタンのHTML出力 -->
    </p>
  </form>

  <!-- 結果があれば表示 -->
  {% if result %}
    <p><strong>{{ result }}</strong></p>
  {% endif %}
</body>
</html>

この問題の要件

以下の要件に従ってコードを完成させてください。

以下の条件をすべて満たすPythonファイル(app.py)を作成してください。

  • Flask-WTFのフォームクラスを FruitForm という名前で作成すること。
  • フォームには以下の2つの項目を含めること:
    • fruit_name:入力必須のテキスト入力欄
    • submit:送信ボタン
  • CSRFトークン保護を設定すること。
  • フォームの送信が成功した場合、入力されたフルーツ名に対して「〇〇は美味しそうですね!」というメッセージを作成し、テンプレートへ渡すこと。
  • form.validate_on_submit() を使ってバリデーションとPOST送信チェックを行うこと。

この問題を解くヒント

1からコードを組み立てることが難しい場合は、以下のヒントを開いて参考にしましょう。

Q
ヒント:コードの構成を見る

正解のコードは上から順に以下のような構成となっています。

1:必要なライブラリをすべてインポート
→ Flask本体のほか、Flask-WTFとWTForms関連の部品も読み込みます。

2:Flaskアプリの本体を作成し、CSRF対策のための「secret_key」を設定
→ フォームを安全に使うために必要な準備です。

3:「FruitForm」という名前のフォームクラスを定義
→ この中で、テキスト入力欄(フルーツ名)と送信ボタンを定義します。
→ 入力は必須にしたいため、バリデーションの指定も行います。

4:「/fruit」というURLにアクセスしたときにフォームを表示する関数を定義
→ この関数では、フォームのオブジェクトを作り、テンプレートに渡す準備をします。

5:フォームが送信されたかどうか、そして入力内容が正しいかをチェック
→ 条件を満たす場合は、入力されたフルーツ名を取り出し、表示用のメッセージを作ります。

6:フォームオブジェクトとメッセージをテンプレートに渡して表示
→ 表示のために render_template() を使い、テンプレートファイル(fruit.html)に情報を渡します。

7:このPythonファイルが直接実行されたときだけアプリが起動するように設定
→ アプリのエントリーポイント(if name == “main”:)を忘れずに書きましょう。

このヒントを見てもまだ回答を導き出すのが難しいと感じる場合は、先に正解のコードと解説を見て内容を理解するようにしましょう。

正解コード

例えば、以下のようなプログラムが考えられます。

Q
正解コード
from flask import Flask, render_template
from flask_wtf import FlaskForm  # Flask-WTFの基本クラス
from wtforms import StringField, SubmitField  # 入力欄とボタン
from wtforms.validators import DataRequired  # 入力必須のバリデータ

app = Flask(__name__)
app.secret_key = "これはCSRF対策のために必要な秘密鍵です"

# フォームクラスの定義
class FruitForm(FlaskForm):
    # フルーツ名を入力する欄。空欄禁止(DataRequired)バリデーション付き
    fruit_name = StringField("好きなフルーツの名前", validators=[DataRequired()])
    # 送信ボタン
    submit = SubmitField("送信する")

# フォームを表示・処理するルート
@app.route("/fruit", methods=["GET", "POST"])
def fruit():
    form = FruitForm()
    result = None

    # フォームが送信され、バリデーションが成功したときの処理
    if form.validate_on_submit():
        # 入力されたフルーツ名を取り出す
        fruit_name = form.fruit_name.data
        # 結果メッセージを作成
        result = f"{fruit_name}は美味しそうですね!"

    # フォームと結果メッセージをテンプレートに渡す
    return render_template("fruit.html", form=form, result=result)

# このファイルが直接実行されたときだけアプリを起動
if __name__ == "__main__":
    app.run(debug=True)

正解コードの詳細解説

正解コードをブロックごとに分割して解説します。

Q
正解コードの詳細解説

必要なライブラリのインポート

from flask import Flask, render_template
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

ここでは、Flaskアプリを作成するために必要なライブラリを読み込んでいます。

  • DataRequired:入力必須(空欄禁止)のバリデーションルール。
  • Flask:Webアプリ本体を作るためのクラス。
  • render_template:テンプレート(HTML)を表示するための関数。
  • FlaskForm:Flask-WTFでフォームを使うときに必要な基本クラス。
  • StringField:テキスト入力用のフォーム部品。
  • SubmitField:送信ボタンの部品。

Flaskアプリ本体の作成とセキュリティ設定

app = Flask(__name__)
app.secret_key = "これはCSRF対策のために必要な秘密鍵です"
  • app = Flask(__name__):Flaskアプリケーションの本体を作り、変数 app に保存。
  • secret_key:CSRF対策に必要な暗号鍵。これがあることで不正な送信を防ぐことができる。

フォームのクラスを作成

class FruitForm(FlaskForm):
    fruit_name = StringField("好きなフルーツの名前", validators=[DataRequired()])
    submit = SubmitField("送信する")

Flask-WTFで使うフォームを定義しています。

  • submit:送信ボタン。ラベルは「送信する」。
  • FruitForm:フォームの名前(自由に決められるがわかりやすく)。
  • fruit_name:ユーザーが入力する「好きなフルーツ名」の欄。ラベルは「好きなフルーツの名前」。
  • validators=[DataRequired()]:入力を必須にする設定。未入力のまま送信しようとするとエラーになる。

ルーティングとフォーム処理

@app.route("/fruit", methods=["GET", "POST"])
def fruit():
    form = FruitForm()
    result = None

    if form.validate_on_submit():
        fruit_name = form.fruit_name.data
        result = f"{fruit_name}は美味しそうですね!"

    return render_template("fruit.html", form=form, result=result)

この部分では、/fruit にアクセスされたときの動きを定義しています。

  • render_template():HTMLファイルを表示し、その中にフォームと結果を渡す。
  • @app.route("/fruit", methods=["GET", "POST"]):フォームを表示&送信処理をするURLを設定。
  • form = FruitForm():作成したフォームクラスを使ってフォームのインスタンスを作成。
  • form.validate_on_submit():ページが送信された(POST)ことと「空欄禁止」を満たしていることを同時にチェック
  • form.fruit_name.data:ユーザーが入力したフルーツ名を取得。
  • result:表示用のメッセージを作成。

Flaskアプリの起動設定

if __name__ == "__main__":
    app.run(debug=True)
  • if __name__ == "__main__"::このファイルが直接実行されたときだけ、下の処理を実行する。
  • app.run(debug=True):アプリを起動。debug=True にすると、コードを変更したときに自動で再起動してくれる。

<<前のページ

Flaskの記事一覧

次のページ>>

FAQ|Flask-WTFの導入と活用に関するよくある質問

Q
Q1. Flask-WTFの導入時に「secret_key」は必ず必要ですか?

はい、Flask-WTFではCSRF保護機能が標準で有効になっており、そのトークンの生成に「secret_key」が必須です。これが設定されていないと、フォームは正しく動作しません。

Q
Q2. Flask-WTFのフォームクラスは複数作っても問題ありませんか?

問題ありません。用途ごとに異なるフォームクラスを定義することで、アプリの構造が整理され、保守性も高まります。各クラスはFlaskFormを継承して独立して動作します。

Q
Q3. Flask-WTFを使うとフォームのバリデーションエラーは自動で表示されますか?

いいえ、自動では表示されません。テンプレート側で {{ form.field.errors }} を使って、必要な位置にエラーメッセージを表示させる必要があります。

質問用コンタクトフォーム

この記事を書くにあたりAIを活用しています。

人間の目による確認も行っていますが、もし間違い等ありましたらご指摘頂けると大変助かります。

記事URLをコピーしました