Flaskでフラッシュメッセージを表示する方法

(アドベントカレンダー3日目にして、ネタが尽き始めていますが、)

Flaskでフラッシュメッセージを表示する方法を説明していきます。
フラッシュメッセージは、ユーザーが何らかの行動を起こしたときに、その行動が正しく処理されたこと知らせたり、失敗したことを知らせるために利用されることが多いです。
例えば、ログイン成功時や、何らかの投稿に失敗した時などなど。

こんな感じで。
![](https://i.gyazo.com/299fc9e093ab544bd31fb54a84ead56d.gif)

Flaskの[公式ドキュメント](http://flask.pocoo.org/docs/1.0/patterns/flashing/)を見れば、ざっくり書いてありますが、アレンジを加えつつ、実装していきます。

## 目次
- Flaskのヘルパーメソッドのflash()の説明
- Flaskのヘルパーメソッドのget_flashed_messages()の説明
- サンプルアプリを実装
- おまけ

## 対象
- Flaskでフラッシュメッセージを表示したい人
- Flaskでアプリケーションを作っている人
- pythonでwebサービスを作ろうとしている人

## Flaskのヘルパーメソッドのflash()の説明
Flaskでフラッシュメッセージを表示する際に用いられるメソッドです。

`flask/helpers.py`の中を覗いて`flash()`を観察してみましょう。

```python:flask/helpers.py

def flash(message, category='message'):
    flashes = session.get('_flashes', [])
    flashes.append((category, message))
    session['_flashes'] = flashes
    message_flashed.send(current_app._get_current_object(),
                         message=message, category=category)
```

それほど複雑なことはやっていなさそうですね。

まず、第二引数でmessage以外も渡せるようですね。(今回初めて知りました^^;)
flashesという配列に、引数で渡されたmessageとcategoryを追加しています。

## Flaskのヘルパーメソッドのget_flashed_messages()の説明
ビューで使用するメソッドです。get_flashed_messages()を使用すれば、指定したflash()内の文字列が、ビューで表示することができます。

### 引数
引数は2つ指定することができます。`with_categories`と`category_filter`です。
#### with_categories
デフォルトはFalse。

`with_categories=True`にすると、フラッシュメッセージにもう一つ文字列を紐付けることできます。
そのもう一つの文字列をliタグのclassに設定することが多いようです。

```python:コントローラー
flash("フラッシュメッセージ", "もう一つの文字列")
```


```html:ビュー

<li class="もう一つの文字列">フラッシュメッセージ</li>
```
後ほど、サンプルアプリ内で実装します。


#### category_filter
今回は実装の対象から外しています。
`category_filter=["error"]`のように指定すると、配列の中の文字列をcategory(※1)として持つフラッシュメッセージしか表示しないようにできます。

※1 categoryとは、上の「with_categories」でいう「もう一つの文字列」です

## サンプルアプリを実装
実装してなんぼ！ってことで、簡単なサンプルアプリを作りましょう。

### コントローラーの実装

```python:app.py
from flask import Flask, flash, render_template, request

app = Flask(__name__)
app.config["SECRET_KEY"] = "sample1203"


@app.route("/", methods=["GET", "POST"])
def index():
    username = ""
    text = ""
    if request.method == "GET":
        return render_template("index.html")

    if request.method == "POST":
        if request.form["username"] and request.form["text"]:
            username = request.form["username"]
            text = request.form["text"]
            flash("投稿を成功しました＼(^o^)／", "success")
        if request.form["username"] == "":
            flash("ユーザー名を入力してください。", "failed")
        if request.form["text"] == "":
            flash("テキストを入力してください。", "failed")

        return render_template("index.html", username=username, text=text)


if __name__ == "__main__":
    app.run()
```

### ビューを実装

(少しきれいに見せるためにcssをいじっていますが、そんなに凝らなくても表示はされますw

```html:templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>サンプル</title>

<style>
    li {
        margin-left: -50px;
        border-bottom: 0.5px solid white;
        width: 100vw;
        color: white;
        padding-left: 10px;
    }
    .success {
        background-color: midnightblue;
    }
    .failed {
        background-color: deeppink;
    }
</style>
</head>
<body>

{% with messages = get_flashed_messages(with_categories=true) %}
  {% if messages %}
    <ul class=flashes>
    {% for category, message in messages %}
      <li class="{{ category }}">{{ message }}</li>
    {% endfor %}
    </ul>
  {% endif %}
{% endwith %}

<h2>フラッシュメッセージを表示できるかな</h2>

{{ username }}
{{ text }}

<form action="/" method=post>
    <dl>
      <dt>ユーザー名:
      <dd><input type=text name=username>
      <dt>テキスト:
      <dd><input type=text name=text>
    </dl>
    <p><input type=submit value=Send>
</form>

</body>
</html>
```

あとは、app.pyを動かすだけ！

```bash
$ python app.py
```

#### 成功した時のフラッシュメッセージ
![](https://i.gyazo.com/894680dd58ce8b31c66db79a34724dba.gif)

#### 失敗した時のフラッシュメッセージ
![](https://i.gyazo.com/299fc9e093ab544bd31fb54a84ead56d.gif)

## おまけ
最後までお付き合いいただき、ありがとうございました。
少しでもお役に立てたのであれば、嬉しいです。

[Renttle](https://renttle.jp)というサービスを開発中です。ぜひ、使ってみて、レビューをください。