Flaskフォームの実践ガイド:forms.pyとWTFormsの活用法

forms.pyとは?

forms.pyは、Flaskでフォームの管理を記述するモジュールです。
Flaskでは、フォームを手動で作成することも可能ですが、ライブラリであるWTFormsを活用することで、より簡単かつ堅牢にフォームを構築できます。


フォームの作成方法

手動でフォームを作成する

以下のコードはHTMLを直接記述してフォームを作成する方法です。

<form action="{{ url_for('app.register') }}" method="POST" enctype="multipart/form-data">
    <label for="name">名前:</label>
    <input type="text" id="name" name="name" placeholder="名前">
    <label for="email">メールアドレス:</label>
    <input type="email" id="email" name="email" placeholder="sample@sample.com">
    <input type="submit" value="送信">
</form>

このように、labelを書いて、inputタグを書いて、、と面倒ですよね。。これをwtformsを使って書いてみます。

WTFormsで会員登録フォーム

forms.py

会員登録フォームをwtformsで書いてみましょう。ポイントは表示させたいフォームをクラスで書くことです。また、クラスメソッドに、バリデーションを書くことができます。

from wtforms.form import Form
from wtforms.fields import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired, Email, EqualTo, ValidationError
from flaskr.models import User

class RegisterForm(Form):
    email = StringField('メール: ', validators=[DataRequired(), Email('メールアドレスが誤っています')])
    username = StringField('名前: ', validators=[DataRequired()])
    password = PasswordField('パスワード: ', validators=[DataRequired(), EqualTo('password_confirm', message='パスワードが一致しません')])
    password_confirm = PasswordField('パスワード確認: ', validators=[DataRequired()])
    submit = SubmitField('登録')

    # 独自バリデーションメソッド
    def validate_email(self, field):
        if User.select_by_email(field.data):
            raise ValidationError('メールアドレスは既に登録されています')

views.py

作成したフォームをviews.pyに書いてみます。RegisterFormはクラスで書いているのでインポートして使えます。formロジックで扱うには、form=RegisterForm()でインスタンスを作成します。さらに、入力されたデータを form.username.data のように取り出します。 ( views.py )

from .models import RegisterForm


@bp.route('/register', methods=['GET', 'POST'])
def register():
    form = RegisterForm(request.form)
    if request.method == 'POST' and form.validate():
        user = User(
            email=form.email.data,
            username=form.username.data,
            password=form.password.data
        )
        user.add_user()
        return redirect(url_for('app.login'))
    return render_template('register.html', form=form)

html

view関数の render_templateで、formを渡しているので、htmlで展開できます。

{% extends "base.html" %}
{% block content %}
<h1>ユーザ情報登録画面</h1>
<form method="POST">
    {{ form.csrf_token }}
    {{ form.email.label }}{{ form.email() }}
    {{ form.username.label }}{{ form.username() }}
    {{ form.password.label }}{{ form.password() }}
    {{ form.password_confirm.label }}{{ form.password_confirm() }}
    {{ form.submit() }}
</form>
{% endblock %}

WTFormsでログインフォーム

ログインについてもwtformsで書けます。

forms.py

class LoginForm(Form):
    email = StringField('メール: ', validators=[DataRequired(), Email()])
    password = PasswordField('パスワード: ', validators=[DataRequired()])
    submit = SubmitField('ログイン')

views.pyでのログイン処理

@bp.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm(request.form)
    if request.method == 'POST' and form.validate():
        user = User.select_by_email(form.email.data)
        if user and user.validate_password(form.password.data):
            login_user(user, remember=True)
            next = request.args.get('next') or url_for('app.welcome')
            return redirect(next)
    return render_template('login.html', form=form)

html

{% from "_formhelpers.html" import render_field %}
{% extends "base.html" %}
{% block content %}
{% for message in get_flashed_messages() %} {# リダイレクトした際のメッセージ#}
{{ message }}
{% endfor %}
<form method="POST">
    {{form.csrf_token}}
    {{render_field(form.email)}}
    {{render_field(form.password)}}
    {{form.submit()}}
</form>
{% endblock %}

こちらはマクロを使った記述です。
マクロについてはこちらをご覧ください

Flask開発におけるtemplatesフォルダの役割とJinja2テンプレートエンジンの活用方法

templatesフォルダの役割 Flaskでは、テンプレートファイルを格納する…
benelop.jp

その他の入力フォーム

会員登録、ログイン以外にも、ブラウザでformで表示するものがさまざまあります。
例えば、ラジオボタン、チェックボックス、セレクトがあります。これらをflaskで扱ってみます。

name=の部分を読み取って

view関数で

papersize = request.form[‘papersize’] のようにして、そこに入力された値を格納します。実際には、valueの値が格納されています。

セレクトボタン

form

{% extends "base.html" %}
{% block content %}

<form action="{{ url_for('select') }}" method="POST">
      <label for="fruite">好きな果物</label>
      <select name="selectbutton">
        <option value="">選択してください</option>
        <option value="banana">バナナ🍌</option>
        <option value="apple">リンゴ🍎</option>
        <option value="grape">ぶどう🍇</option>
        <option value="orange">オレンジ🍊</option>
      </select>
    <input type="submit" value="送信">
</form> 

メッセージ:{{ message }}

{% endblock %}

views

@app.route('/select', methods=["GET","POST"])
def select():
    if request.method == "POST":
        value = request.form["selectbutton"]
        return render_template("selectform.html", message=value)
    return render_template("selectform.html")

ラジオボタン

form

{% extends "base.html" %}
{% block content %}

<form action="{{ url_for('radio') }}" method="POST">
    今日の感想<br>

    <input type="radio" name="radiobutton" value="good">good<br>
    <input type="radio" name="radiobutton" value="bad">bad
    <br>
    <input type="submit" value="送信">
</form>

メッセージ:{{ message }}

{% endblock %}

views

@app.route('/radio', methods=["GET","POST"])
def radio():
    if request.method == "POST":
        value = request.form["radiobutton"]
        message = f"今日の感想:{value}でした"
        return render_template("radioform.html", message=message)
    return render_template("radioform.html")

チェックボックス

form

{% extends "base.html" %}
{% block content %}


<form action="{{ url_for('checkbox') }}" method="POST">
    個人情報保護に同意した
    <input type="checkbox" name="accept" value="1">
    <input type="hidden" name="accept" value="0">
    <input type="submit" value="送信">
</form>

{% if message %}
    <p>メッセージ:{{ message }}</p>
{% endif %}


{% endblock %}

views

@app.route('/checkbox', methods=["GET","POST"])
def checkbox():
    if request.method == "POST":        
        value = request.form.get("accept")
        print(value)
        if value == "1":
            message = "同意しました"
        else:
            message = "同意してません"
        return render_template("checkboxform.html", message=message) 
    return render_template("checkboxform.html")

まとめ

WTFormsを活用することで、Flaskでのフォーム管理が簡素化され、堅牢なアプリケーションを構築できます。ぜひこのガイドを参考に、効率的なフォーム作成を実践してみてください!

( 引用 https://tanuhack.com/flask-client2server/ )

Flaskアプリ開発の全てがここに!アプリ構成の全てはこちらをご覧ください!

Flask入門: Webアプリケーション開発の基礎ガイド

Webアプリケーションとは Webアプリケーションの定義 Webアプリケーション…
benelop.jp