iT邦幫忙

2026 iThome 鐵人賽

DAY 12
0

Day 11 已經完成 Flask 和 MySQL 的連線。
目前資料庫裡有:

ai_task_manager
├── categories
└── tasks

但 Day 10 的新增任務功能還只是把資料送到 Flask,沒有真正保存。
所以 Day 12 的目標很單純:
把新增任務表單真正寫進 MySQL。
這次先不碰首頁顯示、修改、刪除、篩選。


先讓 Claude Code 規劃

我先把目前狀態交給 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

目前專案變成:
https://ithelp.ithome.com.tw/upload/images/20260926/20183857mM6qpEXgqi.png


建立 Category Model

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

建立 Task Model

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;

https://ithelp.ithome.com.tw/upload/images/20260926/20183857uZ65DZILBp.png


status 改成資料庫使用的值

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。


分類先用名稱查 id

分類下拉選單目前仍然維持:

<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

due_date 需要先轉成日期

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 寫進 MySQL

整理完表單資料後:

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

https://ithelp.ithome.com.tw/upload/images/20260926/20183857ptDQwLXIXp.png
這張圖可以直接證明資料已經真的寫進 MySQL。


due_date、created_at、updated_at 的差別

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。
下一步就是把首頁原本的假資料換掉,直接從資料庫讀出真正的任務。


上一篇
[Day 11] 讓任務有地方保存:開始接 MySQL
下一篇
[Day 13] 不再寫死任務:從 MySQL 顯示資料
系列文
從零開始 Claude Code:30 天打造 AI 任務管理 Web App 共 16 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言