這篇紀錄的是我在開發 YamAgent 專案時,針對 Session 模組的資料表關聯與刪除邏輯進行修復與重構的過程。從 ForeignKey 錯誤、ORM relationship 的使用,到 RESTful API 回應行為,整個流程雖然繁瑣,但修完之後整個系統更乾淨、穩定。


🧩 問題背景

我希望在刪除某筆 Session 時,能同時清除與其關聯的:

  • ChatHistory
  • MemoryRecord
  • TokenUsageRecord

但實際執行時出現了以下錯誤:

  • PostgreSQL 拒絕刪除:ForeignKeyViolation
  • Alembic 找不到 sessions table:NoReferencedTableError
  • SQLAlchemy ORM 無法初始化:relationship expects a class
  • 前端收到錯誤:Unexpected end of JSON input

🔧 解法摘要

1️⃣ 正確設計 ForeignKey(Cascade 刪除)

session_id = Column(UUID(as_uuid=True), ForeignKey("sessions.id", ondelete="CASCADE"))

2️⃣ 補上雙向 relationship

# 子 model
session = relationship("SessionRecord", back_populates="chat_history", passive_deletes=True)

# Session model
chat_history = relationship("ChatHistory", back_populates="session", cascade="all, delete-orphan")

3️⃣ 修正 class 名拼錯:"Session""SessionRecord"

SQLAlchemy 要的是 ORM class,不是 table name!


4️⃣ Alembic 重新產生 migration

  • 確保 env.py 有 import 所有 model
  • alembic revision --autogenerate
  • alembic upgrade head

5️⃣ 修正 REST API delete 回傳錯誤

原本這樣會報錯:

@router.delete(..., status_code=204)
return {"message": "刪除成功"}  # ❌ 204 不能回傳 body

改為:

@router.delete(..., status_code=200)
return {"message": "刪除成功"}  # ✅

✅ 最終成果

  • 可以順利刪除 Session,且關聯資料會自動清除
  • 不再出現外鍵錯誤
  • 前端能正確收到 JSON response
  • Alembic migration 與資料庫 schema 同步

🧠 心得

這次修復讓我更熟悉了 SQLAlchemy 的 cascade 機制與 Alembic 的 model 掃描邏輯。也再次體會到 RESTful 的 response 行為與狀態碼配對的嚴謹性。

雖然中間踩了一堆坑,但總算還是修好了 🎉