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

台南吃貨評分站(上):建立專案,打通前兩條功能

第一次完整走一遍六步建站法。前三步的成果已經在 PROJECT.md 裡;今天從第 ④ 步開始,一次一條功能,做出排行榜與店家頁。

W4吃貨站Flask+SQLite

本週結束時你會

今天的時間安排

0:00–0:10先看成品長什麼樣子;回顧 PROJECT.md 與上週的排行榜 SQL
0:10–0:45步驟 0【跟著做】建立專案、虛擬環境、初始化資料庫
0:45–1:15功能 1【跟著做】首頁排行榜:建置、對答案、存檔
1:15–1:25休息
1:25–2:10功能 2【自己做】店家頁
2:10–2:30更新 PROJECT.md 與專案週誌、本週檢核

每一條功能都走同一套小流程

  1. 請 AI 先讀 PROJECT.md,再說明這條功能要改哪些檔案。
  2. 建置:只做這一條功能,從路由、SQL 到頁面。
  3. 對答案:用下方的標準答案逐項比對,不對就請 AI 說明原因再修。
  4. 存檔:Git commit,並更新 PROJECT.md 的功能進度。

口訣:對過答案 → 存檔 → 再做下一條。之後 AI 不管怎麼改壞,都能回到這一版。

專案長這樣

程式設計三/
└─ foodie/                 ← 用 Antigravity 開啟這個資料夾
   ├─ AGENTS.md            課程規則(課程套件)
   ├─ .agents/skills/      存檔、推上 GitHub、上線前檢查(課程套件)
   ├─ PROJECT.md           專案說明書(複製第 2 週的範例)
   ├─ app.py               Flask 程式:路由與資料庫查詢
   ├─ schema.sql           建表 SQL(課程提供)
   ├─ seed.sql             示範資料:12 間店、49 則評論(課程提供)
   ├─ templates/           頁面:base.html、index.html、shop.html …
   ├─ instance/foodie.db   資料庫檔(由程式產生,不放上 GitHub)
   ├─ tests/               自動測試(第 5 週)
   └─ requirements.txt

步驟 0:建立專案【跟著做】

  1. 在 程式設計三 底下建立 foodie 資料夾,用 Antigravity 開啟。
  2. 從課程資源複製 schema.sql、seed.sql、PROJECT_範例_吃貨評分站.md 進來,並把最後一個改名成 PROJECT.md。
  3. 把 Antigravity 課程套件的 AGENTS.md 與 .agents 資料夾複製進來。之後說「存檔」「推上 GitHub」就會使用對應的 skill。
  4. 在 AI 對話框貼上:
提示詞 0 Antigravity
請先完整閱讀 PROJECT.md。這是一個 Flask + SQLite 網站,目前資料夾已有 schema.sql 與 seed.sql。
請建立專案骨架,先不要做任何頁面功能:
1. 建立虛擬環境 .venv,安裝 Flask,產生 requirements.txt
2. 建立 app.py:資料庫檔放在 instance/foodie.db;寫一個 get_db() 讓每個請求共用連線,查詢結果可以用欄位名稱取值,並開啟 PRAGMA foreign_keys
3. 加一個指令 flask --app app init-db:執行 schema.sql 與 seed.sql
4. 建立 .gitignore,排除 .venv、instance/、__pycache__
5. git init 並 commit,訊息「建立專案骨架」
完成後執行 init-db,並用一行 SQL 告訴我 shops 與 reviews 各有幾筆。
應該看到shops 12 筆、reviews 49 筆。數字不對,代表 seed.sql 沒有完整執行。
常見狀況:Windows 無法啟動虛擬環境

PowerShell 出現「因為這個系統上已停用指令碼執行」:在終端機執行一次 Set-ExecutionPolicy -Scope CurrentUser RemoteSigned,輸入 Y 確認,再重新啟動虛擬環境。這只影響你自己的帳號。

Antigravity 會在建立虛擬環境、安裝套件、執行 git 之前詢問。示範時刻意停下來,念出它要執行的指令,讓學生養成「看清楚再按繼續」的習慣。

功能 1:首頁排行榜【跟著做】

提示詞 1 Antigravity
請先讀 PROJECT.md。現在只做這一條功能:「首頁排行榜」。
1. 路由 /:列出所有店家的店名、類別、平均分數(四捨五入到小數 1 位)、評論數
2. 排序:平均分數高到低;同分時評論數多的在前;沒有評論的店排最後,平均分數顯示「尚無評分」
3. 網址參數 ?category=麵 時只顯示該類別;上方提供「全部、飯、麵、小吃、飲料、甜點」的篩選連結,目前的類別要標示出來
4. 頁面用 Jinja 範本:templates/base.html(共用版型,引入 Pico.css)與 templates/index.html
5. SQL 一律用 ? 參數,不可以用字串拼接
只修改 app.py 與 templates/。完成後啟動網站告訴我網址,並說明你的排行榜 SQL 每一行在做什麼。

對答案

檢查正確結果
第 1、2 名派森牛肉湯(4.7,6 則)、函式炒鱔魚(4.7,3 則)
第 4、5 名字典虱目魚粥(4.5,6 則)、迴圈碗粿(4.5,4 則)
最後一名模組肉燥飯,顯示「尚無評分」,評論數 0
店家總數12
點「麵」函式炒鱔魚、縮排鍋燒意麵、變數擔仔麵,網址變成 /?category=麵
巡堂時最常見的錯誤

只有 11 間店:AI 用了 JOIN 而不是 LEFT JOIN,沒有評論的店消失了。請學生把上週的實作 2 拿出來對照。

派森牛肉湯和函式炒鱔魚順序相反:AI 依四捨五入後的 4.7 排序,再依店名排,忽略了「同分時評論數多者優先」。要求 AI 依照 PROJECT.md 的驗收標準修正。

SQL 用 f-string 組字串:例如 f"WHERE category = '{category}'"。這會造成 SQL 注入漏洞,請學生要求 AI 改成 ? 參數。

提示詞 2:存檔 Antigravity · git-save
排行榜已對過答案,請存檔,訊息「功能1:首頁排行榜」,並把 PROJECT.md 功能進度中的「首頁列出店家」「依類別篩選」「排行榜排序」打勾。

功能 2:店家頁【自己做】

這次沒有提示詞可以複製。照著功能 1 的提示詞結構自己寫,需求如下:

路由 /shop/<id>
- 顯示店名、類別、地址
- 平均分數(小數 1 位)與評論數,例如「★ 4.7(6 則評論)」;沒有評論時顯示「尚無評分」
- 所有評論由新到舊:暱稱、星星、短評、時間
- 店家不存在時回應 404,顯示「找不到這個頁面或店家」
- 首頁的店名要能點進來

記得寫上:「請先讀 PROJECT.md」「只做這一條功能」「只修改 app.py 與 templates/」「SQL 用 ? 參數」。

對答案

網址正確結果
/shop/1派森牛肉湯,★ 4.7(6 則評論),第一則是「小林」4 星「價格合理」
/shop/12模組肉燥飯,「尚無評分」,沒有評論
/shop/999404,顯示找不到
/shop/abc404(不是程式錯誤畫面)

全部正確後存檔:「功能2:店家頁」。

/shop/abc 是刻意加的。若 AI 用 <id> 而非 <int:id>,會在 SQL 查不到後回傳 404,結果碰巧正確;若它直接 int(id) 轉型,則會出現 500 錯誤。兩種寫法都讓學生看看,討論「讓框架先擋下錯誤輸入」的好處。

收尾:更新文件

  1. 確認 PROJECT.md 的功能進度已打勾。
  2. 在專案工具包的「每週進度紀錄」寫下:完成了什麼、AI 犯了什麼錯、下週要做什麼。

本週檢核

第 5 週預告:讓使用者寫評論、新增店家,並用自動測試證明每一條驗收標準都做到了。