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円)ですが、現在は期間限定で無料公開中です。

<<前のページ

Flaskの記事一覧

次のページ>>

フォームバリデーションとは?正しい入力を保証する仕組みを学ぼう

バリデーション(validation)は、英語で「妥当性確認」という意味です。

Web開発においては、ユーザーの入力データに間違いがないかを確認する処理のことを指します。

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

たとえば以下のようなルールを定義できます:

  • 名前は必須入力
  • 年齢は18歳以上、99歳以下
  • パスワードは10文字以内で、確認用パスワードと一致
  • メールアドレスは正しい形式であること

バリデーションは、入力された値が 正しい形式・条件に沿っているか をチェックする仕組みです。

WTFormsでは、各入力フィールドに対して「このフィールドは必須」「この値は〇歳以上であるべき」「この形式はメールアドレスでなければならない」など、さまざまな条件を指定できます。

これらの条件は、バリデータ と呼ばれる関数の形で提供されており、各フィールドの validators引数 にリストで渡します。

基本的な構文は以下のようになります。

from wtforms.validators import バリデータ名

フィールド = StringField('ラベル', validators=[バリデータ1, バリデータ2, ...])

複数のバリデータを同時に指定することもできます。

DataRequiredで必須入力をチェックしよう|基本的なバリデータの使い方

DataRequired は入力が空ではいけないという条件を付けるためのバリデータです。フォームの必須項目に使います。

基本構文は以下の通りです。

from wtforms.validators import DataRequired # DataRequired関数をインポート

フィールド = StringField('名前:', validators=[DataRequired('名前は入力必須です')])
# フィールドが未入力または空白で送信された場合、「名前は入力必須です」とエラー表示される

このように、validatorsDataRequired()を指定することで、値が空だった場合に指定したメッセージを表示させることができます。

名前を入力するフォームに使用する場合は以下のようになります。

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>
  1. {% if form.errors %}
    フォームの送信時に、エラーが1つでも存在する場合にのみ以下の処理を実行します。
  2. <ul>タグ
    HTMLのリスト要素です。エラーをリスト形式で表示するために使用します。
  3. {% for field, errors in form.errors.items() %}
    すべてのフォームフィールドに対して、関連するエラーを1つずつ取り出して処理しています。
    • field:エラーが発生したフィールド名
    • errors:そのフィールドに紐づくエラー内容(通常はリスト)
  4. <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で作成したコードと全く同じですので、続けて学習している方はコピーして使用可能です。

Q
アプリケーションファイル
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()
Q
フォームの定義ファイル
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('送信')
Q
ベーステンプレート
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>WTForms</title>
</head>
<body>
    {% block title %} タイトル {% endblock %}
    <hr />
    {% block content %} 内容 {% endblock %}
</body>
</html>
Q
入力テンプレート
{% 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 %}
Q
結果テンプレート
{% 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アプリケーションの 品質を飛躍的に向上させる 一歩です。

次の章では、さらに応用的な「カスタムバリデータ」の作成に挑戦していきましょう。これまでの知識を活かして、より高度なバリデーションを実装していく準備が整いました!

<<前のページ

Flaskの記事一覧

次のページ>>

FAQ|Flaskでのフォームバリデーションに関するよくある質問

Q
Q1. バリデーションエラーはどのようにユーザーに表示すればいいですか?

WTFormsでは各フィールドにエラーメッセージが自動的に格納されるので、テンプレート側でform.フィールド.errorsを使って表示できます。ループを使って複数のエラーを丁寧に見せると親切です。

Q
Q2. 複数のバリデータを一つのフィールドに同時に使うことは可能ですか?

はい、可能です。例えばDataRequired()Length(min=5)を同時に指定することで「入力が必須で、かつ5文字以上であること」といった複合条件を簡単に設定できます。

Q
Q3. 特定の文字列形式(例:郵便番号や電話番号)をチェックするにはどうすればいいですか?

正規表現バリデータRegexp()を使うと、特定の文字パターンにマッチするかどうかを検証できます。郵便番号ならRegexp(r'^\d{3}-\d{4}$')のように書くと効果的です。

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

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

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

記事URLをコピーしました