0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「Busker Beat」制作記:Flask×PostgreSQL×Renderでオブジェクト指向UIを実装

Last updated at Posted at 2025-04-14

はじめに

Busker Beat は路上ライブ演者の「出没情報」を地図上に可視化し、演者が日々の演奏記録をつけ、ファンとつながることができるアプリです。

  • 技術スタック
    • Flask (Python3)
    • PostgreSQL (Renderでホスト)
    • HTML/CSS/JavaScript/Leaflet.js
    • Git/GitHub + Render でデプロイ・管理
    • 完成イメージ: 地図クリックで投稿場所の緯度経度を自動入力 → モーダルからDBへ登録 → 即時マップに反映

アプリ概要

1. 地図(Leaflet.js)

  • 地図上で投稿ポイントを可視化
  • ダークなタイルレイヤーで独特な雰囲気

2. 投稿機能

  • モーダルを使って演者名・投げ銭額・セトリなどを入力
  • マップクリックで緯度経度取得
  • Ajaxで即反映し、リロードなしでマーカーが増える

3. ユーザー認証

  • ログイン・新規登録はモーダルで完結
  • Flaskセッションでログイン状態を管理
  • ログインユーザーだけが投稿可能

UIのコンセプトは「地図が中心」かつ「オブジェクト指向の直感的操作」です。

ディレクトリ構成

busker_beat/
├── myapp.py               # Flaskのエントリポイント
├── requirements.txt
├── static/
│   └── css/
│       └── styles.css     # ダークテーマ+モノトーン+赤
├── templates/
│   ├── index.html
│   ├── _map.html
│   ├── _post_cards.html
│   └── _auth_modal.html              
└── config.py    # DB接続情報
  • _map.html: 地図 + 投稿フォームモーダル
  • _post_cards.html: 投稿一覧(左カラム表示)
  • _auth_modal.html: ログイン/新規登録のモーダル
  • styles.css: 黒背景+赤要素のUIテーマを定義

Flaskのルート処理

以下は主要ルートのみ抜粋。

@app.route("/")
def home():
    conn = psycopg2.connect(...)
    cur = conn.cursor()
    cur.execute("SELECT * FROM bb_posts ORDER BY id DESC;")
    posts = cur.fetchall()
    # postsをテンプレートへ
    return render_template("index.html", posts=posts)

@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == "POST":
        # DBからユーザー取得
        # check_password_hashで照合
        # セッションにuser_idを保存
        return redirect("/")
    return render_template("login.html")

@app.route("/signup", methods=["GET", "POST"])
def signup():
    if request.method == "POST":
        # generate_password_hashでハッシュ化
        # DBにINSERT
        return redirect("/")
    return render_template("signup.html")

@app.route("/logout")
def logout():
    session.clear()
    return redirect("/")

@app.route("/posting", methods=["POST"])
def posting():
    # Ajaxで受け取ったFormDataをDBにINSERT
    # 成功ならJSONで { success: True, ...} 返す
    return jsonify(...)

ポイント:

  • /posting は画面表示しないバックエンド処理
  • /login / /signup はモーダル想定だが、従来の画面遷移にも対応
  • session["user_id"] があるかどうかでログイン判定

PostgreSQLでのデータ管理

DB構成は以下。SQLを管理しやすいようにシンプルに作成。

CREATE TABLE bb_users (
  id SERIAL PRIMARY KEY,
  uname TEXT UNIQUE NOT NULL,
  email TEXT UNIQUE NOT NULL,
  pw TEXT NOT NULL
);

CREATE TABLE bb_posts (
  id SERIAL PRIMARY KEY,
  user_id INTEGER REFERENCES bb_users(id),
  artist_name TEXT,
  lat FLOAT NOT NULL,
  lng FLOAT NOT NULL,
  audience INTEGER,
  setlist TEXT,
  amount INTEGER,
  event_date DATE,
  created_at TIMESTAMP DEFAULT now()
);
  • ハッシュ化されたパスワードを pw に保存(werkzeug.security利用)
  • user_id で bb_users と紐づけ → 投稿者追跡可能
  • lat, lng は FLOAT。地図クリック→AjaxでINSERT

SQLの具体的なINSERT/SELECTはFlaskのコードに埋め込んだ。

レイアウト:オブジェクト指向UI

  1. 地図を画面右側、左に投稿一覧
  2. クリックで投稿: マップオブジェクトそのものに手を触れるイメージ
  3. モーダルを多用し、「画面遷移」を最小限に抑えた

これにより、「オブジェクトを配置する感覚」+「入力フォーム」が直感的にマッチ。

企画図:

ラフ:

Git/GitHub + Renderでのデプロイ

  1. git add . && git commit -m "feat: add map + modals"
  2. git push origin main → Renderが自動デプロイ
  3. Render でDBを作り、環境変数に SECRET_KEY を設定
  4. 公開URL: https://busker-beat.onrender.com

継続的に変更しても、GitHub連携で自動ビルドされるのが便利。

まとめ & 所感

  • Flaskでのルート処理は非常にシンプルに書ける
  • Leafletとの連携で直感的な投稿が実現
  • PostgreSQLでのリレーション管理により、ユーザー認証と投稿がしっかり紐づく
  • オブジェクト指向UI (地図クリック→モーダル投稿→非同期DB保存) は、使ってみるとかなり直感的

今回の制作で学んだのは、シンプルな設計 + UI/UXを意識することで、少ないコード量でも便利なアプリができるということです。

今後の拡張

  • ログインユーザー別のマイページ
  • コメント機能 or いいね機能
  • Tailwind導入でUIをさらに洗練
  • Docker化で開発環境を安定化

もし似たような機能を作りたい人がいれば、ぜひ参考にしてみてください!

以上、「Busker Beat」開発の備忘録でした。

0
0
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
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?