Day 10 已經可以從表單把任務送到 Flask,終端機也會看到:
收到新任務: {
'title': '測試',
'description': '測試',
'status': '待處理',
'category': '學習',
'due_date': '2026-09-24'
}
但這筆資料目前只是被 Flask 收到而已。
只要重新整理頁面、關掉程式,資料就沒有真正保存。
所以 Day 11 要做的事情很明確:
先把 Flask 和 MySQL 接起來。
今天先不把新增任務寫進資料庫,只做到:
建立資料庫
↓
建立資料表
↓
建立 Flask 資料庫設定
↓
Flask 成功連上 MySQL
真正把 Day 10 的表單資料存進去,留到 Day 12。
我先把目前狀態交給 Claude Code:
目前專案狀態:
- Flask 首頁可以正常執行
- 已有新增任務表單
- POST /tasks/new 可以收到表單資料
- 目前資料只會顯示在頁面並 print 到終端機
- 還沒有資料庫連線
今天要開始接 MySQL。
今天只做到:
1. 更新 requirements.txt
2. 建立 .env
3. 建立 .env.example
4. 建立 .gitignore
5. 建立 config.py
6. 建立 db.py
7. 建立 schema.sql
8. 修改 app.py 接上 Flask-SQLAlchemy
9. 確認 Flask 可以成功連上 MySQL
今天不要:
- INSERT 任務
- 修改首頁任務列表
- 做分類 CRUD
- 做任務修改或刪除
- 建立 models/
- 建立 services/
- 建立 routes/
- 加入 Claude API
這次沒有再往後擴功能。
完成後,專案變成:
ai-task-manager/
├── app.py
├── config.py
├── db.py
├── schema.sql
├── .env
├── .env.example
├── .gitignore
├── requirements.txt
│
├── templates/
│ ├── index.html
│ └── add_task.html
│
├── static/
│ └── css/
│ └── style.css
│
└── venv/
這次新增的重點主要是:
config.py
db.py
schema.sql
.env
.env.example
.gitignore
原本只有 Flask。
現在變成:
Flask
Flask-SQLAlchemy
PyMySQL
python-dotenv
這三個新增套件分別有不同用途。Flask-SQLAlchemy 讓 Flask 可以使用 SQLAlchemy。PyMySQL 負責 Python 和 MySQL 之間的連線。python-dotenv 則讓程式可以讀取 .env 裡的設定。
安裝方式還是一樣:
pip install -r requirements.txt
.env?資料庫連線需要:
Host
Port
Database
User
Password
這些如果直接寫在 app.py 裡,不太適合。
所以這次新增:
.env
內容大概是:
DB_HOST=localhost
DB_PORT=3306
DB_NAME=ai_task_manager
DB_USER=ai_task_manager_user
DB_PASSWORD=你的密碼
真正的 MySQL 密碼只放在自己的 .env。
不放進文章,也不放到 Git。
.env.example 又是什麼?另外還建立:
.env.example
它跟 .env 結構一樣,但不放真實密碼:
DB_HOST=localhost
DB_PORT=3306
DB_NAME=ai_task_manager
DB_USER=ai_task_manager_user
DB_PASSWORD=your_password
這個檔案可以留在專案裡,讓之後的人知道需要哪些環境變數。
簡單來說:
.env
→ 真正設定
→ 不公開
.env.example
→ 設定範例
→ 可以放進 Git
.gitignore 是做什麼的?這次也先建立:
.gitignore
內容是:
venv/
.env
__pycache__/
*.pyc
它的意思是告訴 Git:
這些檔案或資料夾不要追蹤
例如 .env 裡面有資料庫密碼,所以不應該上傳。venv/ 則是虛擬環境,也不需要跟著專案一起上傳。
目前雖然還沒有 git init,但先準備好也沒問題。
這次新增:
config.py
內容是:
import os
from dotenv import load_dotenv
load_dotenv()
class Config:
SQLALCHEMY_DATABASE_URI = (
f"mysql+pymysql://{os.environ['DB_USER']}:{os.environ['DB_PASSWORD']}"
f"@{os.environ['DB_HOST']}:{os.environ['DB_PORT']}/{os.environ['DB_NAME']}"
"?charset=utf8mb4"
)
SQLALCHEMY_TRACK_MODIFICATIONS = False
先看:
load_dotenv()
它會讀取 .env。
接著:
os.environ['DB_USER']
os.environ['DB_PASSWORD']
os.environ['DB_HOST']
os.environ['DB_PORT']
os.environ['DB_NAME']
會拿出對應設定。
最後組成類似:
mysql+pymysql://ai_task_manager_user:密碼@localhost:3306/ai_task_manager?charset=utf8mb4
這就是 Flask-SQLAlchemy 用來連 MySQL 的連線網址。
這次另外建立:
ai_task_manager_user
而不是讓 Flask 一直使用:
root
這個專用帳號只需要存取:
ai_task_manager
這個資料庫。
這樣之後 Flask 使用資料庫時,不需要拿最高權限的 root 帳號來連。
這次的 db.py:
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
目前只有兩行。
它先建立一個共用的:
db
之後不管是:
Task
Category
都可以使用同一個 db。
Day 11 還沒有建立 Model,所以目前只先把這個基礎準備好。
這次新增:
schema.sql
第一段是:
CREATE DATABASE IF NOT EXISTS ai_task_manager
DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
USE ai_task_manager;
這裡建立:
ai_task_manager
作為這個專案使用的資料庫。utf8mb4 則是為了避免中文或其他 Unicode 文字出現問題。
第一張表:
CREATE TABLE categories (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL UNIQUE,
color VARCHAR(7) DEFAULT '#6c757d'
) DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
目前有三個欄位:
id
name
color
id 是分類編號。name 是分類名稱。
例如:
學習
專案
生活
color 則是之後可以拿來控制分類顯示顏色。
第二張表:
CREATE TABLE tasks (
id INT AUTO_INCREMENT PRIMARY KEY,
title VARCHAR(200) NOT NULL,
description TEXT NULL,
status ENUM('todo', 'in_progress', 'done') NOT NULL DEFAULT 'todo',
category_id INT NULL,
due_date DATE NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (category_id)
REFERENCES categories(id)
ON DELETE SET NULL
) DEFAULT CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
目前任務有:
id
title
description
status
category_id
due_date
created_at
updated_at
這些欄位已經開始跟 Day 10 的表單對起來。
任務名稱 → title
說明 → description
狀態 → status
分類 → category_id
截止日期 → due_date
另外:
created_at
updated_at
則是用來記錄建立與更新時間。
Day 10 畫面目前顯示:
待處理
進行中
已完成
但資料庫裡設定的是:
todo
in_progress
done
也就是:
todo → 待處理
in_progress → 進行中
done → 已完成
之後程式可以用固定的值判斷狀態,畫面再顯示中文。
目前 Day 11 還沒有真的存資料,所以這部分先保留到下一步處理。
tasks 沒有直接存:
學習
而是存:
category_id
例如:
categories
id | name
---|------
1 | 學習
2 | 專案
3 | 生活
如果某個任務屬於「學習」,那 tasks 裡可能是:
category_id = 1
這樣任務和分類就可以建立關聯。
這段:
ON DELETE SET NULL
代表如果某個分類之後被刪掉,任務不會一起消失。
只會把:
category_id
改成:
NULL
任務本身還是存在。
我這次直接用 MySQL Workbench 執行 schema.sql。
執行完成後,再確認:
USE ai_task_manager;
SHOW TABLES;
可以看到:
categories
tasks

也可以再執行:
DESCRIBE tasks;
確認 tasks 欄位都有建立成功。
這次沒有讓 Flask 直接使用 root。
而是建立:
ai_task_manager_user
在 Workbench 使用 root 連線執行:
CREATE USER IF NOT EXISTS
'ai_task_manager_user'@'localhost'
IDENTIFIED BY '自己設定的密碼';
GRANT ALL PRIVILEGES
ON ai_task_manager.*
TO 'ai_task_manager_user'@'localhost';
FLUSH PRIVILEGES;
這個帳號只用來操作:
ai_task_manager
建立完成後,我另外在 Workbench 新增一個:
AI Task Manager
連線。
設定:
Host: localhost
Port: 3306
User Name: ai_task_manager_user
Default Schema: ai_task_manager
再使用剛才設定的密碼測試連線。
連線成功會出現:
Day 10 的 app.py 原本只有 Flask。
Day 11 多了:
from sqlalchemy import text
from config import Config
from db import db
接著:
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
這兩行是今天很重要的地方。
app.config.from_object(Config)
把 config.py 的設定載進 Flask。
接著:
db.init_app(app)
把剛才 db.py 建立的 SQLAlchemy 物件跟 Flask app 接起來。
今天還不會查任務,也不會新增任務。
所以 Claude Code 加了一個很單純的測試:
if __name__ == "__main__":
try:
with app.app_context():
db.session.execute(text("SELECT 1"))
print("MySQL 連線成功")
except Exception as e:
print("MySQL 連線失敗:", e)
app.run(debug=True)
其中:
db.session.execute(text("SELECT 1"))
不是在查 tasks。
它只是送一個最簡單的 SQL 給 MySQL。
如果 MySQL 可以正常收到並回應,就代表資料庫連線成功。
實際啟動 Flask 後,終端機會看到:
這就是今天最重要的驗證結果。
雖然 MySQL 已經連線成功,但是 Day 10 的:
submitted_task = {
"title": request.form.get("title", ""),
"description": request.form.get("description", ""),
"status": request.form.get("status", ""),
"category": request.form.get("category", ""),
"due_date": request.form.get("due_date", ""),
}
目前還是只有:
print("收到新任務:", submitted_task)
沒有真正執行資料庫新增。
所以現在流程是:
新增任務表單
↓
Flask
↓
print()
而另一邊:
Flask
↓
MySQL
已經確認可以連線。
但兩條線還沒有接在一起。
Day 11 完成之後,專案第一次有了真正的資料庫環境。
目前已經做到:
.env
↓
config.py
↓
Flask
↓
SQLAlchemy
↓
PyMySQL
↓
MySQL
MySQL 裡也已經有:
ai_task_manager
├── categories
└── tasks
而 Flask 啟動後也能成功印出:
MySQL 連線成功
下一步才會真正把 Day 10 的表單和 MySQL 接在一起:
新增任務表單
↓
Flask
↓
INSERT
↓
tasks