【Flask】WTFormsのフォームバリデーションの基本と実践|Chapter4-4

一つ前のページではWTFormsの基本について学習しました。
今回は フォームのバリデーション について見ていきましょう。
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:エラーハンドリングとデバッグ編
Chapter7:アプリ開発編
Webアプリケーションでは、ユーザーが入力フォームにさまざまなデータを入力します。
しかし、その入力内容が正しくなかったらどうなるでしょうか?
たとえば名前欄に数値が書かれたたり、年齢が空欄だったり、メールアドレスが不正だったり――。
そんなときに活躍するのが バリデーション(validation)です。
バリデーションとは、ユーザーが送信したデータが正しいかどうかをチェックする仕組みのことです。
この記事では、Pythonのフォームライブラリ「WTForms」が提供する標準のバリデーション機能について解説します。
紹介する5つのコードファイルを通じて、どのようにバリデーションを組み込むのかを体験してください。
- Flask開発を Stream Deck でボタン化しよう!
-
Flaskは非常に軽量かつシンプルなフレームワークですが、それゆえに定型作業が多く、開発は単調な作業の連続になりがちです。
それこそがFlaskのメリットであり、習得難易度が低い理由でもありますが、単調な作業は退屈で、ミスも起こりやすいでしょう。
そこで役に立つのが Stream Deck 。
このような定型手順が多い作業を “ボタン化” することで視覚化。
圧倒的に 効率的 かつ ストレスフリー な開発環境が簡単に手に入ります↓↓
あわせて読みたいFlask開発をStreamDeckでボタン化しようあわせて読みたいプログラマー向けStream Deckの選び方|初心者でも失敗しないモデル比較ガイド
フォームバリデーションとは?使い方と主要バリデータ一覧

フォームバリデーションとは何か|正しい入力を保証する仕組み
バリデーション(validation)は、英語で「妥当性確認」という意味です。
Web開発においては、ユーザーの入力データに間違いがないかを確認する処理のことを指します。
たとえば以下のようなルールを定義できます:
- 名前は必須入力
- 年齢は18歳以上、99歳以下
- パスワードは10文字以内で、確認用パスワードと一致
- メールアドレスは正しい形式であること
バリデーションは、入力された値が 正しい形式・条件に沿っているか をチェックする仕組みです。
WTFormsでは、各入力フィールドに対して「このフィールドは必須」「この値は〇歳以上であるべき」「この形式はメールアドレスでなければならない」など、さまざまな条件を指定できます。
これらの条件は、バリデータ と呼ばれる関数の形で提供されており、各フィールドの validators引数 にリストで渡します。
- バリデータ:入力条件を示す関数
- validators引数:フィールドに付加するバリデータ
- フィールド:フィールドクラスのオブジェクト
基本的な構文は以下のようになります。
from wtforms.validators import バリデータ名(関数名) # 必要なバリデータ関数をインポート
フィールド = フィールドクラス('ラベル', validators=[バリデータ名]) # インスタンス生成
# 例:name = StringField('名前', validators=[バリデータ1, バリデータ2, ...])例のように複数のバリデータを同時に指定することもできます。
バリデータの使い方|DataRequiredで必須入力化する例
DataRequired は「入力が空ではいけない」という条件を付けるためのバリデータです。フォームに必ず入力して欲しいときに使います。
ここでは文字列を入力するフィールドクラス「StringField」を用いて、基本構文を確認しましょう。
名前を入力するフォームに使用する場合は以下のようになります。
from wtforms.validators import DataRequired # DataRequired関数をインポート
name= StringField('名前:', validators=[DataRequired('名前は入力必須です')])
# フィールドが未入力または空白で送信された場合、「名前は入力必須です」とエラー表示されるこのように、validatorsにDataRequired()を指定することで、値が空だった場合に指定したメッセージを表示させることができます。
また、バリデーションから少し話がズレますが、ここに render_kw引数 を追記してフィールドに記入例が表示されるようにしてみましょう。
# フィールド = フィールドクラス('ラベル', 引数1, 引数2, ...)
name = StringField('名前: ',
validators=[DataRequired('名前は入力必須です')], # validatoers引数
render_kw={"placeholder": "(例)フラスク太郎"}) # render_kw引数- render_kw引数:フィールドのHTMLに属性を追加(辞書形式で書く)
- placeholder属性:サンプル入力例を示す属性
ただし、Pythonファイル側だけに記述しても上手く動きません。テンプレート側にも、バリデーションエラー発生時の対応を書く必要があります。
<div>
{% if form.errors %} <!-- もしエラーがあるなら -->
<ul>
=== エラーメッセージ ===
{% for field, errors in form.errors.items() %}
<li>{{ field }}:{{ errors }}</li>
{% endfor %}
</ul>
{% endif %} <!-- if文はここまで -->
</div>{% if form.errors %}
フォームの送信時に、エラーが1つでも存在する場合にのみ以下の処理を実行します。<ul>タグ
HTMLのリスト要素です。エラーをリスト形式で表示するために使用します。{% for field, errors in form.errors.items() %}
すべてのフォームフィールドに対して、関連するエラーを1つずつ取り出して処理しています。field:エラーが発生したフィールド名errors:そのフィールドに紐づくエラー内容(通常はリスト)
<li>{{ field }}:{{ errors }}
実際に画面に表示される部分です。
例えば、nameフィールドに"このフィールドは必須です"というエラーがある場合、以下のように出力されます:username:['このフィールドは必須です']
このようにすることで、未入力で送信ボタンを押すと「名前は入力必須です」というエラーが表示されます。
実際に上記のコードをコピーしてVSCodeに貼り付け、フォームをブラウザ表示してみましょう。
初心者が知っておきたい主要バリデータまとめ
以下はWTFormsでよく使われるバリデータの一覧です。
前のチャプターのフィールドクラスと同様に、これらを全て覚える必要は全くありません。
どのような種類のバリデータがあるかだけを覚えておき、必要に応じて調べて使う習慣を身につけましょう。
| バリデータ名 | 説明 | 使用例 |
|---|---|---|
DataRequired | 入力が空でないかをチェック | validators=[DataRequired('必須項目です')] |
Length(min, max) | 入力の長さ(文字数)を制限 | validators=[Length(4, 10, '4文字以上10文字以下')] |
Email | メールアドレス形式をチェック | validators=[Email('正しいメールアドレスを入力してください')] |
NumberRange(min, max) | 数値の範囲を制限 | validators=[NumberRange(18, 100, '18歳〜100歳で入力')] |
EqualTo('field') | 他のフィールドと一致しているか | validators=[EqualTo('confirm_password', 'パスワードが一致しません')] |
なお、初心者が知っておくべき組み込みバリデータは、今回紹介したものも含めて全て↓↓の用語集に記載してありますので参考にしてください。

サンプルコードで理解するバリデーション付きフォームの作り方
実際のコードを使って、WTFormsを使ったアプリケーションがどのように動くかを見ていきましょう。
これから紹介する5つのコードをコピーして、以下の階層になるよう保存してください。
保存フォルダ
├── chapter4-4.py
├── forms.py
└── templates/(フォルダ)
├── base.html
├── enter.html
└── result.htmlこれからこれらのコードをベースとして、バリデーションを追加していく作業を行います。
なお、これらのベースコードはChapter4-3で作成したコードと全く同じですので、続けて学習している方はコピーして使用可能です。
- chapter4-4.py(アプリケーションファイル)
-
from flask import Flask, render_template, request app = Flask(__name__) # --- ルーティング --- from forms import UserInfoForm # forms.pyのUserInfoFormクラスをインポート # ユーザー情報入力 @app.route('/', methods=['GET','POST']) # このURLにGETまたはPOSTリクエストが来たときに実行 def show_Enter(): # フォームの作成 form = UserInfoForm(request.form) # インスタンスを生成 if request.method == 'POST': # もしリクエストがPOSTなら(フォームが送信されたら) return render_template('result.html',form=form) # formインスタンスをresult.htmlに渡して表示 return render_template('enter.html', form=form) # formインスタンスをenter.htmlに渡して表示 # request.formはフォームデータを格納する辞書オブジェクト # これを引数としてUserInfoFormに渡すことで、送信された値がフィールドに反映される # --- 実行 --- if __name__ == '__main__': app.run()
- forms.py(フォームの定義ファイル)
-
from wtforms import Form # wtformsモジュールのForm基底クラスをインポート from wtforms.fields import( # wtforms.fieldsの各種クラスをインポート StringField, IntegerField, PasswordField, DateField, RadioField, SelectField, BooleanField, TextAreaField, EmailField, SubmitField ) # ユーザー情報クラス class UserInfoForm(Form): # Formクラスを継承したUserInfoFormクラスの定義 # 名前:文字列入力 name = StringField('名前: ', render_kw={"placeholder": "(例)フラスク太郎"}) # 年齢:整数値入力 age = IntegerField('年齢: ', default=20) # パスワード:パスワード入力 password = PasswordField('パスワード: ') # 確認用:パスワード入力 confirm_password = PasswordField('パスワード確認: ') # Email:メールアドレス入力 email = EmailField('メールアドレス: ') # 生年月日:日付入力 birthday = DateField('生年月日: ', format="%Y-%m-%d", render_kw={"placeholder": "yyyy/mm/dd"}) # 性別 gender = RadioField('性別: ', choices=[('man', '男性'), ('woman', '女性')], default='man') # 出身地域:セレクトボックス area = SelectField('出身地域: ',choices=[('east', '東日本'), ('west', '西日本')]) # 既婚:真偽値入力 is_married = BooleanField('既婚?: ') # メッセージ:複数行テキスト note = TextAreaField('備考: ') # ボタン submit = SubmitField('送信')
- base.html(ベーステンプレート)
-
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>WTForms</title> </head> <body> {% block title %} タイトル {% endblock %} <hr /> {% block content %} 内容 {% endblock %} </body> </html>
- enter.html(入力テンプレート)
-
{% extends "base.html" %} {% block title %} <h1>WTForm:入力</h1> {% endblock %} {% block content %} <form method="POST"> {# {{ form.フィールド.label }} {{ form.フィールド(引数) }} #} {{ form.name.label }} {{ form.name(size=20) }}<br> {{ form.age.label }} {{ form.age() }}<br> {{ form.password.label }} {{ form.password(size=20) }}<br> {{ form.confirm_password.label }} {{ form.confirm_password(size=20) }}<br> {{ form.email.label }} {{ form.email(placeholder="xxxx@example.com" ) }}<br> {{ form.birthday.label }} {{ form.birthday() }}<br> {{ form.gender.label }} {{ form.gender() }}<br> {{ form.area.label }} {{ form.area() }}<br> {{ form.is_married.label }} {{ form.is_married() }}<br> {{ form.note.label }} {{ form.note(style="height:100px; width:150px") }}<br> {{ form.submit() }} </form> {% endblock %}
- result.html(結果テンプレート)
-
{% extends "base.html" %} {% block title %} <h1>WTForm:結果</h1> {% endblock %} {% block content %} <ul> <li>名前:{{form.name.data}}</li> <li>年齢:{{form.age.data}}</li> <li>パスワード:{{form.password.data}}</li> <li>Email:{{form.email.data}}</li> <li>生年月日:{{form.birthday.data}}</li> <li>性別:{{form.gender.data}}</li> <li>地域:{{form.area.data}}</li> <li>既婚:{{form.is_married.data}}</li> <li>備考:{{form.note.data}}</li> </ul> {% endblock %}
このコードを実行すると以下のようなフォームが表示されます。これにバリデーションを追加していきましょう。

アプリケーションファイルの書き方
バリデーションは、 form.validate()メソッド を呼び出して実行します。
フォーム送信後に実行されるようにするため、コードの12行目「if request.method == ‘POST’」の後ろにこのメソッドを付け足しましょう。
if request.method == 'POST' and form.validate(): # フォームが送信され、form.validate()がTrueなら
フォームの定義ファイルの書き方
全てのフィールドにも同様にバリデータを追加しましょう。
以下は修正後のコード全体です。
from wtforms import Form # wtformsモジュールのForm基底クラスをインポート
from wtforms.fields import( # 各種フィールドクラスをインポート
StringField, IntegerField, PasswordField, DateField,
RadioField, SelectField, BooleanField, TextAreaField,
EmailField, SubmitField
)
from wtforms.validators import( # 各種バリデータをインポート
DataRequired, Length, Email, NumberRange, EqualTo
)
# ユーザー情報クラス
class UserInfoForm(Form): # Formクラスを継承したUserInfoFormクラスの定義
# 名前:文字列入力
name = StringField('名前: ',
validators=[DataRequired('名前は入力必須です')],
render_kw={"placeholder": "(例)フラスク太郎"}
)
# 年齢:整数値入力
age = IntegerField('年齢: ',
validators=[NumberRange(18, 100, '入力範囲は18歳から100歳です')],
default=20
)
# パスワード:パスワード入力
password = PasswordField('パスワード: ',
validators=[Length(1, 10, 'パスワードの長さは1文字以上10文字以内です'),
EqualTo('confirm_password', 'パスワードが一致しません')
]
)
# 確認用:パスワード入力
confirm_password = PasswordField('パスワード確認: ')
# Email:メールアドレス入力
email = EmailField('メールアドレス: ',
# validators=[Email('メールアドレスのフォーマットではありません')]
)
# 生年月日:日付入力
birthday = DateField('生年月日: ',
validators=[DataRequired('生年月日の入力は必須です')],
format="%Y-%m-%d",
render_kw={"placeholder": "yyyy/mm/dd"}
)
# 性別
gender = RadioField('性別: ', choices=[('man', '男性'), ('woman', '女性')], default='man')
# 出身地域:セレクトボックス
area = SelectField('出身地域: ',choices=[('east', '東日本'), ('west', '西日本')])
# 既婚:真偽値入力
is_married = BooleanField('既婚?: ')
# メッセージ:複数行テキスト
note = TextAreaField('備考: ')
# ボタン
submit = SubmitField('送信')入力テンプレートの書き方
バリデーションエラー発生時にそれを表示するため、入力テンプレートにも追記しましょう。
{% block content %}の中に以下のように書き足してください。
{% block content %}
<div style="color: red">
{% if form.errors %} <!-- もし入力エラーがあるなら -->
<ul>
=== エラーメッセージ ===
{% for field, errors in form.errors.items() %}
<li>{{ field }}:{{ errors }}</li>
{% endfor %}
</ul>
{% endif %} <!-- if文はここまで -->
</div>
<form method="POST">コードの修正は以上です。
実際に各フィールドに入力してみて、どのように表示されるか確認してください。
- サイト改善アンケート|1分だけ、ご意見をお聞かせください
-
本サイトでは、みなさまの学習をよりサポートできるサービスを目指しております。
そのため、ご利用者のみなさまの「プログラミングを学習する理由」などをアンケート形式でお伺いしています。ご協力いただけますと幸いです。
アンケート
まとめ|フォームバリデーションで安心・安全なWebアプリを作ろう
この章では、WTFormsにおけるバリデーション処理について学習しました。
DataRequired をはじめとするさまざまな バリデータ を使うことで、ユーザーの入力を安全に、正確に受け取ることができるようになります。
バリデーションを正しく使うことは、フォーム入力を伴うWebアプリケーションの 品質を飛躍的に向上させる 一歩です。
次の章では、さらに応用的な「カスタムバリデータ」の作成に挑戦していきましょう。これまでの知識を活かして、より高度なバリデーションを実装していく準備が整いました!
FAQ|Flaskでのフォームバリデーションに関するよくある質問
- Q1. バリデーションエラーはどのようにユーザーに表示すればいいですか?
-
WTFormsでは各フィールドにエラーメッセージが自動的に格納されるので、テンプレート側で
form.フィールド.errorsを使って表示できます。ループを使って複数のエラーを丁寧に見せると親切です。
- Q2. 複数のバリデータを一つのフィールドに同時に使うことは可能ですか?
-
はい、可能です。例えば
DataRequired()とLength(min=5)を同時に指定することで「入力が必須で、かつ5文字以上であること」といった複合条件を簡単に設定できます。
- Q3. 特定の文字列形式(例:郵便番号や電話番号)をチェックするにはどうすればいいですか?
-
正規表現バリデータ
Regexp()を使うと、特定の文字パターンにマッチするかどうかを検証できます。郵便番号ならRegexp(r'^\d{3}-\d{4}$')のように書くと効果的です。






