Flask + SQLAlchemyで音楽共有SNSを作る - フォロー/アンフォロー機能編

## はじめに

前回の記事では、全投稿表示機能について解説しました。今回は、ユーザー間のつながりを強化するフォロー機能とアンフォロー機能の実装について詳しく説明します。

## フォロー機能の実装

まず、ユーザーをフォローする機能を実装します：

```python
from flask import flash, redirect, url_for
from flask_login import login_required, current_user
from app import app, db
from app.models import User

@app.route('/follow/<username>')
@login_required
def follow(username):
    user = User.query.filter_by(username=username).first()
    if user is None:
        flash('ユーザーが見つかりません。')
        return redirect(url_for('index'))
    if user == current_user:
        flash('自分自身をフォローすることはできません。')
        return redirect(url_for('user', username=username))
    current_user.follow(user)
    db.session.commit()
    flash(f'{username} をフォローしました。')
    return redirect(url_for('user', username=username))
```

### 解説

1. **ルーティングとアクセス制御**:
   - `@app.route('/follow/<username>')`で、フォローするユーザー名をURLパラメータとして受け取ります。
   - `@login_required`デコレータにより、ログインユーザーのみがアクセスできます。

2. **ユーザーの取得と検証**:
   - `User.query.filter_by(username=username).first()`で、フォロー対象のユーザーを取得します。
   - ユーザーが存在しない場合や、自分自身をフォローしようとした場合はエラーメッセージを表示し、適切なページにリダイレクトします。

3. **フォロー処理**:
   - `current_user.follow(user)`で、現在のユーザーが対象ユーザーをフォローします。
   - `db.session.commit()`で、変更をデータベースに保存します。

4. **フィードバックとリダイレクト**:
   - フラッシュメッセージでフォロー成功を通知します。
   - フォローしたユーザーのプロフィールページにリダイレクトします。

## アンフォロー機能の実装

次に、ユーザーのフォローを解除する機能を実装します：

```python
@app.route('/unfollow/<username>')
@login_required
def unfollow(username):
    user = User.query.filter_by(username=username).first()
    if user is None:
        flash('ユーザーが見つかりません。')
        return redirect(url_for('index'))
    if user == current_user:
        flash('自分自身をアンフォローすることはできません。')
        return redirect(url_for('user', username=username))
    current_user.unfollow(user)
    db.session.commit()
    flash(f'{username} のフォローを解除しました。')
    return redirect(url_for('user', username=username))
```

### 解説

アンフォロー機能の実装は、フォロー機能とほぼ同じ構造を持っています。主な違いは以下の点です：

- ルーティングが`/unfollow/<username>`になっています。
- `current_user.unfollow(user)`を呼び出してフォローを解除します。
- フラッシュメッセージの内容が「フォローを解除しました」に変更されています。

## セキュリティとユーザビリティの考慮点

1. **アクセス制御**: `@login_required`デコレータにより、認証されたユーザーのみがフォロー/アンフォロー操作を行えます。

2. **エラー処理**: 
   - 存在しないユーザーへの操作を防ぐ
   - 自分自身へのフォロー/アンフォロー操作を防ぐ

3. **ユーザーフィードバック**: フラッシュメッセージにより、操作の結果をユーザーに明確に伝えています。

4. **UX考慮**: 操作後、関連するユーザーのプロフィールページにリダイレクトすることで、操作結果をすぐに確認できます。

## フロントエンド実装の例

フォロー/アンフォローボタンをユーザープロフィールページに実装する例：

```html
{% if current_user.is_authenticated and user != current_user %}
    {% if current_user.is_following(user) %}
        <a href="{{ url_for('unfollow', username=user.username) }}" class="btn btn-secondary">フォロー解除</a>
    {% else %}
        <a href="{{ url_for('follow', username=user.username) }}" class="btn btn-primary">フォローする</a>
    {% endif %}
{% endif %}
```

このコードは、ログインユーザーが他のユーザーのプロフィールページを見ている場合にのみフォロー/アンフォローボタンを表示し、現在のフォロー状態に応じてボタンの表示を切り替えます。

## まとめ

フォロー/アンフォロー機能の実装により、ユーザー間のつながりを強化し、アプリケーション内でのソーシャルネットワークを構築することができます。この機能は以下の利点をもたらします：

1. ユーザーエンゲージメントの向上
2. パーソナライズされたコンテンツフィードの提供
3. ユーザー間のコミュニティ形成の促進

## 参考リンク
- [Flask-Login ドキュメント](https://flask-login.readthedocs.io/)
- [SQLAlchemy リレーションシップ](https://docs.sqlalchemy.org/en/14/orm/basic_relationships.html)
- [Flask フラッシュメッセージング](https://flask.palletsprojects.com/en/2.0.x/patterns/flashing/)