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

## はじめに

前回の記事では、日々の楽曲投稿フォーム（DailySongForm）について解説しました。今回は、メッセージ機能のためのフォーム、ユーザー認証のためのユーザーローダー、そしてファイルアップロード時の拡張子チェック機能について詳しく説明します。

## MessageFormの定義

MessageFormは、ユーザー間でメッセージをやり取りするためのフォームです。Flask-WTFを使用して以下のように定義します：

```python
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired, Length

class MessageForm(FlaskForm):
    message = StringField('メッセージ', validators=[
                          DataRequired(), Length(min=1, max=500)])
    submit = SubmitField('送信')
```

### MessageFormの解説

1. **message**: メッセージ入力フィールド
   - `StringField`: テキスト入力フィールドを作成
   - `DataRequired()`: 必須入力を指定
   - `Length(min=1, max=500)`: メッセージの長さを1〜500文字に制限

2. **submit**: 送信ボタン
   - `SubmitField`: フォーム送信ボタンを作成

このフォームにより、ユーザーは他のユーザーに500文字以内のメッセージを送ることができます。

## ユーザーローダーの定義

Flask-Loginを使用する際、ユーザーIDからユーザーオブジェクトを取得するためのユーザーローダー関数が必要です。以下のように定義します：

```python
@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))
```

### ユーザーローダーの解説

- `@login_manager.user_loader`: このデコレータは、この関数がユーザーローダーであることをFlask-Loginに伝えます。
- `user_id`: セッションに保存されているユーザーIDが引数として渡されます。
- `User.query.get(int(user_id))`: 指定されたIDを持つユーザーをデータベースから取得します。

この関数により、Flask-Loginはセッションに保存されたユーザーIDを使用して、対応するUserオブジェクトを取得できます。

## ファイル拡張子チェック関数

ユーザーがアップロードしたファイルが許可された拡張子を持っているかをチェックする関数を定義します：

```python
def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in app.config['ALLOWED_EXTENSIONS']
```

### ファイル拡張子チェック関数の解説

- `'.' in filename`: ファイル名にピリオドが含まれているかチェックします。
- `filename.rsplit('.', 1)[1].lower()`: ファイル名を最後のピリオドで分割し、拡張子部分を小文字に変換します。
- `in app.config['ALLOWED_EXTENSIONS']`: 取得した拡張子が許可されたリストに含まれているかチェックします。

この関数は、ファイルアップロード時に呼び出され、許可されていない拡張子を持つファイルのアップロードを防ぎます。

## 使用例

1. MessageFormの使用例：

```python
@app.route('/send_message/<recipient>', methods=['GET', 'POST'])
@login_required
def send_message(recipient):
    form = MessageForm()
    if form.validate_on_submit():
        message = Message(sender=current_user, recipient=recipient, body=form.message.data)
        db.session.add(message)
        db.session.commit()
        flash('メッセージを送信しました。')
        return redirect(url_for('user', username=recipient))
    return render_template('send_message.html', title='メッセージ送信', form=form, recipient=recipient)
```

2. ファイル拡張子チェック関数の使用例：

```python
@app.route('/upload', methods=['POST'])
@login_required
def upload_file():
    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('index'))
    else:
        flash('許可されていないファイル形式です')
        return redirect(request.url)
```

## まとめ

この記事では、以下の3つの重要な機能について解説しました：

1. MessageForm: ユーザー間のメッセージ送信を可能にするフォーム
2. ユーザーローダー: Flask-Loginでユーザー認証を行うための重要な関数
3. ファイル拡張子チェック関数: セキュアなファイルアップロードを実現するための関数

これらの機能を組み合わせることで、ユーザー間のコミュニケーション機能を強化し、セキュアなファイル管理を行うことができます。

## 参考リンク
- [Flask-Login Documentation](https://flask-login.readthedocs.io/)
- [Flask File Uploads](https://flask.palletsprojects.com/en/2.0.x/patterns/fileuploads/)
- [WTForms Field Types](https://wtforms.readthedocs.io/en/2.3.x/fields/)