【Flask】WTFormsの基本構文と使い方|Chapter4-3

一つ前のページではフォームの基本について学習しました。
今回は 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アプリケーションでユーザーから情報を入力してもらう場面はよくあります。
たとえば名前や年齢、メールアドレスを入力してもらったり、アンケートのようなフォームを作ったりすることもあるでしょう。
Flaskではこのようなフォームを手軽に扱えるようにするためのライブラリがいくつか存在します。
その中でも、今回紹介する「WTForms(ダブルティーフォームズ)」はFlaskと非常に相性がよく、効率的にフォームを作成・管理することができます。
この記事ではWTFormsの基本的な使い方を学び、フォームの作成からテンプレートへの組み込みまでの流れを、具体的なコード例と共に丁寧に解説していきます。
- Flask開発を Stream Deck でボタン化しよう!
-
Flaskは非常に軽量かつシンプルなフレームワークですが、それゆえに定型作業が多く、開発は単調な作業の連続になりがちです。
それこそがFlaskのメリットであり、習得難易度が低い理由でもありますが、単調な作業は退屈で、ミスも起こりやすいでしょう。
そこで役に立つのが Stream Deck 。
このような定型手順が多い作業を “ボタン化” することで視覚化。
圧倒的に 効率的 かつ ストレスフリー な開発環境が簡単に手に入ります↓↓
あわせて読みたいFlask開発をStreamDeckでボタン化しようあわせて読みたいプログラマー向けStream Deckの選び方|初心者でも失敗しないモデル比較ガイド
WTFormsの役割とインストール方法の解説

WTFormsとは?|Pythonでフォームを扱う外部ライブラリ
WTForms は、PythonでWebフォームを扱うための外部ライブラリです。
「WT」は「Web Template」の略とされ、HTMLテンプレートと連携するためのフォーム機能という意味が込められています。
WTFormsはFlaskに標準で組み込まれているわけではありませんが、非常に連携しやすく、「 Flask-WTF 」という統合ラッパーを使えばさらに便利になります(Flask-WTFについてはChapter4-7で詳しく学習します)。
Chapter4-2で紹介したように、Flask単体でもフォームを使うことはできます。しかしWTFormsを使うと次のようなメリットがあります。
- 入力フィールドをPythonのクラスとして定義できる
- 入力チェック(バリデーション)を簡単に行える
- HTMLテンプレートへの組み込みがシンプルになる
WTFormsのインストール方法
WTFormsは外部ライブラリのため、最初にインストールが必要です。
Flaskをインストールしたときと同様に、ターミナル(またはコマンドプロンプト)に以下のコマンドを書き込み、実行しましょう。
pip install wtforms
インストールが完了後は、Pythonコード内でインポートすることで使う準備が整います。
from wtforms import Form from wtforms.fields import StringField, IntegerField # など
WTFormsで使う基本構文と主要フィールド紹介
WTFormsを使うためには、まず Formクラスを継承した自作フォームクラス を作成します。
フィールド(名前、年齢などの入力欄)は、wtforms.fieldsモジュール に入っているクラス を使って定義します。

WTFormsでフォームを定義する方法
基本構文は以下の通りです。
class フォーム名(Form): # クラスとしてフォームを定義
フィールド名 = フィールドクラス('ラベル', オプション引数) # フィールド(入力欄)のインスタンス生成例えば、ブラウザに以下のようなフォームを表示させたいとします。
- 名前と年齢を記入できる
- 名前の記入フィールドには薄く例が書いてある
- 年齢の記入フィールドにはデフォルトで20と記入してある

これは、以下のコードで実現できます。
class UserInfoForm(Form): # UserInfoFormという名のフォームをクラスとして定義
name = StringField('名前: ', render_kw={"placeholder": "(例)フラスク太郎"}) # StringFieldクラスのインスタンス生成
age = IntegerField('年齢: ', default=20) # IntegerFieldクラスのインスタンス生成
#フィールド名 = フィールドクラス('ラベル', オプション引数)nameには「文字列を入力させるためのフィールドクラス:StringField」を使い、ラベルには「名前:」を表示しています。
render_kwはHTMLの属性(placeholderなど)を追加するための辞書です。
詳細はこれから学習しますので、ここではイメージが分かればOKです。
初心者が押さえるべき定番フィールドクラス一覧
代表的なフィールドクラスを一覧で紹介します。
これらを全て覚える必要は全くありません。どのような種類のフォームがあるかだけを覚えておき、必要に応じて調べて使う習慣を身につけましょう。
| フィールドクラス | 説明 |
|---|---|
| StringField | 文字列を入力する欄 |
| IntegerField | 数値を入力する欄 |
| PasswordField | パスワード入力欄 |
| DateField | 日付入力欄 |
| RadioField | 選択肢(ラジオボタン) |
| SelectField | プルダウン形式の選択欄 |
| BooleanField | チェックボックス |
| TextAreaField | 複数行テキスト欄 |
| EmailField | メールアドレス入力欄 |
| SubmitField | 送信ボタン |
なお、初心者が知っておくべきフィールドクラスは、今回紹介したものも含めて全て↓↓の用語集に記載してありますので参考にしてください。

HTMLにフォームを組み込む方法
定義したフォームクラスをHTMLテンプレートで表示する方法を紹介します。

この画像の1行目を表示するには、「名前:」というラベルと入力欄をそれぞれ指定する必要がります。
ラベル表示の基本構文
ラベル表示の基本構文は以下の通りです。
{{ form.フィールド名.label }}今回の場合は「{{ form.name.label }}」と記入することになります。
フィールド表示の基本構文
入力欄表示の基本構文は以下の通りです。
{{ form.フィールド(引数) }}今回の場合は「{{ form.name(size=20) }}」と記入することになります。

実用的な入力フォームのサンプルコードとその解説
実際のコードを使って、WTFormsを使ったアプリケーションがどのように動くかを見ていきましょう。
これから紹介する5つのコードをコピーして、以下の階層になるよう保存してください。
保存フォルダ
├── chapter4-3.py
├── forms.py
└── templates/(フォルダ)
├── base.html
├── enter.html
└── result.html保存後に「chapter4-3.py」を実行すると下図の左側のように表示され、入力欄記入後に送信ボタンを押すと右側のように表示されるはずです。

アプリケーションファイル
from forms import UserInfoForm # forms.pyのUserInfoFormクラスをインポート
from flask import Flask, render_template, request
app = Flask(__name__)
# --- ルーティング ---
# ユーザー情報入力
@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()request.formでフォームからの入力を受け取り、それをフォームクラスに渡します。render_template()でテンプレートへフォームオブジェクトを渡しています。
フォームの定義ファイル
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('送信')このクラスにより、テンプレート側ではform.nameのように呼び出せるようになります。
ベーステンプレート
<!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やresult.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 %}- 各フィールドはラベルと入力欄をセットで表示しています。
- 見やすくするために半角スペースを入れて位置を揃えています。(揃えなくても問題ない)
sizeやplaceholderなどの属性もここで指定可能。
結果テンプレート
{% 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 %}.dataでフォームに入力された値を取得し、表示しています。
{{ form.フィールド名.data }}まとめ
この記事ではWTFormsの基礎を学びました。
フォームをPythonのコードで定義し、HTMLテンプレートと連携させることで、柔軟で拡張性のある入力画面を作成できるようになります。
- WTFormsはPythonコードでフォーム構成を管理できるライブラリ
- 代表的なフィールドには、文字列、数値、パスワード、選択肢など多くの種類がある
- Flaskのテンプレートエンジンと相性が良く、
{{ form.xxx }}の形でHTMLに表示可能
次回は、フォームの入力内容をチェックする「バリデーション」について学んでいきましょう。
ここまでの知識を土台に、さらに実用的なフォーム処理ができるようになります!
- サイト改善アンケート|1分だけ、ご意見をお聞かせください
-
本サイトでは、みなさまの学習をよりサポートできるサービスを目指しております。
そのため、ご利用者のみなさまの「プログラミングを学習する理由」などをアンケート形式でお伺いしています。ご協力いただけますと幸いです。
アンケート
練習問題:WTFormsの基本的なフォームクラスとフィールドを使ってみよう

この問題では、WTFormsを使って「フィードバックフォーム」を作成します。
Pythonファイル(chapter4-3-2.py と forms.py)は既に用意されていますので、それを参考にしながら、必要なHTMLテンプレートファイルを作成してください。
フォームの入力項目には「名前」「満足度(選択式)」「コメント」があり、送信ボタンを押すとその内容が別のページに表示される仕組みを作成します。
フォームの定義にはFormクラスの継承を使い、各フィールドはStringFieldやSelectField、TextAreaFieldなどを用います。
テンプレートファイルは、1つ目に「フォーム入力ページ」、2つ目に「送信結果を表示するページ」の2つを作成してください。
from flask import Flask, render_template, request
from forms import FeedbackForm # フォームクラスをインポート
app = Flask(__name__)
# トップページ:フォームの表示と結果の処理
@app.route("/", methods=["GET", "POST"])
def index():
form = FeedbackForm(request.form) # フォームのインスタンスを作成
if request.method == "POST":
# POSTメソッドの場合は、入力結果をresult.htmlに渡す
return render_template("result.html", form=form)
# 初回アクセス時やGETメソッドの場合は入力フォームを表示
return render_template("index.html", form=form)
if __name__ == "__main__":
app.run(debug=True)from wtforms import Form
from wtforms.fields import StringField, SelectField, TextAreaField, SubmitField
# フィードバックフォームの定義
class FeedbackForm(Form):
# 名前:文字列の入力フィールド
name = StringField('お名前:', render_kw={"placeholder": "例)山田太郎"})
# 満足度:選択式(プルダウン)
satisfaction = SelectField(
'サービスの満足度:',
choices=[('good', '良い'), ('normal', '普通'), ('bad', '悪い')]
)
# コメント:複数行テキスト入力
comment = TextAreaField('ご意見・ご感想:', render_kw={"placeholder": "自由にご記入ください"})
# 送信ボタン
submit = SubmitField('送信する')この問題の要件
以下の要件に従ってコードを完成させてください。
templates/index.htmlを作成し、以下を満たすことform.name.labelとform.name(size=30)を使って名前入力欄を表示form.satisfaction.labelとform.satisfaction()を使って選択式満足度を表示(良い、普通、悪い)form.comment.labelとform.comment(rows=5, cols=40)を使ってコメント欄を表示form.submit()で送信ボタンを表示
templates/result.htmlを作成し、以下を満たすことform.name.data,form.satisfaction.data,form.comment.dataを使って入力結果を表示
ただし、以下のような実行結果となるコードを書くこと。

正解コード
例えば、以下のようなプログラムが考えられます。
- 正解コード
-
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>フィードバックフォーム</title> </head> <body> <h1>フィードバックをお願いします</h1> <form method="POST"> <!-- WTFormsで定義したフィールドのラベルと入力欄を表示 --> {{ form.name.label }} {{ form.name(size=30) }}<br><br> {{ form.satisfaction.label }} {{ form.satisfaction() }}<br><br> {{ form.comment.label }}<br> {{ form.comment(rows=5, cols=40) }}<br><br> {{ form.submit() }} </form> </body> </html><!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>送信結果</title> </head> <body> <h1>送信ありがとうございました!</h1> <ul> <!-- フォームで入力されたデータを表示 --> <li>お名前:{{ form.name.data }}</li> <li>満足度:{{ form.satisfaction.data }}</li> <li>コメント:{{ form.comment.data }}</li> </ul> </body> </html>
正解コードの詳細解説
正解コードをブロックごとに分割して解説します。
- 正解コード(index.html)の詳細解説
-
HTML基本構造
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>フィードバックフォーム</title> </head><!DOCTYPE html>:このファイルがHTML5形式であることを指定しています。<html lang="ja">:HTML文書の言語が日本語であることを示します。<meta charset="UTF-8">:文字コードとしてUTF-8を使う指定。日本語が正しく表示されるようになります。<title>:ブラウザのタブなどに表示されるページのタイトル。
見出しとフォームタグの開始
<body> <h1>フィードバックをお願いします</h1> <form method="POST"><h1>:ページの見出しです。<form method="POST">:フォームのデータを送信する方法を指定します。ここではPOST(データを送信する際によく使う)を使います。
名前フィールドの表示
{{ form.name.label }} {{ form.name(size=30) }}<br><br>{{ form.name.label }}:forms.pyで定義したnameフィールドのラベル(=「お名前:」)を表示します。{{ form.name(size=30) }}:テキスト入力欄を表示します。size=30は入力欄の幅を指定しています。
満足度(プルダウン)フィールドの表示
{{ form.satisfaction.label }} {{ form.satisfaction() }}<br><br>form.satisfaction.label:SelectFieldで定義したラベル(=「サービスの満足度:」)を表示します。form.satisfaction():プルダウンメニュー(選択欄)を表示します。
コメント(複数行入力)フィールドの表示
{{ form.comment.label }}<br> {{ form.comment(rows=5, cols=40) }}<br><br>form.comment.label:コメント入力欄のラベル(=「ご意見・ご感想:」)を表示します。form.comment(rows=5, cols=40):縦5行、横40文字分のサイズでテキストエリアを表示します。
送信ボタンの表示とフォームの終了
{{ form.submit() }} </form> </body> </html>form.submit():送信ボタンを表示します。ボタンのラベルは「送信する」と定義されています。</form>:フォームの終わりです。
- 正解コード(result.html)の詳細解説
-
HTML構造の基本
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>送信結果</title> </head>上記は
index.htmlと同様に、HTMLの文書構造と文字コード、言語を指定する部分です。送信完了メッセージ
<body> <h1>送信ありがとうございました!</h1><h1>:ユーザーがフォームを送信した後に表示される感謝のメッセージです。入力内容の表示
<ul> <li>お名前:{{ form.name.data }}</li> <li>満足度:{{ form.satisfaction.data }}</li> <li>コメント:{{ form.comment.data }}</li> </ul><ul>:リストを作るタグです(順序なしリスト)。{{ form.フィールド名.data }}:各入力欄に入力されたデータを取得して表示します。form.comment.data:コメント欄に入力された内容form.name.data:名前欄に入力された内容form.satisfaction.data:選択した満足度(goodなど)
FAQ|WTFormsの使い方に関するよくある質問
- Q1. WTFormsのフィールドはどのように選べばいいですか?
-
用途に合わせて選ぶのが基本です。たとえば文字入力なら
StringField、数値ならIntegerField、複数選択肢から選ぶならSelectFieldなど、入力内容に合ったフィールドを使いましょう。
- Q2. WTFormsでフォームを定義するクラスはどこに書けばいいですか?
-
一般的には、Flaskプロジェクト内の
forms.pyという別ファイルに定義するのがおすすめです。アプリケーションの見通しがよくなり、コードの管理も楽になります。
- Q3. HTMLにフォームを表示する際、WTFormsでラベルや入力欄にCSSを適用できますか?
-
はい、WTFormsはフォームの各フィールドに属性(
classやstyleなど)を渡すことができるため、テンプレート内で自由に装飾できます。






