こんにちは、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アプリ応用編は完結です。この知識を元に、ぜひあなた自身のアイデアを形にしてみてください!
コメント