社團活動報名平台(上):多張資料表與登入
第一次做「不同的人能做不同的事」的網站。本週完成規劃、三張資料表、註冊登入,以及報名與名額控制。
本週結束時你會
今天的時間安排
| 0:00–0:20 | 讀需求說明書;觀念:使用者、角色、權限 |
| 0:20–0:50 | 【自己做】提案書、時程、PROJECT.md 前三段 |
| 0:50–1:20 | 【跟著做】建立專案、三張資料表、示範資料 |
| 1:20–1:30 | 休息 |
| 1:30–2:00 | 功能 1【跟著做】註冊與登入 |
| 2:00–2:30 | 功能 2【自己做】活動列表、報名與取消;週誌 |
三張資料表,兩種關聯
users 使用者 id · username(不可重複)· password_hash · role(member/officer)
events 活動 id · title · held_on · capacity
signups 報名紀錄 id · event_id → events.id · user_id → users.id · created_at
UNIQUE (event_id, user_id) ← 同一人不能重複報名同一場
一個人可以報名很多場活動,一場活動也有很多人報名,這叫多對多。多對多一定要有一張「中間表」(signups)記錄誰報了哪一場。
常見錯誤
在 events 表放一個「報名者」欄位,存「阿明,小美,阿哲」這樣的文字。要算人數、要判斷重複、要刪除某一人,都會變得很麻煩又容易出錯。
正確做法
每一筆報名是 signups 表的一列。人數用 COUNT,重複由 UNIQUE 擋下,取消就是 DELETE 一列。
學生的 PROJECT.md 若沒有中間表,請他用「阿明報了兩場、工作坊有三個人」畫出資料,自己會發現一個欄位裝不下。參考解答在 practice/s3-4-1_社團報名平台/參考解答/club-signup/。
建立專案【跟著做】
請先讀 PROJECT.md 與需求說明書。建立 Flask 專案骨架: 1. 沿用記帳網站的做法:db.py 依 DATABASE_URL 切換 SQLite/Postgres,schema_sqlite.sql 與 schema_postgres.sql 分開 2. 三張資料表 users、events、signups;signups 用 UNIQUE (event_id, user_id) 防止重複報名;SQLite 要開啟 PRAGMA foreign_keys 3. flask --app app init-db:建表並放入需求說明書的示範帳號與活動。密碼必須用 werkzeug 的 generate_password_hash 雜湊後再存 4. app.secret_key 從環境變數 SECRET_KEY 讀取;本機沒有設定時才使用開發用的預設值 完成後用 SQL 告訴我:四場活動各有幾人報名,以及 users 表中阿明的 password_hash 長什麼樣子。
| 檢查 | 正確結果 |
|---|---|
| 報名人數 | 社員大會 2、工作坊 3、校外參訪 1、成果展 0 |
| 阿明的 password_hash | 一長串以演算法名稱開頭的文字(例如 scrypt:…),不是 member123 |
為什麼密碼要雜湊:雜湊是單向的,可以驗證「輸入的密碼對不對」,但無法從雜湊值反推出密碼。就算資料庫外洩,攻擊者也拿不到密碼本身。
功能 1:註冊與登入【跟著做】
請先讀 PROJECT.md。現在只做「註冊、登入、登出」: 1. /register:暱稱 2–20 字且不可重複、密碼至少 8 字元、兩次輸入要相同;錯誤時顯示中文訊息 2. /login:成功後把 user_id 存進 session;失敗時只顯示「暱稱或密碼錯誤」,不透露是哪一個錯 3. 登出必須用 POST 4. 寫一個 login_required 裝飾器:未登入時導到登入頁,登入後回到原本的頁面(只接受站內網址,避免被導到外部網站) 5. 導覽列顯示目前登入的暱稱,幹部加註「(幹部)」 只修改 app.py 與 templates/。
| 操作 | 正確結果 |
|---|---|
| 阿明/member123 登入 | 導覽列顯示「阿明」 |
| 阿明/錯誤密碼 | 「暱稱或密碼錯誤」 |
| 註冊暱稱「阿明」 | 已經有人使用 |
| 註冊密碼 short | 至少 8 個字元 |
| 小派社長登入 | 導覽列顯示「小派社長(幹部)」 |
巡堂重點
密碼存成明碼:打開 DB Browser 看 users 表最清楚。請學生要求 AI 改用雜湊,並重新 init-db。
登入後跳到外部網站:如果 next 參數沒有檢查,/login?next=https://壞網站 會把剛登入的使用者送去釣魚頁。這叫「開放式重新導向」。
存檔並推上 GitHub:「功能1:註冊與登入」。
功能 2:活動列表、報名與取消【自己做】
自己寫提示詞。需求:首頁列出活動與剩餘名額;活動頁依狀態顯示「登入後才能報名」「我要報名」「已額滿」或「取消報名」;報名與取消都用 POST 並要求登入;寫入前在伺服器再檢查一次名額;重複報名由資料庫的 UNIQUE 擋下並顯示友善訊息。
| 操作 | 正確結果 |
|---|---|
| 首頁 | 社員大會剩 28 位、工作坊「額滿」、校外參訪剩 1 位、成果展剩 50 位 |
| 未登入按報名 | 導到登入頁,登入後回到該活動 |
| 豆豆報名工作坊 | 按鈕停用;就算用開發者工具送出,也顯示已額滿 |
| 豆豆報名校外參訪 | 成功,已報名 2 人,變成額滿 |
| 豆豆再送出一次報名 | 「你已經報名過了」,仍是 2 人 |
| 豆豆取消報名 | 變回 1 人 |
兩個人同時搶最後一個名額:如果兩人在同一瞬間按下報名,兩邊都先查到「還有 1 位」,就會超收。參考解答在 Postgres 上用 SELECT … FOR UPDATE 鎖住那場活動,讓第二個人等第一個人寫完再檢查。這是真實系統常見的問題,可以請 AI 解釋給你聽。
存檔並推上 GitHub:「功能2:報名與取消」。