Day 11 已經完成 Flask 和 MySQL 的連線。
目前資料庫裡有:
ai_task_manager
├── categories
└── tasks
但 Day 10 的新增任務功能還只是把資料送到 Flask,沒有真正保存。
所以 Day 12 的目標很單純:
把新增任務表單真正寫進 MySQL。
這次先不碰首頁顯示、修改、刪除、篩選。
我先把目前狀態交給 Claude Code:
目前狀態:
- Flask 已經可以成功連上 MySQL
- 已有 categories、tasks 兩張表
- /tasks/new 已經可以收到表單資料
- 目前還沒有把任務寫進資料庫
今天只做:
1. 建立 Category Model
2. 建立 Task Model
3. status 改成 todo / in_progress / done
4. category 用中文名稱查 Category
5. 取得 category_id
6. 建立 Task
7. db.session.add()
8. db.session.commit()
9. 成功後 redirect 回 /tasks/new
今天不要:
- 修改首頁任務列表
- 做編輯
- 做刪除
- 做篩選
- 做分類 CRUD
- 加入 Claude API
- 建立 services/
- 建立 routes/
確認規劃沒問題後,再開始修改。
新增:
models/
├── category.py
└── task.py
修改:
app.py
templates/add_task.html
schema.sql
目前專案變成:
models/category.py:
from db import db
class Category(db.Model):
__tablename__ = "categories"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), unique=True, nullable=False)
color = db.Column(db.String(7), default="#6c757d")
這個 Model 對應 MySQL 裡的:
categories
models/task.py:
from db import db
class Task(db.Model):
__tablename__ = "tasks"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(200), nullable=False)
description = db.Column(db.Text, nullable=True)
status = db.Column(
db.Enum("todo", "in_progress", "done"),
nullable=False,
default="todo"
)
category_id = db.Column(
db.Integer,
db.ForeignKey("categories.id"),
nullable=True
)
due_date = db.Column(db.Date, nullable=True)
created_at = db.Column(
db.DateTime,
server_default=db.func.now()
)
updated_at = db.Column(
db.DateTime,
server_default=db.func.now(),
onupdate=db.func.now()
)
其中:
db.ForeignKey("categories.id")
代表:
tasks.category_id
會對應到:
categories.id
所以任務不會直接存「專案」這個中文字,而是存對應的分類編號。
Day 10 的分類選項是:
學習
專案
生活
所以 schema.sql 最後加入:
INSERT IGNORE INTO categories (name)
VALUES ('學習'), ('專案'), ('生活');
因為原本資料庫已經建立完成,所以這次我自己在 MySQL Workbench 執行:
USE ai_task_manager;
INSERT IGNORE INTO categories (name)
VALUES ('學習'), ('專案'), ('生活');
再確認:
SELECT * FROM categories;

Day 10 原本是:
<option value="待處理">待處理</option>
<option value="進行中">進行中</option>
<option value="已完成">已完成</option>
Day 12 改成:
<select id="status" name="status">
<option value="todo">待處理</option>
<option value="in_progress">進行中</option>
<option value="done">已完成</option>
</select>
畫面上還是中文,但 Flask 收到的值會是:
todo
in_progress
done
這樣就能直接對應 MySQL 的 ENUM。
分類下拉選單目前仍然維持:
<select id="category" name="category">
<option value="學習">學習</option>
<option value="專案">專案</option>
<option value="生活">生活</option>
</select>
送出後,Flask 會用名稱查分類:
category = Category.query.filter_by(
name=request.form.get("category")
).first()
如果選的是:
專案
而資料庫裡「專案」的 id 是:
2
最後任務存的就是:
category_id = 2
HTML 的日期欄位送到 Flask 時,其實是字串:
2026-09-26
但資料庫欄位是:
db.Date
所以先轉成 Python 的日期:
due_date_text = request.form.get("due_date", "")
due_date = (
datetime.strptime(due_date_text, "%Y-%m-%d").date()
if due_date_text
else None
)
如果沒有填截止日期,就會存成:
NULL
整理完表單資料後:
task = Task(
title=request.form.get("title", ""),
description=request.form.get("description", ""),
status=request.form.get("status", "todo"),
category_id=category.id if category else None,
due_date=due_date,
)
真正寫入資料庫的是:
db.session.add(task)
db.session.commit()
這兩行就是 Day 12 最重要的地方。
成功後:
return redirect(url_for("new_task"))
回到空白的新增任務頁。
Day 10 原本顯示:
已收到(尚未儲存)
的暫時預覽也拿掉了。
我在新增任務頁面輸入:
任務名稱:測試
說明:測試
狀態:待處理
分類:專案
截止日期:2026/09/26
送出後,再到 MySQL Workbench 執行:
SELECT * FROM tasks;
可以看到:
title = 測試
description = 測試
status = todo
category_id = 2
due_date = 2026/09/26

這張圖可以直接證明資料已經真的寫進 MySQL。
Workbench 裡還會看到:
due_date
created_at
updated_at
它們用途不同。
due_date 是使用者自己設定的截止日期。created_at 是這筆任務建立的時間。updated_at 是這筆任務最後一次修改的時間。
剛建立任務時:
created_at = 建立時間
updated_at = 建立時間
之後如果修改任務:
created_at = 不變
updated_at = 更新
我這次剛好在 2026/09/26 建立任務,而且截止日期也選 2026/09/26,所以三個日期看起來剛好一樣。
目前只完成:
新增任務
→ MySQL
首頁的三張任務卡片還是之前寫死的 HTML。
所以資料雖然已經在資料庫裡,首頁還不會自動顯示。
這部分留到下一篇。
Day 12 終於把新增任務和資料庫接起來了。
現在從網頁送出的任務,可以真正保存到 MySQL。
下一步就是把首頁原本的假資料換掉,直接從資料庫讀出真正的任務。