Flask + SQLAlchemyで音楽共有SNSを作る - 全投稿表示機能編

## はじめに

前回の記事では、日々の楽曲投稿機能について解説しました。今回は、投稿された全ての楽曲を表示する機能と、フォローしているユーザーの投稿を表示する機能の実装について詳しく説明します。

## 全投稿表示機能の実装

以下に、全ての投稿とフォローしているユーザーの投稿を表示する機能の実装コードを示します：

```python
from flask import render_template, request
from flask_login import current_user
from app import app
from app.models import DailySong

@app.route('/all_posts')
def all_posts():
    sort_order = request.args.get('sort', 'desc')
    if sort_order == 'asc':
        posts = DailySong.query.order_by(DailySong.date_posted.asc()).all()
    else:
        posts = DailySong.query.order_by(DailySong.date_posted.desc()).all()

    followed_posts = []
    if current_user.is_authenticated:
        followed_users = [user.id for user in current_user.followed]
        if sort_order == 'asc':
            followed_posts = DailySong.query.filter(DailySong.user_id.in_(
                followed_users)).order_by(DailySong.date_posted.asc()).all()
        else:
            followed_posts = DailySong.query.filter(DailySong.user_id.in_(
                followed_users)).order_by(DailySong.date_posted.desc()).all()

    return render_template('all_posts.html', posts=posts, followed_posts=followed_posts, current_sort=sort_order)
```

## 機能の詳細解説

1. **ルーティング**
   ```python
   @app.route('/all_posts')
   ```
   - `/all_posts` URLにアクセスすると、この関数が呼び出されます。

2. **ソート順の取得**
   ```python
   sort_order = request.args.get('sort', 'desc')
   ```
   - URLパラメータから`sort`の値を取得します。デフォルトは降順（`'desc'`）です。

3. **全投稿の取得**
   ```python
   if sort_order == 'asc':
       posts = DailySong.query.order_by(DailySong.date_posted.asc()).all()
   else:
       posts = DailySong.query.order_by(DailySong.date_posted.desc()).all()
   ```
   - ソート順に応じて、全ての投稿を日付順に取得します。

4. **フォロー中のユーザーの投稿取得**
   ```python
   followed_posts = []
   if current_user.is_authenticated:
       followed_users = [user.id for user in current_user.followed]
       if sort_order == 'asc':
           followed_posts = DailySong.query.filter(DailySong.user_id.in_(
               followed_users)).order_by(DailySong.date_posted.asc()).all()
       else:
           followed_posts = DailySong.query.filter(DailySong.user_id.in_(
               followed_users)).order_by(DailySong.date_posted.desc()).all()
   ```
   - ユーザーがログインしている場合、フォローしているユーザーの投稿を取得します。
   - `current_user.followed`から、フォロー中のユーザーIDリストを作成します。
   - そのIDリストを使用して、フォロー中のユーザーの投稿をフィルタリングします。

5. **テンプレートのレンダリング**
   ```python
   return render_template('all_posts.html', posts=posts, followed_posts=followed_posts, current_sort=sort_order)
   ```
   - 取得した投稿データとソート順をテンプレートに渡してレンダリングします。

## テンプレートの例

`all_posts.html`テンプレートの例を以下に示します：

```html
{% extends "base.html" %}

{% block content %}
    <h1>全ての投稿</h1>
    
    <div>
        ソート順:
        <a href="{{ url_for('all_posts', sort='desc') }}" {% if current_sort == 'desc' %}class="active"{% endif %}>最新順</a> |
        <a href="{{ url_for('all_posts', sort='asc') }}" {% if current_sort == 'asc' %}class="active"{% endif %}>古い順</a>
    </div>

    <h2>全ユーザーの投稿</h2>
    {% for post in posts %}
        <div class="post">
            <h3>{{ post.title }} - {{ post.artist }}</h3>
            <p>ジャンル: {{ post.genre }}</p>
            <p>投稿者: {{ post.user.username }}</p>
            <p>投稿日: {{ post.date_posted.strftime('%Y-%m-%d') }}</p>
            <a href="{{ post.music_url }}" target="_blank">曲を聴く</a>
        </div>
    {% endfor %}

    {% if current_user.is_authenticated %}
        <h2>フォロー中のユーザーの投稿</h2>
        {% for post in followed_posts %}
            <div class="post">
                <h3>{{ post.title }} - {{ post.artist }}</h3>
                <p>ジャンル: {{ post.genre }}</p>
                <p>投稿者: {{ post.user.username }}</p>
                <p>投稿日: {{ post.date_posted.strftime('%Y-%m-%d') }}</p>
                <a href="{{ post.music_url }}" target="_blank">曲を聴く</a>
            </div>
        {% endfor %}
    {% endif %}
{% endblock %}
```

## 機能の特徴とユーザビリティ

1. **ソート機能**: ユーザーは投稿を最新順または古い順で表示できます。

2. **全投稿とフォロー投稿の分離**: 全てのユーザーの投稿と、フォローしているユーザーの投稿を別々に表示することで、情報の整理がしやすくなっています。

3. **ログイン状態の考慮**: ログインしていないユーザーにはフォロー中の投稿を表示しないようにしています。

4. **外部リンク**: 各投稿には音楽を聴くためのリンクが含まれており、ユーザーは簡単に楽曲にアクセスできます。

## まとめ

この全投稿表示機能により、ユーザーは簡単に他のユーザーの投稿を閲覧し、新しい音楽を発見することができます。また、フォロー機能と組み合わせることで、興味のあるユーザーの投稿を優先的に見ることができ、よりパーソナライズされた体験を提供することができます。

今後の改善点として、ページネーション機能の追加や、ジャンルによるフィルタリング機能の実装などが考えられます。これにより、さらに使いやすいインターフェースを提供することができると考えています。

## 参考リンク
- [Flask でのクエリパラメータの取得](https://flask.palletsprojects.com/en/2.0.x/api/#flask.Request.args)
- [SQLAlchemy でのクエリとフィルタリング](https://docs.sqlalchemy.org/en/14/orm/query.html)
- [Jinja2 テンプレートエンジン](https://jinja.palletsprojects.com/en/3.0.x/)