0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Flask + SQLAlchemyで作る音楽共有SNS「DailyBeat」の開発と今後の展望 - コード詳細解説付き

Last updated at Posted at 2024-10-09

はじめに

こんにちは。今回は、FlaskとSQLAlchemyを使用して開発中の「DailyBeat」というWebアプリケーションについて、コードの詳細な解説を交えながら紹介します。このアプリは、ユーザーが毎日1曲を共有し、音楽を通じて他のユーザーとつながることができる音楽特化型のSNSプラットフォームです。

アプリケーションの概要

「DailyBeat」は以下の主要機能を持っています:

  1. ユーザー登録・ログイン
  2. 毎日の楽曲投稿
  3. タイムライン表示
  4. ユーザーフォロー機能
  5. 楽曲再生機能(部分的に実装)

それでは、各機能の実装について詳しく見ていきましょう。

1. ユーザー登録とログイン

ユーザー登録とログイン機能は、Flask-Loginを使用して実装しています。

@app.route('/register', methods=['GET', 'POST'])
def register():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        if User.query.filter_by(username=username).first():
            flash('Username already exists')
        else:
            new_user = User(username=username,
                            password=generate_password_hash(password))
            db.session.add(new_user)
            db.session.commit()
            flash('Registration successful')
            return redirect(url_for('login'))
    return render_template('register.html')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        username = request.form.get('username')
        password = request.form.get('password')
        user = User.query.filter_by(username=username).first()
        if user and check_password_hash(user.password, password):
            login_user(user)
            return redirect(url_for('mypage'))
        flash('Invalid username or password')
    return render_template('login.html')

コード解説

  • register関数:

    • POSTリクエストの場合、フォームからユーザー名とパスワードを取得します。
    • ユーザー名の重複をチェックし、新規の場合はパスワードをハッシュ化して保存します。
    • db.session.add(new_user)で新しいユーザーをセッションに追加し、db.session.commit()でデータベースに反映します。
  • login関数:

    • POSTリクエストの場合、ユーザー名とパスワードを検証します。
    • check_password_hash関数でパスワードを照合し、正しい場合はlogin_user関数でユーザーをログイン状態にします。

2. 毎日の楽曲投稿

ユーザーが毎日1曲を投稿できる機能を実装しています。

@app.route('/post_daily_song', methods=['GET', 'POST'])
@login_required
def post_daily_song():
    form = DailySongForm()
    if form.validate_on_submit():
        today = date.today()
        DailySong.query.filter_by(
            user_id=current_user.id, date_posted=today, is_current=True).update({'is_current': False})
        new_song = DailySong(
            user_id=current_user.id,
            title=form.title.data,
            artist=form.artist.data,
            genre=form.genre.data,
            music_url=form.music_url.data,
            is_current=True
        )
        db.session.add(new_song)
        db.session.commit()
        flash('今日の1曲を投稿しました!', 'success')
        return redirect(url_for('mypage'))
    return render_template('post_daily_song.html', form=form)

コード解説

  • @login_requiredデコレータにより、ログインユーザーのみがアクセスできるようになっています。
  • form.validate_on_submit()でフォームのバリデーションを行います。
  • 既存の今日の投稿がある場合、is_currentフラグをFalseに更新します。
  • 新しいDailySongオブジェクトを作成し、データベースに追加します。
  • flash関数で成功メッセージを設定し、マイページにリダイレクトします。

3. タイムライン表示

ユーザーの投稿を時系列順に表示するタイムライン機能を実装しています。

@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)

コード解説

  • request.args.get('sort', 'desc')でURLパラメータから並び順を取得します。デフォルトは降順です。
  • DailySong.query.order_by()で投稿を日付順にソートします。
  • ログインユーザーの場合、フォローしているユーザーの投稿も取得します。
  • DailySong.user_id.in_(followed_users)で、フォローしているユーザーの投稿をフィルタリングします。

4. ユーザーフォロー機能

ユーザー同士のつながりを強化するためのフォロー機能を実装しています。

@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))

@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))

コード解説

  • @login_requiredデコレータで、ログインユーザーのみがアクセスできるようにしています。
  • User.query.filter_by(username=username).first()で対象ユーザーを取得します。
  • 自分自身をフォロー/アンフォローしようとした場合はエラーメッセージを表示します。
  • current_user.follow(user)current_user.unfollow(user)メソッドで、フォロー関係を更新します。
  • 変更をデータベースに反映するためにdb.session.commit()を呼び出しています。

5. 楽曲再生機能(部分的に実装)

現在、楽曲再生機能は部分的に実装されています。Spotify APIを使用してアルバムアートを取得する機能が実装されていますが、実際の楽曲再生機能は未実装です。

def get_album_art(artist, title):
    client_id = 'your_spotify_client_id'
    client_secret = 'your_spotify_client_secret'

    auth_response = requests.post('https://accounts.spotify.com/api/token', {
        'grant_type': 'client_credentials',
        'client_id': client_id,
        'client_secret': client_secret,
    })
    auth_response_data = auth_response.json()
    access_token = auth_response_data['access_token']

    headers = {
        'Authorization': f'Bearer {access_token}'
    }
    params = {
        'q': f'artist:{artist} track:{title}',
        'type': 'track',
        'limit': 1
    }
    response = requests.get(
        'https://api.spotify.com/v1/search', headers=headers, params=params)
    response_data = response.json()

    if 'tracks' in response_data and response_data['tracks']['items']:
        return response_data['tracks']['items'][0]['album']['images'][0]['url']
    else:
        return None

@app.route('/get_album_art/<artist>/<title>')
def get_album_art_api(artist, title):
    album_art_url = get_album_art(artist, title)
    return jsonify({'album_art_url': album_art_url})

コード解説

  • get_album_art関数:

    • Spotify APIの認証トークンを取得します。
    • 楽曲の検索クエリを作成し、APIにリクエストを送信します。
    • レスポンスから、アルバムアートのURLを抽出して返します。
  • get_album_art_api関数:

    • URLパラメータからアーティスト名と曲名を受け取ります。
    • get_album_art関数を呼び出してアルバムアートのURLを取得します。
    • 結果をJSONで返します。

今後の展望

DailyBeatの開発は現在進行中であり、以下の機能の実装や改善を計画しています:

  1. 楽曲再生機能の完全実装
  2. ユーザープロフィールページの充実
  3. 楽曲推薦システムの導入
  4. モバイルアプリ版の開発
  5. ソーシャルシェア機能の強化

これらの機能を追加することで、ユーザーエクスペリエンスをさらに向上させ、より魅力的な音楽共有プラットフォームを目指しています。

まとめ

「DailyBeat」は、FlaskとSQLAlchemyを使用して構築された音楽共有SNSです。現在のバージョンでは、ユーザー登録、ログイン、楽曲投稿、タイムライン表示、フォロー機能が実装されており、部分的な楽曲情報取得機能も含まれています。

このプロジェクトでは、以下のような技術的なポイントが特徴的です:

  1. Flask-Loginを使用した安全なユーザー認証
  2. SQLAlchemyを活用した効率的なデータベース操作
  3. フォーム処理とバリデーション(WTFormsを使用)
  4. 外部API(Spotify)との連携

今後は、楽曲再生機能の完全実装や新機能の追加を通じて、ユーザーにとってより魅力的なプラットフォームを目指して開発を続けていきます。

このプロジェクトは、Pythonの強力な機能やFlaskのシンプルさを活かしつつ、SQLAlchemyを使用した効率的なデータベース操作を実現しています。特に、日々の楽曲投稿やフォロー機能の実装は、実際のSNSの基本的な機能を網羅しており、学習教材としても有用です。

皆さんも、自分のプロジェクトでFlaskとSQLAlchemyを使ってこのような機能豊富なWebアプリケーションを構築する際の参考にしていただければ幸いです。

参考リンク

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?