FlaskForm を利用したフォームバリデーション

# WTForm,FlaskFormを使ってみたい
*FlaskFormを使うにあたり、あまり記事がなかったので備忘録もかねて記事にします。*
*入力フォームで送られてきたデータを受け取る動きを例にします*

*まず下準備*

```index.py
from flask import Flask
from flask_wtf import FlaskForm
from wtforms import (StringField, PasswordField, BooleanField,
                     RadioField, SelectField,SubmitField, ValidationError)
```
# フィールドの例
 `StringField` : 文字列
 `PasswordField` : パスワード
 `BooleanField` : チェックボックス
 `RadioField` : ラジオボタン
 `SelectField` : セレクトボックス
 `SubmitField` : 送信ボタン
 `ValidationError` : エラー表示

```index.py
class signup_form(FlaskForm):
   """登録フォームに関するvalidate関数"""
    name = StringField(label=('名前'))

    password = PasswordField(label=('パスワード'))

    gender = RadioField(label='性別', choices=[
                        ('1', '男性'), ('2', '女性'), ('0', 'その他')])
    birth = SelectField(label=('誕生年'),
                           choices=[
                                    ('', '選択してください'),
                                    ('2021', '令和３年'),
                                    ('2022', '令和４年')
                                   ])

   shikaku1 = BooleanField(label=('基本情報技術者試験（FP)'),
                            false_values=(False, 'false', 0, '0'))
   shikaku2 = BooleanField(label=('応用情報技術者試験（AP)'),
                            false_values=(False, 'false', 0, '0'))

   submit = SubmitField(label=('登録'))
    # とりあえず使ってない、　htmlのsubumitでも動く

   def validate_name(self, name):
        """ name を検証 """
        if name.data == "":
            # if 〇〇の　エラーコードを出す
            raise ValidationError('入力してください')
        if "/" in name.data:
            raise ValidationError('名前に「/」は含められません')
        if len(name.data) > 20:
            raise ValidationError('文字数が多すぎます')

    def validate_password(self, password):
        pw_pat = re.compile("^[0-9a-zA-Z]+$")
        if password.data == "":
            raise ValidationError('入力してください')

        if len(password.data) < 8 or len(password.data) > 25:
            raise ValidationError('8文字以上25文字以下')

        if not pw_pat.match(password.data):
            raise ValidationError('使用できない文字が含まれています')

    def validate_gender(self, gender):
        if gender.data is None:
            raise ValidationError('選択してください')

    def validate_birth(self, birth):
        if ken_code.data == "":
            raise ValidationError('選択してください')

```

フォームクラスを定義して、バリデーション内容をif文で追加していきます。
設定したバリデーションに引っかからなければ、正しい値として送られます。

```signup.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8" />
    <title>登録</title>
    <h1>登録情報を入力</h1>
</head>
<body>
    <h2>{{ message }}</h2>
    <form method="POST" action="/registrate">
        {{ form.csrf_token }}
        {{ form.hidden_tag() }}
        <!-- 名前 -->
        <div style="margin: 10px;">
            {{ form.name.label }}</br>
            {% if form.name.errors %}
            <span style="color: red;">{{ form.name.errors[0] }}</span></br>
            {% endif %}
            {{ form.name(placeholder="田中太郎") }}
        </div>
        <!-- パスワード -->
        <div style="margin: 10px;">
            {{ form.password.label }}</br>
            {% if form.password.errors %}
            <span style="color: red;">{{ form.password.errors[0] }}</span></br>
            {% endif %}
            {{ form.password(placeholder="8文字以上25字以下") }}
        </div>
        <!-- 性別 -->
        <div style="margin: 10px;">
            {{ form.gender.label }}</br>
            {% if form.gender.errors %}
            <span style="color: red;">{{ form.gender.errors[1] }}</span></br>
            {% endif %}
            {% for subgender in form.gender %}
            <tr>
                <td>
                    {{ subgender}}
                </td>
                <td>
                    {{ subgender.label }}
                </td>
            </tr>
            {% endfor %}
        </div>
        <!-- 生まれ年 -->
        <div style="margin: 10px;">
            {{ form.birth.label }}</br>
            {% if form.birtherrors %}
            <span style="color: red;">{{ form.birth.errors[0] }}</span></br>
            {% endif %}
            {{ form.birth}}
        </div>
        <!-- 取得資格 -->
        <div style="margin: 10px;">
            取得資格</br>
            {{ form.shikaku1 }}
            {{ form.shikaku1.label }}
            </br>
            {{ form.shikaku2 }}
            {{ form.shikaku2.label }}
            </br>
        </div>
        <div style="margin: 10px;">
            <input type="submit" value="送信">
        </div>
    </form>
</body>
</html>
```

```index.py
@app.route('/')
def signup():
    """会員登録ページを表示"""
    form = signup_form(request.form)
    
    return render_template(
        'signup.html',
        form=form
    )

@app.route('/registrate', methods=['POST'])
def registrate():
    """登録情報を受け取る"""
    form = signup_form()
    if form.validate_on_submit():
       # validateを通過したら
       name = form.name.data,
       password = form.password.data,
       gender = form.gender.data,
       birth = form.birth.data,  
       if form.shikaku1.data != 0:
           shikaku1 = form.shikaku1.data
       if form.shikaku2.data != 0:
           shikaku2 = form.shikaku2.data
           return redirect('/')
    # validat通過できなければ入力フォームに戻す
    message = "バリデーションによる阻止"
    return render_template(
        "signup.html",
        form=form,
        message=message
    )

```
ちなみに、入力フォームに最初から値を入れておきたい場合（placeholderではなくvalueの方）
`form.変数名.data = 表示させたい値`　と書いておくといい。

```index.py
@app.route('/')
def signup():
    """会員登録ページを表示"""
    form = signup_form(request.form)

    form.name.data = '田中太郎'
    form.gender.data = '1'
    form.birth.data = '2021'
    form.shikaku1.data = True # checked
    form.shikaku2.data = False

    return render_template(
        'signup.html',
        form=form
    )

```
BooleanFieldは名前の通りbool値を返すフィールド。
チェックがあればTrue,チェックがなければFalseを返します。

今回はsignup_form()という関数を作りましたが、例えば入力項目を減らしてログインフォームなどに流用しようとすると、validation関数のところで引っかかってエラーになります。


# 参考にした記事など
> FlaskFormで簡単にバリデーションする方法　https://qiita.com/kotamatsuoka/items/c93129f6ade5974dc122
>【Python】Flask+WTFormsで簡単にフォームを作成する https://hirahira.blog/flask-wtforms/
> Flask Form Validation with Flask-WTF　https://stackabuse.com/flask-form-validation-with-flask-wtf/
> WTForm公式ドキュメント https://wtforms.readthedocs.io/en/2.3.x/