Flask + SQLAlchemyで音楽共有SNSを作る - ユーザー機能編

## はじめに

前回の記事では、基本的なルーティング（トップページ、ログイン、新規登録）について解説しました。今回は、ログイン後のユーザー固有の機能、具体的にはマイページ、ログアウト、ファイルアップロードの実装について詳しく説明します。

## マイページの実装

まず、ユーザーのマイページを表示する機能を実装します：

```python
from flask import render_template
from flask_login import login_required, current_user
from datetime import date

@app.route('/mypage')
@login_required
def mypage():
    today = date.today()
    daily_song = DailySong.query.filter_by(
        user_id=current_user.id, date_posted=today, is_current=True).first()
    past_posts = DailySong.query.filter_by(user_id=current_user.id).order_by(
        DailySong.date_posted.desc()).limit(20).all()
    return render_template('mypage.html', user=current_user, daily_song=daily_song, past_posts=past_posts)
```

### 解説
- `@login_required`: このデコレータは、ログインしているユーザーのみがこのページにアクセスできるようにします。
- `date.today()`: 今日の日付を取得します。
- `DailySong.query...`: 今日の投稿と過去20件の投稿を取得します。
- `render_template()`: マイページのテンプレートをレンダリングし、必要なデータを渡します。

## ログアウト機能の実装

次に、ログアウト機能を実装します：

```python
from flask import redirect, url_for
from flask_login import login_required, logout_user

@app.route('/logout')
@login_required
def logout():
    logout_user()
    return redirect(url_for('index'))
```

### 解説
- `@login_required`: ログインしているユーザーのみがログアウトできるようにします。
- `logout_user()`: ユーザーをログアウト状態にします。
- `redirect(url_for('index'))`: ログアウト後、トップページにリダイレクトします。

## ファイルアップロード機能の実装

最後に、ファイルアップロード機能を実装します：

```python
import os
from flask import request, flash
from werkzeug.utils import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
@login_required
def upload_file():
    if request.method == 'POST':
        if 'file' not in request.files:
            flash('ファイルがありません')
            return redirect(request.url)
        file = request.files['file']
        if file.filename == '':
            flash('ファイルが選択されていません')
            return redirect(request.url)
        if file and allowed_file(file.filename):
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            flash('ファイルがアップロードされました')
            return redirect(url_for('mypage'))
    return render_template('upload.html')
```

### 解説
- `request.files`: アップロードされたファイルを取得します。
- `allowed_file()`: ファイルの拡張子が許可されているかチェックします（この関数は別途定義が必要です）。
- `secure_filename()`: ファイル名を安全なものに変換します。
- `file.save()`: ファイルを指定されたディレクトリに保存します。

## セキュリティに関する注意点

1. アクセス制御: `@login_required` デコレータを使用して、認証されたユーザーのみがアクセスできるようにしています。

2. ファイルアップロードのセキュリティ:
   - ファイルの拡張子チェック
   - `secure_filename()` を使用して安全なファイル名に変換
   - アップロードされたファイルの保存先を適切に設定

3. CSRF対策: Flask-WTFを使用している場合、フォームにCSRFトークンを含めることを忘れないでください。

4. 入力のバリデーション: ファイルアップロードに関しては、サイズ制限やコンテンツタイプの確認なども考慮する必要があります。

## まとめ

この記事では、以下の3つのユーザー固有の機能について解説しました：

1. マイページ: ユーザーの個人ページを表示
2. ログアウト: ユーザーをログアウト状態にする
3. ファイルアップロード: ユーザーがファイルをアップロードできる機能

これらの機能を実装することで、ユーザーは自分の投稿を管理し、新しいコンテンツをアップロードすることができます。適切にセキュリティ対策を施すことで、安全で使いやすいアプリケーションを構築することができます。

## 参考リンク
- [Flask-Login ドキュメント](https://flask-login.readthedocs.io/en/latest/)
- [Flask でのファイルアップロード](https://flask.palletsprojects.com/en/2.0.x/patterns/fileuploads/)
- [Werkzeug ユーティリティ関数](https://werkzeug.palletsprojects.com/en/2.0.x/utils/)