MENU

【初心者向けWebサイト開発】Flaskで掲示板をつくってみる

この記事ではWebアプリケーションフレームワークのFlaskを使用して掲示板をつくってみます。

この記事のサンプルコードでは画像のような掲示板を作成できます。

Flaskのインストールや基本的な使い方は以下の記事で紹介していますので、まだの方はご覧ください。

目次

ディレクトリ構成

今回つくるアプリケーションは、以下のようなディレクトリ構成になっています。

my_flask_app/
├── app.py
├── models.py
├── templates/
│   ├── index.html
│   ├── new_post.html
│   └── post_detail.html
└── static/
    └── style.css

サンプルコード

app.py

from flask import Flask, render_template, request, redirect, url_for
from models import db, Post, Reply
from datetime import datetime

def create_app():
    app = Flask(__name__)
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///bbs.db'
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

    db.init_app(app)

    with app.app_context():
        db.create_all()

    return app

app = create_app()

@app.route('/')
def index():
    posts = Post.query.order_by(Post.timestamp.desc()).all()
    return render_template('index.html', posts=posts)

@app.route('/new', methods=['GET', 'POST'])
def new_post():
    if request.method == 'POST':
        title = request.form['title']
        content = request.form['content']
        new_post = Post(title=title, content=content)
        db.session.add(new_post)
        db.session.commit()
        return redirect(url_for('index'))
    return render_template('new_post.html')

@app.route('/post/<int:post_id>', methods=['GET', 'POST'])
def post_detail(post_id):
    post = Post.query.get_or_404(post_id)
    if request.method == 'POST':
        content = request.form['content']
        reply_count = Reply.query.filter_by(post_id=post_id).count() + 1
        new_reply = Reply(content=content, post_id=post_id, reply_number=reply_count)
        db.session.add(new_reply)
        db.session.commit()
        return redirect(url_for('post_detail', post_id=post_id))
    return render_template('post_detail.html', post=post)

@app.route('/delete/<int:post_id>', methods=['POST'])
def delete_post(post_id):
    post = Post.query.get(post_id)
    if post:
        db.session.delete(post)
        db.session.commit()
    return redirect(url_for('index'))

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

models.py

from datetime import datetime
from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    title = db.Column(db.String(100), nullable=False)
    content = db.Column(db.Text, nullable=False)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
    replies = db.relationship('Reply', backref='post', cascade="all, delete-orphan", lazy=True)

    def __init__(self, title, content):
        self.title = title
        self.content = content

class Reply(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    content = db.Column(db.Text, nullable=False)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
    post_id = db.Column(db.Integer, db.ForeignKey('post.id'), nullable=False)
    reply_number = db.Column(db.Integer, nullable=False)

    def __init__(self, content, post_id, reply_number):
        self.content = content
        self.post_id = post_id
        self.reply_number = reply_number

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>掲示板</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <div class="container">
        <h1>掲示板</h1>
        <a href="{{ url_for('new_post') }}">新規投稿</a>
        <hr>
        {% for post in posts %}
            <div class="post">
                <h2><a href="{{ url_for('post_detail', post_id=post.id) }}">{{ post.title }}</a></h2>
                <p>{{ post.content }}</p>
                <small>{{ post.timestamp }}</small>
                <a href="{{ url_for('post_detail', post_id=post.id) }}">返信</a>
                <!-- 削除ボタンの追加 -->
                <form action="{{ url_for('delete_post', post_id=post.id) }}" method="post" style="display:inline;">
                    <button type="submit" onclick="return confirm('本当に削除しますか?');">削除</button>
                </form>
            </div>
            <hr>
        {% endfor %}
    </div>
</body>
</html>

new_post.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>新規投稿</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <div class="container">
        <h1>新規投稿</h1>
        <form action="{{ url_for('new_post') }}" method="post">
            <label for="title">タイトル:</label><br>
            <input type="text" id="title" name="title"><br>
            <label for="content">内容:</label><br>
            <textarea id="content" name="content"></textarea><br>
            <input type="submit" value="投稿">
        </form>
        <a href="{{ url_for('index') }}">戻る</a>
    </div>
</body>
</html>

post_detail.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{{ post.title }}</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
    <div class="container">
        <h1>{{ post.title }}</h1>
        <p>{{ post.content }}</p>
        <small>{{ post.timestamp }}</small>
        <!-- 削除ボタンの追加 -->
        <form action="{{ url_for('delete_post', post_id=post.id) }}" method="post" style="display:inline;">
            <button type="submit" onclick="return confirm('本当に削除しますか?');">削除</button>
        </form>
        <hr>
        <form action="{{ url_for('post_detail', post_id=post.id) }}" method="post">
            <label for="content">返信:</label><br>
            <textarea id="content" name="content"></textarea><br>
            <input type="submit" value="返信">
        </form>
        <hr>
        {% for reply in post.replies %}
            <div class="reply">
                <p>{{ reply.content }}</p>
                <small>{{ reply.timestamp }} - #{{ reply.reply_number }}</small>
            </div>
            <hr>
        {% endfor %}
        <a href="{{ url_for('index') }}">戻る</a>
    </div>
</body>
</html>

style.css

/* static/style.css */
body {
    display: flex;
    justify-content: center;
    align-items: center;
    height: 100vh; /* Full viewport height */
    margin: 0;
    font-family: Arial, sans-serif;
    background-color: #f5f5f5;
}

.container {
    width: 80%;
    max-width: 800px;
    margin: 20px auto;
    padding: 20px;
    border: 1px solid #ccc;
    border-radius: 8px;
    background-color: #ffffff;
    box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

/* 投稿や返信のタイトル、内容のボックスサイズ調整 */
textarea {
    width: 100%;
    max-width: 100%;
    padding: 10px;
    box-sizing: border-box;
}

input[type="text"], textarea {
    margin-bottom: 10px;
}

実行結果

実行すると、以下のようなホーム画面が表示されます。

「新規投稿」をクリックして新しい掲示板を作成してみます。

ホーム画面に投稿が追加されました。

「テスト」をクリックして、先ほどの投稿に移動し、返信してみます。

返信が追加されました。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

20代の組み込みソフトウェアエンジニア
主な使用言語はC++

---------------------資格---------------------
応用情報技術者
ネットワークスペシャリスト

目次