Flask入門|フォームバリデーションの基本と実践【チャプター4-04】

一つ前のページでは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:Flaskの便利機能編
Chapter7:アプリ開発編
Webアプリケーションでは、ユーザーが入力フォームにさまざまなデータを入力します。
しかし、その入力内容が正しくなかったらどうなるでしょうか?
たとえば名前が空欄だったり、年齢に文字が入っていたり、メールアドレスが不正だったり――。
そんなときに活躍するのが バリデーション(validation)です。
バリデーションとは、ユーザーが送信したデータが正しいかどうかをチェックする仕組みのことです。
この記事では、Pythonのフォームライブラリ「WTForms」が提供する標準のバリデーション機能について解説します。
紹介する5つのコードファイルを通じて、どのようにバリデーションを組み込むのかを体験してください。
本記事は 有料記事(100円)ですが、現在は期間限定で無料公開中です。
フォームバリデーションとは?正しい入力を保証する仕組みを学ぼう
バリデーション(validation)は、英語で「妥当性確認」という意味です。
Web開発においては、ユーザーの入力データに間違いがないかを確認する処理のことを指します。
たとえば以下のようなルールを定義できます:
- 名前は必須入力
- 年齢は18歳以上、99歳以下
- パスワードは10文字以内で、確認用パスワードと一致
- メールアドレスは正しい形式であること
バリデーションは、入力された値が 正しい形式・条件に沿っているか をチェックする仕組みです。
WTFormsでは、各入力フィールドに対して「このフィールドは必須」「この値は〇歳以上であるべき」「この形式はメールアドレスでなければならない」など、さまざまな条件を指定できます。
これらの条件は、バリデータ と呼ばれる関数の形で提供されており、各フィールドの
validators
引数 にリストで渡します。
基本的な構文は以下のようになります。
from wtforms.validators import バリデータ名 フィールド = StringField('ラベル', validators=[バリデータ1, バリデータ2, ...])
複数のバリデータを同時に指定することもできます。
DataRequiredで必須入力をチェックしよう|基本的なバリデータの使い方
DataRequired
は入力が空ではいけないという条件を付けるためのバリデータです。フォームの必須項目に使います。
基本構文は以下の通りです。
from wtforms.validators import DataRequired # DataRequired関数をインポート フィールド = StringField('名前:', validators=[DataRequired('名前は入力必須です')]) # フィールドが未入力または空白で送信された場合、「名前は入力必須です」とエラー表示される
このように、validators
にDataRequired()
を指定することで、値が空だった場合に指定したメッセージを表示させることができます。
名前を入力するフォームに使用する場合は以下のようになります。
name = StringField('名前: ', validators=[DataRequired('名前は入力必須です')], render_kw={"placeholder": "(例)フラスク太郎"})
ただし、Pythonファイル側だけに記述しても上手く動きません。テンプレート側にも、バリデーションエラー発生時の対応を書く必要があります。
<div> {% if form.errors %} <ul> === エラーメッセージ === {% for field, errors in form.errors.items() %} <li>{{ field }}:{{ errors }}</li> {% endfor %} </ul> {% endif %} </div>
{% if form.errors %}
フォームの送信時に、エラーが1つでも存在する場合にのみ以下の処理を実行します。<ul>
タグ
HTMLのリスト要素です。エラーをリスト形式で表示するために使用します。{% for field, errors in form.errors.items() %}
すべてのフォームフィールドに対して、関連するエラーを1つずつ取り出して処理しています。field
:エラーが発生したフィールド名errors
:そのフィールドに紐づくエラー内容(通常はリスト)
<li>{{ field }}:{{ errors }}
実際に画面に表示される部分です。
例えば、name
フィールドに"このフィールドは必須です"
というエラーがある場合、以下のように出力されます:username:['このフィールドは必須です']
このようにすることで、未入力で送信ボタンを押すと「名前は入力必須です」というエラーが表示されます。
初心者が知っておきたい主要バリデータまとめ
以下は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で作成したコードと全く同じですので、続けて学習している方はコピーして使用可能です。
- アプリケーションファイル
-
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()
- フォームの定義ファイル
-
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('送信')
- ベーステンプレート
-
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>WTForms</title> </head> <body> {% block title %} タイトル {% endblock %} <hr /> {% block content %} 内容 {% endblock %} </body> </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 %}
- 結果テンプレート
-
{% 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 %}
前のChaterを見ずにこのページに来た人は、まずはこの5つのコードを実行して挙動を確認しましょう。
アプリケーションファイル
バリデーションを使うには、フォーム送信後に.validate()
メソッドを呼び出してチェックする必要があります。
「if request.method == ‘POST’」の後ろに付けたしましょう。
if request.method == 'POST' and form.validate(): # フォームが送信され、form.validate()がTrueなら
フォームの定義ファイル
全てのフィールドにも同様にバリデータを追加しましょう。
以下は修正後のコード全体です。
from wtforms import Form # wtformsモジュールのForm基底クラスをインポート from wtforms.fields import( # wtforms.fieldsの各種クラスをインポート StringField, IntegerField, PasswordField, DateField, RadioField, SelectField, BooleanField, TextAreaField, EmailField, SubmitField ) # 使用するvalidatorをインポート from wtforms.validators import( DataRequired, EqualTo, Length, NumberRange, Email ) # ユーザー情報クラス 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 %} </div> <form method="POST">
コードの修正は以上です。
実際に各フィールドに入力してみて、どのように表示されるか確認してください。
フォームバリデーションの基本を押さえて安心・安全なWebアプリを作ろう
この章では、WTFormsにおけるバリデーション処理について学習しました。
DataRequired をはじめとするさまざまな バリデータ を使うことで、ユーザーの入力を安全に、正確に受け取ることができるようになります。
バリデーションを正しく使うことは、フォーム入力を伴うWebアプリケーションの 品質を飛躍的に向上させる 一歩です。
次の章では、さらに応用的な「カスタムバリデータ」の作成に挑戦していきましょう。これまでの知識を活かして、より高度なバリデーションを実装していく準備が整いました!
FAQ|Flaskでのフォームバリデーションに関するよくある質問
- Q1. バリデーションエラーはどのようにユーザーに表示すればいいですか?
-
WTFormsでは各フィールドにエラーメッセージが自動的に格納されるので、テンプレート側で
form.フィールド.errors
を使って表示できます。ループを使って複数のエラーを丁寧に見せると親切です。
- Q2. 複数のバリデータを一つのフィールドに同時に使うことは可能ですか?
-
はい、可能です。例えば
DataRequired()
とLength(min=5)
を同時に指定することで「入力が必須で、かつ5文字以上であること」といった複合条件を簡単に設定できます。
- Q3. 特定の文字列形式(例:郵便番号や電話番号)をチェックするにはどうすればいいですか?
-
正規表現バリデータ
Regexp()
を使うと、特定の文字パターンにマッチするかどうかを検証できます。郵便番号ならRegexp(r'^\d{3}-\d{4}$')
のように書くと効果的です。
質問用コンタクトフォーム
この記事を書くにあたりAIを活用しています。
人間の目による確認も行っていますが、もし間違い等ありましたらご指摘頂けると大変助かります。