FlaskとSQLAlchemyを用いた掲示板トップページの投稿一覧実装とモデル関連付け

掲示板アプリケーションのトップページにおいて、投稿一覧を表示するためのバックエンドのデータモデル関連付けと、フロントエンドのレイアウト実装について解説します。ユーザーと投稿データを紐付け、トップページで一覧としてレンダリングする流れを構築します。

1. データモデルの拡張と関連付け

まず、投稿(スレッド)とユーザー(メンバー)を関連付けるために、SQLAlchemyのモデル定義を更新します。外部キーとリレーションシップを設定し、投稿データからユーザー情報へ簡単にアクセスできるようにします。

class Thread(db.Model):
    __tablename__ = 'threads'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    subject = db.Column(db.String(255), nullable=False)
    body = db.Column(db.Text, nullable=False)
    created_at = db.Column(db.DateTime, default=datetime.utcnow)
    
    board_id = db.Column(db.Integer, db.ForeignKey('boards.id'))
    board = db.relationship('Board', backref='threads')
    
    member_id = db.Column(db.String(64), db.ForeignKey('members.id'), nullable=False)
    member = db.relationship('Member', backref='threads')

モデルを変更した後は、データベースのマイグレーションを適用します。既存のテストデータは外部キー制約に違反する可能性があるため、一度テーブルをクリアしてから flask db migrate および flask db upgrade を実行し、新しいスキーマを反映させます。

2. ビュー関数(ルート)の更新

投稿作成時の処理にユーザー情報の紐付けを追加し、トップページのルートで必要なデータをクエリしてテンプレートに渡します。

@blueprint.route('/create_thread/', methods=['GET', 'POST'])
@login_required
def create_thread():
    if request.method == 'POST':
        # フォームのバリデーション処理...
        
        new_thread = Thread(
            subject=form.subject.data,
            body=form.body.data,
            board_id=form.board_id.data,
            member_id=current_user.id  # ログインユーザーを著者として設定
        )
        db.session.add(new_thread)
        db.session.commit()
        return redirect(url_for('front.home'))
        
    return render_template('front/create_thread.html')

@blueprint.route('/')
def home():
    top_banners = Banner.query.order_by(Banner.weight.desc()).limit(4).all()
    all_boards = Board.query.all()
    recent_threads = Thread.query.order_by(Thread.created_at.desc()).all()
    
    context = {
        'banners': top_banners,
        'boards': all_boards,
        'threads': recent_threads
    }
    return render_template('front/home.html', **context)

3. フロントエンドテンプレートの構築

Jinja2テンプレートを使用して、トップページに投稿一覧のHTML構造を構築します。ソート用のタブと、各投稿のサムネイル・メタデータを表示するリストを実装します。

<div class="thread-tabs">
    <a href="#" class="tab-link active">新着順</a>
    <a href="#" class="tab-link">人気投稿</a>
    <a href="#" class="tab-link">高評価順</a>
    <a href="#" class="tab-link">コメント順</a>
</div>

<ul class="thread-list">
    {% for thread in threads %}
    <li class="thread-item">
        <div class="avatar-wrapper">
            <img src="{{ thread.member.avatar_url or url_for('static', filename='img/default_avatar.png') }}" alt="Avatar">
        </div>
        <div class="thread-details">
            <h3 class="thread-subject">{{ thread.subject }}</h3>
            <div class="thread-meta">
                <span class="meta-author">{{ thread.member.nickname }}</span>
                <span class="meta-date">{{ thread.created_at.strftime('%Y-%m-%d %H:%M') }}</span>
                <span class="meta-comments">コメント: 0</span>
                <span class="meta-views">閲覧: 0</span>
            </div>
        </div>
    </li>
    {% endfor %}
</ul>

4. CSSスタイリングの適用

Flexboxを活用して、アバターとテキスト情報を横並びに配置し、モダンでクリーンなリストデザインを適用します。

.thread-container {
    border: 1px solid #e0e0e0;
    border-radius: 8px;
    padding: 15px;
    margin-top: 24px;
    background-color: #fff;
}

.thread-tabs {
    display: flex;
    gap: 10px;
    border-bottom: 1px solid #eee;
    padding-bottom: 10px;
}

.tab-link {
    text-decoration: none;
    color: #555;
    padding: 6px 12px;
    border-radius: 4px;
    font-size: 14px;
}

.tab-link.active {
    background-color: #f0f0f0;
    color: #333;
    font-weight: bold;
}

.thread-list {
    list-style: none;
    margin-top: 16px;
    padding: 0;
}

.thread-item {
    display: flex;
    align-items: flex-start;
    padding: 12px 0;
    border-bottom: 1px solid #f5f5f5;
}

.thread-item:last-child {
    border-bottom: none;
}

.avatar-wrapper img {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    object-fit: cover;
    margin-right: 16px;
}

.thread-details {
    flex: 1;
}

.thread-subject {
    font-size: 16px;
    font-weight: 500;
    color: #222;
    margin: 0 0 8px 0;
}

.thread-meta {
    display: flex;
    gap: 16px;
    font-size: 12px;
    color: #999;
}

タグ: flask SQLAlchemy Jinja2 Python CSS

10月12日 23:29 投稿