台南吃貨評分站(下):讓使用者寫入資料,再用測試證明它是對的
上週的功能只會「讀」資料庫,今天要開始「寫」。寫入是網站最容易出錯、也最容易被攻擊的地方,所以今天的重點是驗收。
本週結束時你會
今天的時間安排
| 0:00–0:10 | 回顧:讀取(SELECT)與寫入(INSERT)的差別;表單 GET 與 POST |
| 0:10–0:50 | 功能 3【跟著做】新增評論:建置、用 6 種錯誤輸入對答案 |
| 0:50–1:20 | 功能 4【自己做】新增店家 |
| 1:20–1:30 | 休息 |
| 1:30–2:05 | 步驟 ⑤ 驗收【跟著做】讓 AI 依驗收標準寫測試 |
| 2:05–2:30 | 推上 GitHub、專案回顧、本週檢核 |
功能 3:新增評論【跟著做】
請先讀 PROJECT.md。現在只做這一條功能:「新增評論」。 1. 在店家頁加入評論表單:暱稱(必填,最多 20 字)、評分(下拉選單 1–5)、短評(選填,最多 100 字) 2. 表單用 POST 送到 /shop/<id>/review 3. 伺服器端一定要檢查(不能只靠網頁表單的 required):暱稱不可空白;評分必須是 1–5 的整數;短評不超過 100 字 4. 有錯誤時:回應狀態碼 400,在同一頁顯示中文錯誤訊息,保留使用者已輸入的內容,而且不寫入資料庫 5. 成功時:寫入資料庫,再重新導向回店家頁(避免重新整理時重複送出) 6. SQL 用 ? 參數 只修改 app.py 與 templates/shop.html。完成後告訴我要怎麼手動測試。
為什麼伺服器一定要再檢查一次:網頁上的 required、下拉選單,只要打開瀏覽器的開發者工具就能繞過。真正擋得住的,只有伺服器端的檢查和資料庫的 CHECK。
對答案:在派森牛肉湯(/shop/1)測試
| 輸入 | 正確結果 |
|---|---|
| 暱稱「測試員」、5 星、短評「好吃」 | 成功,回到店家頁,變成「4.7(7 則評論)」,第一則是測試員 |
| 暱稱空白(或只有空格) | 顯示「請輸入暱稱」,評論數不變 |
| 短評 101 個字 | 顯示「短評最多 100 個字」,評論數不變 |
| 評分 0、6、abc、3.5(用下方方法送出) | 顯示「評分必須是 1 到 5 的整數」,評論數不變 |
| 送出後按 F5 重新整理 | 不會多出一筆重複的評論 |
怎麼送出下拉選單沒有的評分?
在評分選單上按右鍵 →「檢查」,把其中一個 <option value="5"> 的 5 改成 6,再選它送出。這就是使用者繞過網頁限制的方式,也是伺服器必須自己檢查的原因。
巡堂時最常見的錯誤
評分 3.5 被接受:AI 用 float() 或 int(float()) 轉型。要求整數就要先檢查是不是整數字串。
錯誤時表單內容消失:使用者打了一大段短評,一個欄位填錯就全部重打。要求 AI 把已輸入的內容帶回表單。
重新整理會重複新增:成功後沒有 redirect。這叫做 Post/Redirect/Get 模式。
完成後測完資料,執行 flask --app app init-db 讓資料庫回到 49 則評論,再存檔:「功能3:新增評論」。
功能 4:新增店家【自己做】
頁面 /shop/new(導覽列放一個「+ 新增店家」按鈕) - 欄位:店名(必填)、類別(五選一)、地址(選填) - 店名已存在時顯示「這家店已經在名單中了」 - 類別不是五個之一時顯示「請選擇類別」 - 成功後導向新店家的店家頁
| 輸入 | 正確結果 |
|---|---|
| 「測試咖哩」、飯 | 導向新店家頁,首頁排行榜最後多一間「尚無評分」 |
| 「派森牛肉湯」、小吃 | 顯示已經在名單中,不新增 |
| 店名空白 | 顯示請輸入店名 |
存檔:「功能4:新增店家」。
步驟 ⑤ 驗收:讓 AI 依驗收標準寫測試【跟著做】
到目前為止都是用手測。手測很慢,而且每次改程式都要重測一次。自動測試讓電腦幫你每次都檢查全部項目。
請先讀 PROJECT.md 的「5. 驗收標準」,以及本週的兩條新功能。 請用 pytest 與 Flask 的 test_client,在 tests/test_app.py 寫自動測試: 1. 每個測試使用獨立的暫存資料庫(tmp_path),並先執行 init-db 的建表與示範資料,不可以動到 instance/foodie.db 2. 至少涵蓋:排行榜前兩名的順序、沒有評論的店顯示「尚無評分」、類別篩選、店家頁 404、新增評論成功、評分 0/6/空白/abc/3.5 都被擋下且不寫入、暱稱空白、短評超過 100 字、新增重複店家 3. 每個測試函式上方用一行中文註解說明它對應哪一條驗收標準 安裝 pytest,執行 python -m pytest -q,把結果給我看。不要為了讓測試通過而修改 app.py;如果有測試失敗,先告訴我原因。
參考解答共有 17 項測試,全部通過。你的測試數量不必相同,但上面列出的每一種情況都要涵蓋。
最後一句很重要:AI 有時會為了讓測試通過,偷偷改掉程式或改掉測試的預期值。測試失敗時,先搞清楚是程式錯了還是測試錯了,再決定改哪一邊。
參考解答在 參考解答/foodie/tests/test_app.py。製作時有一項測試一開始失敗:SQL 注入測試用的字串超過暱稱的 20 字上限,先被長度檢查擋下了,問題出在測試本身,不是程式。這個真實案例很適合拿來說明「測試失敗時先找原因」。
步驟 ⑥ 推上 GitHub
第 1 週已經完成 GitHub CLI 登入,這次直接請 AI 建立儲存庫並推送(使用 github-publish skill):
推上 GitHub。儲存庫名稱 foodie,選 Public。 推送前請特別確認 .venv、instance/、__pycache__ 與資料庫檔都沒有被追蹤。
為什麼這週還不連 Vercel:吃貨站現在用的是 SQLite,Vercel 主機上的檔案是暫時的,評論寫進去過一陣子就會消失。第 8 週學會把資料庫換成雲端的 Neon 之後,記帳網站和吃貨站都會正式上線。
專案回顧
打開專案工具包的「專案回顧」,花 10 分鐘填寫。最重要的兩題:
- 這個專案中,AI 犯過的錯誤裡,哪一個你差點沒發現?你是怎麼發現的?
- 下一個專案(記帳網站),你會在 PROJECT.md 裡多寫什麼,讓 AI 少犯錯?
本週檢核
第 6 週預告:專案一「記帳網站」。這次六步都由你自己帶著 AI 完成,老師只提供需求。