【Flask】Flask_WTFを使ってフォーム作成してみた

## Flask_WTF　とは

フォームが簡単に作成できるFlaskの外部ライブラリです。
バリデーション設定や、CSRFセキュリティ対策も、よしなにやってくれる。

この記事では基本的な使い方を説明します。
フォームの見た目は装飾せず、機能のコードに極力留めています。
(色々コードを入れると混乱するため)
## 基本的な使い方
### ターミナル
```py
pip3 install Flask-WTF
```
### 事前設定
```py
from flask import Flask, render_template, url_for, redirect, session
from flask_wtf import FlaskForm
from wtforms import ValidationError, StringField, PasswordField, SubmitField

#Flaskアプリをインスタンス化
app = Flask(__name__)

# フォームを利用する際に必要。セキュリティ対策に必要。
app.config['SECRET_KEY'] = 'mysecretkey'

-
-
# この記述がないとアプリを起動できないので、ファイル末尾などに記述を忘れない。
if __name__ == "__main__":
    app.run(debug=True)
```
### フォームクラス
```py
"""
継承元のFlaskFormクラスの機能を使えるようになる。
各フィールドを定義する。ここで定義したフィールドを基にHTMLに配置する。
"""
class RegistrationForm(FlaskForm):
    email = StringField()
    username = StringField()
    password = PasswordField()
    pass_confirm = PasswordField()
    submit = SubmitField()
```
### View関数
```py
"""
"/register" にリクエストが送られると、フォームが作成され、最後のreturn文でユーザー登録ページを表示する。
フォームにデータが入力され、送信されると、再度このビュー関数が呼ばれる。
その際はフォームにデータ入力されているので、入力チェックに問題なければif文の処理の中も実行される
"""
@app.route("/register", methods=["GET", "POST"])
def register():
    #フォームを取得
    form = RegistrationForm()
    #フォームにデータ入力されていれば、入力チェック
    if form.validate_on_submit():
        #フォーム情報をセッションに格納
        session["email"] = form.email.data
        session["username"] = form.username.data
        session["password"] = form.password.data
        #ユーザー管理ページに転送
        return redirect(url_for("user_maintenance"))
    #フォームにはデータが入力されていない状態だと、if文の中は処理されず、ユーザー登録ページを表示する
    return render_template("register.html", form=form)

#ユーザー一覧画面のデコレータ。呼び出されたら画面を表示するだけの設定をしておく。
#ここでは、フォームで送信した内容を受け取れているかチェックするため。
@app.route("/user_maintenance")
def user_maintenance():
  return render_template("user_maintenance.html")
```
### テンプレート(HTML)

"templates"という名前でフォルダ作成し、そのフォルダ内にhtmlファイルを入れる必要がある。
今回は、"register.html"という名前でhtmlファイルを作成し、以下の記述でフォーム作成する。
```py
<form method="POST">
    <!-- CSRF対策(トークンが生成される) -->
    {{ form.hidden_tag() }}
    <div>
        {{ form.username.label }} {{ form.username() }}
    </div>
    <div>
        {{ form.email.label }} {{ form.email() }}
    </div>
    <div>
        {{ form.password.label }} {{ form.password() }}
    </div>
    <div>
        {{ form.pass_confirm.label }} {{ form.pass_confirm() }}
    </div>
    {{ form.submit() }}
</form>
```

フォームからのセッションデータが送られているか確認するために、ユーザー一覧画面を用意。

```py
<h2>ユーザー管理画面</h2>

# 新規登録画面に移動するリンクを生成
<a href="{{ url_for('register' )}}">
  ユーザー登録
</a>

<table>
  <thead>
      <tr>
          <th>ユーザー名</th>
          <th>メールアドレス</th> 
      </tr>
  </thead>
  <tbody>
      <tr>
          # セッションから名前とメールアドレスを取り出す
          <td>{{ session['username'] }}</td>
          <td>{{ session['email'] }}</td>
      </tr>
  </tbody>
</table>

```
### 表示確認
app.py でサーバーを立ち上げ、"/register" をパスパラメータに付けてブラウザ表示する。
名前などフォームに入れて、送信。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695961/2ec6253a-ddaf-178a-a535-5f456ac03526.png)

上記の画面で送信すると、リダイレクト先のユーザー管理画面に遷移する。
新規登録ページで入力した、名前とメールアドレスが表示される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695961/a97e0a2d-d189-aee5-c7a5-c64d9fcf311f.png)

## バリデーション設定を追記
フォームに入力した内容がルールに反していた場合に、エラー表示されるよう、コードを追記

### インポート
```py
from flask import Flask, render_template, url_for, redirect, session
from flask_wtf import FlaskForm
from wtforms import ValidationError, StringField, PasswordField, SubmitField
# 以下を追記
from wtforms.validators import DataRequired, Email, EqualTo
```
### フォームクラス
StringField などのカッコ内に、
各フィールドのバリデーションルールを追記。
```py
class RegistrationForm(FlaskForm):
  email = StringField('メールアドレス', validators=[DataRequired(), Email(message='正しいメールアドレスを入力してください')])
  username = StringField('ユーザー名', validators=[DataRequired()])
  password = PasswordField('パスワード', validators=[DataRequired(), EqualTo("pass_confirm", message='パスワードが一致していません')])
  pass_confirm = PasswordField('パスワード(確認)', validators=[DataRequired()])
  submit = SubmitField('登録')
```
- 全て必須入力
- password と pass_confirmは一致が必須
- ラベル名を日本語に設定
- エラーメッセージを日本語に設定

### テンプレート
エラーメッセージが表示されるように追記。
```py
<form method="POST">
  <!-- CSRF対策(トークンが生成される) -->
  {{ form.hidden_tag() }}
  <div>
    {{ form.username.label }} {{ form.username() }}
    # 追記
    {% for error in form.username.errors %}
      <span style="color: red;">{{ error }}</span>
    {% endfor %}
  </div>
  <div>
    {{ form.email.label }} {{ form.email() }}
    # 追記
    {% for error in form.email.errors %}
      <span style="color: red;">{{ error }}</span>
    {% endfor %}
  </div>
  <div>
    {{ form.password.label }} {{ form.password() }}
    # 追記
    {% for error in form.password.errors %}
      <span style="color: red;">{{ error }}</span>
    {% endfor %}
  </div>
  <div>
    {{ form.pass_confirm.label }} {{ form.pass_confirm() }}
  </div>
    {{ form.submit() }}
</form>
```
### 表示確認
- メールアドレスを正しい形式で入力していない
- パスワードとパスワード(確認)が一致していない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/695961/7de108c3-adc4-310e-8f25-da3eb2363e73.png)


以上、基本的なフォーム作成方法を説明しました。