【Python】簡易掲示板の作成（Flask）

> Python学習の取っ掛かりとして、以前にjavascriptの学習で作った簡易掲示板（投稿/表示/削除のみ）と全く同じものをPython,Flaskで作る。

Python基礎参考

https://qiita.com/python_academia/items/bde85d799e7db4936ce1


<details><summary>簡易掲示板 コード全体 (app.py, index.html)</summary>

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>掲示板</title>
    <!-- Flask url_for関数 static:静的ファイル格納先 -->
    <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>

    <div>
        <h1>掲示板</h1>
        <!-- フォーム：投稿入力欄と投稿ボタン -->
        <form method="post" action="/">
            <textarea id="postInput" name="post_content" placeholder="ここに投稿内容を入力"></textarea>
            <button id="postButton" type="submit">投稿する</button>
        </form>

        <!-- 投稿リスト -->
        <ul id="posts">
            <!-- 投稿リストの各要素ループ -->
            {% for post in posts %}
            <!-- 投稿 -->
            <li class="post">
                {{ post['content'] }}<br>
                <!-- 投稿日時 -->
                <small>{{ post['timestamp'] }}</small>

                <!-- フォーム：投稿削除 -->
                <div class="button-side-container">
                    <form method="post" action="/">
                        <!-- 削除ボタン　削除する投稿のインデックスがvalueとして送信される -->
                        <button type="submit" name="delete_post" value="{{ loop.index0 }}" class="deleteButton">削除</button>
                    </form>
                </div>
            </li>            
            {% endfor %}
        </ul>
    </div>

</body>
</html>
```

```python:app.py
# Flaskフレームワークと必要なモジュールをインポート
from flask import Flask, render_template, request
from datetime import datetime

# Flaskアプリケーションの作成
app = Flask(__name__)

# 投稿を格納するリスト
posts = []

# ルートパスへのGETとPOSTメソッドの処理を行う関数
@app.route('/', methods=['GET', 'POST'])
def index():
    # POSTメソッドでリクエストが送られた場合
    if request.method == 'POST':
        # フォームに'delete_post'が含まれていれば、投稿を削除
        if 'delete_post' in request.form:
            delete_index = int(request.form['delete_post'])
            delete_post(delete_index)
        else:
            # フォームから受け取った投稿内容を取得
            post_content = request.form['post_content']
            # 投稿内容が空でない場合、新しい投稿を追加
            if post_content.strip():
                posts.insert(0, {'content': post_content, 'timestamp': format_datetime()})
    
    # index.htmlテンプレートにpostsの内容を渡してレンダリング
    return render_template('index.html', posts=posts)

# 日時をフォーマットする関数
def format_datetime():
    now = datetime.now()
    return now.strftime("%Y/%m/%d %H:%M")

# 指定されたインデックスの投稿を削除する関数
def delete_post(index):
    if 0 <= index < len(posts):
        del posts[index]

# アプリケーションを実行
if __name__ == '__main__':
    app.run(debug=True)

```

</details>

---

### 1. import文、Flask

```python
from flask import Flask, render_template, request
from datetime import datetime

# Flaskアプリケーションの作成
app = Flask(__name__)
```

- `from モジュール名 import 変数名・関数名`
import文　変数・関数名はコンマ区切り
※from flask import Flask (flask.FlaskクラスをFlaskという変数に代入)

https://qiita.com/python_academia/items/62aefbf4e373cd2aa496

- `app = Flask(__name__)`
Flaskクラスのインスタンスを作り、appという変数に代入。
※`__name__` は、現在のPythonモジュールの名前を指す。


https://flask.palletsprojects.com/en/3.0.x/quickstart/

### 2. デコレータとルーティング

```python
@app.route('/', methods=['GET', 'POST'])
def index():
    # ... 
```
- `@app.route('/', methods=['GET', 'POST'])`
次の行で定義される関数を指定したURLにマッピングする
この場合GETとPOSTメソッドの双方を受け付ける。標準ではGETのみ

https://zenn.dev/yag_ays/articles/4a91622bbfc83c

- `def index():`
`def 関数名()` 
関数の定義

https://www.javadrive.jp/python/userfunc/index1.html


### 3. リクエストの処理、条件分岐

```python
if request.method == 'POST':
    # ... 
```

- `request.method`
HTTPリクエストのメソッドを取得(GET/POST)
- `if request.method == 'POST':`
リクエストがPOSTメソッドである場合

https://qiita.com/nagataaaas/items/3116352da186df102d96

### 4. フォームからのデータ取得

```python
post_content = request.form['post_content']
```

- `request.form['post_content']`
`request.form` フォームから送信されたデータが格納された辞書型オブジェクト
そこから `post_content` という名前のデータを取得

https://www.javadrive.jp/python/dictionary/index2.html

### 5. 日時のフォーマット関数

```python
def format_datetime():
    now = datetime.now()
    return now.strftime("%Y/%m/%d %H:%M")
```

- `datetime.now()`
現在の日時を取得
- `now.strftime("%Y/%m/%d %H:%M")`
取得した日時を指定されたフォーマットに変換

※`from datetime import datetime`

https://docs.python.org/ja/3/library/datetime.html#datetime.date.strftime

### 6. 投稿の削除関数

```python
def delete_post(index):
    if 0 <= index < len(posts):
        del posts[index]
```

- `if 0 <= index < len(posts):` 
指定された `index` が `posts`リストの範囲内にあるかどうかを確認。
- `del posts[index]`
postsリストから指定されたインデックスの要素を削除する。

https://www.javadrive.jp/python/list/index8.html

### 7. Jinjaを使用したHTML生成

```html
{% for post in posts %}
    # ... 
    {{ post['content'] }}
        {{ post['timestamp'] }}
    # ... 
{% endfor %}
```

- `{% for post in posts %}`  `{% endfor %}`
Jinja2におけるループ構造、`posts` リスト内の各要素に対してループ処理を行う
- `{{ }}` で囲まれた部分がJinjaテンプレートエンジンによって動的に生成される部分

https://myafu-python.com/syntax/library/jinja2/

---
### 実行・確認
```html
python app.py
```
デフォルトでは `http://127.0.0.1:5000/`

---
その他参考

https://mojaie.github.io/pyenv-lzma/