【Webアプリ応用編 完結編】Flaskでデータベースの値を表示!動的な掲示板を完成させよう

ALL

こんにちは、Tech Samuraiです!
前回の記事「【Webアプリ応用編 #2】FlaskとSQLiteを連携!」では、Webフォームから送信された投稿を、SQLiteデータベースに保存することに成功しましたね。

私たちのアプリは、ユーザーとの対話を「記憶」する能力を手に入れました。しかし、その記憶はまだデータベースという金庫の中に眠っているだけです。シリーズ最終回となる今回の冒険の目的は、その金庫から**全ての投稿を読み出し、Webページ上に美しく表示する**ことです。

これを実現することで、私たちの「一言掲示板」は、誰もが投稿し、そして閲覧できる、真のコミュニティスペースとして完成します。さあ、Webアプリケーションに命を吹き込む、最後の仕上げを行いましょう!


ステップ1:バックエンドの改造 (`app.py`) – データベースから投稿を取得

まずは、`app.py`を修正し、ページが表示されるたび(GETリクエスト時)に、データベースから全ての投稿データを取得する機能を追加します。

import sqlite3
from flask import Flask, render_template, request, redirect, url_for

app = Flask(__name__)
DATABASE = 'guestbook.db'

def get_db_connection():
    """データベースへの接続を取得する関数"""
    conn = sqlite3.connect(DATABASE)
    # ↓↓↓ 重要:辞書のように列名でアクセスできるようにする ↓↓↓
    conn.row_factory = sqlite3.Row
    return conn

@app.route('/', methods=['GET', 'POST'])
def index():
    # データベースに接続
    conn = get_db_connection()

    if request.method == 'POST':
        name = request.form['name']
        message = request.form['message']

        if name and message:
            # データをINSERT
            conn.execute('INSERT INTO posts (name, message) VALUES (?, ?)',
                         (name, message))
            conn.commit()

        conn.close()
        return redirect(url_for('index'))

    # --- ▼▼▼ ここからがGETリクエスト時の追加処理 ▼▼▼ ---
    # 投稿を新しい順(idの降順)で全て取得する
    posts_cursor = conn.execute('SELECT * FROM posts ORDER BY timestamp DESC')
    posts = posts_cursor.fetchall()
    conn.close()

    # 取得した投稿リストを、HTMLテンプレートに渡す
    return render_template('index.html', posts=posts)


if __name__ == '__main__':
    app.run(debug=True)

コードのポイント解説

  • conn.row_factory = sqlite3.Row
    これは非常に便利な設定です。通常、データベースから取得したデータはタプル(例: (1, 'Tech Samurai', '...'))で返されますが、この設定をすると、辞書のように列名でアクセスできる特別なオブジェクト(例: post['name'])で返してくれるようになります。HTML側での作業が格段に楽になります。
  • SELECT * FROM posts ORDER BY timestamp DESC
    `posts`テーブルから全ての投稿(`*`)を取得します。`ORDER BY timestamp DESC`というSQL句を追加することで、`timestamp`(投稿日時)が新しいものから順(`DESC`)に並べ替えています。
  • render_template('index.html', posts=posts)
    データベースから取得した投稿のリスト(`posts`)を、HTMLテンプレートに`posts`という名前で渡しています。

ステップ2:フロントエンドの改造 (`templates/index.html`) – 投稿一覧の表示

ここが今回の核心です!Pythonから渡された投稿リスト(`posts`)を、HTML側でループ処理を使って、一つずつ表示していきます。Flaskのテンプレートエンジン(Jinja2)の`for`ループを使います。

それでは、**`templates/index.html`の完全なコード**を以下に示します。前回のものから、このコードに完全に置き換えてください。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>一言掲示板</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <div class="container">
        <h1>一言掲示板へようこそ!</h1>
        
        <form method="post" class="post-form">
            <div>
                <label for="name">名前:</label>
                <input type="text" name="name" id="name" required>
            </div>
            <div>
                <label for="message">メッセージ:</label>
                <textarea name="message" id="message" rows="4" required></textarea>
            </div>
            <div>
                <input type="submit" value="投稿する">
            </div>
        </form>

        <hr>

        <h2>投稿一覧</h2>
        <div class="posts-area">
            {% for post in posts %}
                <div class="post">
                    <p class="post-meta">
                        <strong>{{ post['name'] }}</strong>
                        <span class="timestamp">at {{ post['timestamp'] }}</span>
                    </p>
                    <p class="post-message">{{ post['message'] }}</p>
                </div>
            {% else %}
                <p>まだ投稿はありません。最初のメッセージを投稿しよう!</p>
            {% endfor %}
        </div>
    </div>
</body>
</html>

テンプレートのポイント解説

  • {% for post in posts %} ... {% endfor %}
    Pythonの`for`ループと同じです。`posts`リストから一件ずつ`post`を取り出して、間のHTMLを繰り返します。
  • {{ post['name'] }}
    `post`オブジェクトから、`name`列のデータを表示します。sqlite3.Row`のおかげで、このように列名でアクセスできます。
  • {{ post['message'] | nl2br }}
    メッセージを表示します。| nl2brはJinja2の「フィルタ」で、メッセージ内の改行をHTMLの改行タグ(<br>)に自動で変換してくれます。
  • {% else %}
    for`ループで使える特別な構文で、もし`posts`リストが空だった場合に、このブロックの中身が表示されます。

ステップ3:見た目を整えるCSS(おまけ)

掲示板らしく見えるように、`static/style.css`に以下のスタイルを追加しましょう。

/* (前回のCSSに追加) */
.post-form div {
    margin-bottom: 15px;
}
.post-form label {
    display: block;
    margin-bottom: 5px;
}
.post-form input[type="text"], .post-form textarea {
    width: 100%;
    padding: 8px;
    border: 1px solid #ccc;
    border-radius: 4px;
}
.posts-area {
    margin-top: 20px;
}
.post {
    border: 1px solid #ddd;
    border-radius: 5px;
    padding: 10px 15px;
    margin-bottom: 15px;
    background-color: #fff;
}
.post-meta {
    font-size: 14px;
    color: #555;
    border-bottom: 1px solid #eee;
    padding-bottom: 5px;
    margin-bottom: 10px;
}
.post-meta .timestamp {
    float: right;
    color: #999;
}
.post-message {
    font-size: 16px;
    line-height: 1.6;
}
/* メッセージ表示用のスタイルを追加 */
.post-message {
    white-space: pre-wrap; /* この一行が魔法です! */
    word-wrap: break-word; /* 長い単語でも折り返す */
}

まとめ:動的なWebアプリケーションの完成!

おめでとうございます!3回にわたる冒険の末、あなたはユーザーが投稿した内容をデータベースに保存し、それを動的にページに表示する、完全なWebアプリケーションを完成させました。

このシリーズを通して、

  • Pythonと**SQLite**でデータベースを操作する基本
  • **Flask**でWebフォームからのデータを受け取り、データベースに保存する方法
  • データベースから取得したデータを、**Jinja2**のテンプレートループで動的にHTML表示する方法

という、モダンなWebアプリケーション開発の基本的なワークフローを全て体験しました。これは、あらゆるWebサービスの根幹をなす技術です。

これにてWebアプリ応用編は完結です。この知識を元に、ぜひあなた自身のアイデアを形にしてみてください!

コメント

タイトルとURLをコピーしました