py程式設計(三)課程網站
第 5 週 · 單元 2-1 · 示範專案

台南吃貨評分站(下):讓使用者寫入資料,再用測試證明它是對的

上週的功能只會「讀」資料庫,今天要開始「寫」。寫入是網站最容易出錯、也最容易被攻擊的地方,所以今天的重點是驗收。

W5吃貨站寫入+測試

本週結束時你會

今天的時間安排

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:新增評論【跟著做】

提示詞 1 Antigravity
請先讀 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 依驗收標準寫測試【跟著做】

到目前為止都是用手測。手測很慢,而且每次改程式都要重測一次。自動測試讓電腦幫你每次都檢查全部項目。

提示詞 2 Antigravity
請先讀 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):

提示詞 3 Antigravity
推上 GitHub。儲存庫名稱 foodie,選 Public。
推送前請特別確認 .venv、instance/、__pycache__ 與資料庫檔都沒有被追蹤。

為什麼這週還不連 Vercel:吃貨站現在用的是 SQLite,Vercel 主機上的檔案是暫時的,評論寫進去過一陣子就會消失。第 8 週學會把資料庫換成雲端的 Neon 之後,記帳網站和吃貨站都會正式上線。

專案回顧

打開專案工具包的「專案回顧」,花 10 分鐘填寫。最重要的兩題:

  1. 這個專案中,AI 犯過的錯誤裡,哪一個你差點沒發現?你是怎麼發現的?
  2. 下一個專案(記帳網站),你會在 PROJECT.md 裡多寫什麼,讓 AI 少犯錯?

本週檢核

第 6 週預告:專案一「記帳網站」。這次六步都由你自己帶著 AI 完成,老師只提供需求。