夏休みなので小学生とPythonを勉強してみた - 課題8 掲示板

小学6年生の長男が夏休みの自由研究にPythonを勉強したいというので付き合ってみました。
どこから手を付けていけばわからないので、いくつか課題を出し、それを実現するコードを一緒に書くということを繰り返しました。

この記事では、「課題8 掲示板」について扱います。その他の課題については下記の記事をご覧ください。

* [夏休みなので小学生とPythonを勉強してみた - 課題1 FizzBuzz - Qiita](https://qiita.com/shakiyam/items/b8366fde704639abb63d)
* [夏休みなので小学生とPythonを勉強してみた - 課題2 掛け算九九 - Qiita](https://qiita.com/shakiyam/items/0e4153fe7c610b3e8e49)
* [夏休みなので小学生とPythonを勉強してみた - 課題3 掛け算ゲーム - Qiita](https://qiita.com/shakiyam/items/4b4df04369914df3baa6)
* [夏休みなので小学生とPythonを勉強してみた - 課題4 じゃんけん - Qiita](https://qiita.com/shakiyam/items/05c756b4726c941c1c5c)
* [夏休みなので小学生とPythonを勉強してみた - 課題5 サイコロの目の出る確率 - Qiita](https://qiita.com/shakiyam/items/9e0c5548efd0952ab250)
* [夏休みなので小学生とPythonを勉強してみた - 課題6 写真の整理 - Qiita](https://qiita.com/shakiyam/items/2dd2e2763ccf85b19126)
* [夏休みなので小学生とPythonを勉強してみた - 課題7 掛け算ゲームWeb版 - Qiita](https://qiita.com/shakiyam/items/3945a224a8774c332220)
* [夏休みなので小学生とPythonを勉強してみた - 課題9 顔の検出 - Qiita](https://qiita.com/shakiyam/items/110b4819b12124587d80)
* [夏休みなので小学生とPythonを勉強してみた - 課題10 顔の認識 - Qiita](https://qiita.com/shakiyam/items/0125dd5db15976d5520e)

コードは[GitHub](https://github.com/shakiyam/python4kids)にて公開しています。

課題8-1 入力フォームを表示する
------------------------------

### 指示 ###

ユーザーが入力するフォームを表示するプログラムを作りましょう。

### ヒント ###

Webアプリケーションの見栄えをよくするためにBootstrapを使うことにします。

* [Bootstrap · The most popular HTML, CSS, and JS library in the world.](https://getbootstrap.com/)

ドキュメントのQuick startを参考にして作成します。

* [Quick start -- Introduction · Bootstrap](https://getbootstrap.com/docs/4.3/getting-started/introduction/)

### 回答例 ###

```python:01_form.py
from flask import Flask
from flask import render_template

app = Flask(__name__)


@app.route('/', methods=['GET'])
def get():
    return render_template('01.html')
```

```html:templates/01.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Sample BBS</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  </head>
  <body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <div class="container">
        <h1 class="navbar-brand">Sample BBS</h1>
      </div>
    </nav>
    <div class="container">
      <form method="post">
        <textarea class="form-control" name="message"></textarea>
        <div class="d-flex justify-content-end"><button class="btn btn-primary" type="submit">Post</button></div>
      </form>
    </div>
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  </body>
</html>
```

### 実行例 ###

```console
$ FLASK_APP=01_form.py FLASK_ENV=development flask run --host=0.0.0.0
 * Serving Flask app "01_form.py" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 867-383-452
127.0.0.1 - - [23/Aug/2019 07:03:23] "GET / HTTP/1.1" 200 -
```

![ブラウザでのアクセス結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174/1b63dd9d-f229-7faf-29bc-f8e9ce1468c5.png)

課題8-2 リダイレクト
--------------------

### 指示 ###

フォームに入力されたら、rootにリダイレクトされるプログラムを作りましょう。

### ヒント ###

FlaskのQuickstartにある、Redirects and Errorsの節を参考にして作成します。

* [Redirects and Errors -- Quickstart — Flask Documentation](https://flask.palletsprojects.com/quickstart/#redirects-and-errors)

### 回答例 ###

```python:templates/02_redirect.py
from flask import Flask
from flask import redirect
from flask import render_template

app = Flask(__name__)


@app.route('/', methods=['GET'])
def get():
    return render_template('02.html')


@app.route('/', methods=['POST'])
def post():
    return redirect('/', 303)
```

### 実行例 ###

```console
$ FLASK_APP=02_redirect.py FLASK_ENV=development flask run --host=0.0.0.0
 * Serving Flask app "02_redirect.py" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 867-383-452
127.0.0.1 - - [23/Aug/2019 07:12:10] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [23/Aug/2019 07:12:12] "POST / HTTP/1.1" 303 -
127.0.0.1 - - [23/Aug/2019 07:12:12] "GET / HTTP/1.1" 200 -
```

課題8-3 投稿処理
----------------

### 指示 ###

フォームに入力された内容を表示するプログラムを作りましょう。

### ヒント ###

Flaskで使用しているレンダリング・エンジンはJinja2です。Jinja2のドキュメントにforループなどの書き方が載っています。

* [Template Designer Documentation — Jinja Documentation](https://jinja.readthedocs.io/templates/)

### 回答例 ###

```python:03_post.py
from flask import Flask
from flask import redirect
from flask import render_template
from flask import request

app = Flask(__name__)
posts = []


@app.route('/', methods=['GET'])
def get():
    return render_template('03.html', posts=posts)


@app.route('/', methods=['POST'])
def post():
    posts.insert(0, {'message': request.form['message']})
    return redirect('/', 303)
```

```html:templates/03.html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Sample BBS</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
  </head>
  <body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <div class="container">
        <h1 class="navbar-brand">Sample BBS</h1>
      </div>
    </nav>
    <div class="container">
      <form method="post">
        <textarea class="form-control" name="message"></textarea>
        <div class="d-flex justify-content-end"><button class="btn btn-primary" type="submit">Post</button></div>
      </form>
      <br>
      <div class="list-group">
        {% for post in posts %}
          <div class="list-group-item">
            <p class="list-group-item-text">{{ post['message'] }}</p>
          </div>
        {% endfor %}
      </div>
    </div>
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
  </body>
</html>
```

### 実行例 ###

```console
$ FLASK_APP=03_post.py FLASK_ENV=development flask run --host=0.0.0.0
 * Serving Flask app "03_post.py" (lazy loading)
 * Environment: development
 * Debug mode: on
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 867-383-452
127.0.0.1 - - [23/Aug/2019 07:17:47] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [23/Aug/2019 07:18:06] "POST / HTTP/1.1" 303 -
127.0.0.1 - - [23/Aug/2019 07:18:06] "GET / HTTP/1.1" 200 -
```

![ブラウザでのアクセス結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174/44b39cd5-e4a0-c1ba-ff50-4c018b9e285f.png)
![ブラウザでのアクセス結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/174/0c7f3a5e-65b4-41aa-b862-c43d11f776f2.png)