Django 入門:用 Python 輕鬆打造 AI 驅動的多租戶預約平台後端!
前言:開發網站從零開始,其實可以更省力!
您好!寫程式的朋友們應該都清楚,從頭開發一個網站或應用程式,從使用者管理、資料庫操作到安全考量,每環節都需要不少工夫。不過,如果有個工具能幫您把這些基礎的重複性工作先打理好,讓您可以把更多心力放在核心功能上,那倒也挺不錯的。
這就是今天要聊的 Django!一個用 Python 寫的免費開源網頁框架。它有個特色叫「自帶電池 (Batteries included)」,意思就是它已經預備好大部分網站開發會用到的功能,讓您不用到處找工具,算是一站式的解決方案。如果您對 Python 熟悉,那 Django 確實值得花點時間了解。
這篇文章會帶您簡單認識 Django 的基本概念,並結合您手邊這個「AI 驅動的多租戶線上預約平台」的專案。這個專案目前提供的是核心的代碼骨架與基礎配置,也就是說,它為您搭建了一個穩固的平台基礎,方便您在此之上逐步完善各種功能。我們來看看這個 Django 後端是怎麼運作,以及您該如何著手進行後續開發。
您好!寫程式的朋友們應該都清楚,從頭開發一個網站或應用程式,從使用者管理、資料庫操作到安全考量,每環節都需要不少工夫。不過,如果有個工具能幫您把這些基礎的重複性工作先打理好,讓您可以把更多心力放在核心功能上,那倒也挺不錯的。
這就是今天要聊的 Django!一個用 Python 寫的免費開源網頁框架。它有個特色叫「自帶電池 (Batteries included)」,意思就是它已經預備好大部分網站開發會用到的功能,讓您不用到處找工具,算是一站式的解決方案。如果您對 Python 熟悉,那 Django 確實值得花點時間了解。
這篇文章會帶您簡單認識 Django 的基本概念,並結合您手邊這個「AI 驅動的多租戶線上預約平台」的專案。這個專案目前提供的是核心的代碼骨架與基礎配置,也就是說,它為您搭建了一個穩固的平台基礎,方便您在此之上逐步完善各種功能。我們來看看這個 Django 後端是怎麼運作,以及您該如何著手進行後續開發。
一、認識 Django 的基本結構:專案與應用程式 (Project & App)
在 Django 裡面,有兩個基本的概念,了解它們就能掌握大致的骨架:
專案 (Project):您可以把它想像成整個網站的主體框架,裡頭包含所有的設定、資料庫連線資訊,以及整個網站的網址(URL)規劃。您這個 booking-platform-django-fastapi
專案的 Django 後端部分,就是一個 Django Project。通常,在專案的最外層會有一個同名的資料夾(例如 backend/core_project/
),放著主要的設定檔。
應用程式 (App):App 就像是網站裡頭一個個獨立的功能模組。舉例來說,您的網站可能會有「使用者管理」、「商家資料」、「預約排程」這些獨立的功能。在 Django 的設計裡,每個功能區塊都可以設計成一個 App。這樣做的好處是:
分工明確:程式碼分門別類,維護起來比較方便。
彈性運用:寫好的 App 搞不好將來在其他 Django 專案也能直接拿來用。
在您這個 booking-platform-django-fastapi
專案裡,Django 後端就分成了好幾個 App:users
(處理使用者)、merchants
(處理商家)、appointments
(處理預約) 還有 common
(一些通用功能)。這些 App 都放在 backend/apps/
資料夾底下。
booking-platform-django-fastapi/
└── backend/
├── core_project/ # Django Project 的核心設定資料夾
│ ├── settings.py # 專案總設定檔
│ └── urls.py # 專案的總路由配置
├── apps/
│ ├── users/ # 使用者管理 App
│ │ ├── models.py # 資料庫模型定義
│ │ ├── views.py # 處理邏輯與回傳回應
│ │ └── urls.py # App 內部的 URL 路由
│ ├── merchants/ # 商家資訊管理 App
│ │ ├── models.py
│ │ └── ...
│ ├── appointments/ # 預約排程管理 App
│ │ ├── models.py
│ │ └── ...
│ └── common/ # 通用功能 App
│ ├── models.py
│ └── ...
└── manage.py # Django 專案的管理工具
在 Django 裡面,有兩個基本的概念,了解它們就能掌握大致的骨架:
專案 (Project):您可以把它想像成整個網站的主體框架,裡頭包含所有的設定、資料庫連線資訊,以及整個網站的網址(URL)規劃。您這個
booking-platform-django-fastapi
專案的 Django 後端部分,就是一個 Django Project。通常,在專案的最外層會有一個同名的資料夾(例如backend/core_project/
),放著主要的設定檔。應用程式 (App):App 就像是網站裡頭一個個獨立的功能模組。舉例來說,您的網站可能會有「使用者管理」、「商家資料」、「預約排程」這些獨立的功能。在 Django 的設計裡,每個功能區塊都可以設計成一個 App。這樣做的好處是:
分工明確:程式碼分門別類,維護起來比較方便。
彈性運用:寫好的 App 搞不好將來在其他 Django 專案也能直接拿來用。
在您這個 booking-platform-django-fastapi
專案裡,Django 後端就分成了好幾個 App:users
(處理使用者)、merchants
(處理商家)、appointments
(處理預約) 還有 common
(一些通用功能)。這些 App 都放在 backend/apps/
資料夾底下。
booking-platform-django-fastapi/
└── backend/
├── core_project/ # Django Project 的核心設定資料夾
│ ├── settings.py # 專案總設定檔
│ └── urls.py # 專案的總路由配置
├── apps/
│ ├── users/ # 使用者管理 App
│ │ ├── models.py # 資料庫模型定義
│ │ ├── views.py # 處理邏輯與回傳回應
│ │ └── urls.py # App 內部的 URL 路由
│ ├── merchants/ # 商家資訊管理 App
│ │ ├── models.py
│ │ └── ...
│ ├── appointments/ # 預約排程管理 App
│ │ ├── models.py
│ │ └── ...
│ └── common/ # 通用功能 App
│ ├── models.py
│ └── ...
└── manage.py # Django 專案的管理工具
二、核心 Django 概念簡單說
一個 Django 網站的運作,大致上可以套用 MVT (Model-View-Template) 這個模式來理解。雖然您的前端是 Vue.js,沒有直接用到 Django 的 Template 部分,但後端的運作邏輯還是差不多:
Model (模型):
放在 apps/*/models.py
:模型就是把您資料庫的結構,用 Python 的類別 (Class) 來表示。通常一個類別會對應到資料庫裡的一個資料表 (Table),類別裡面的每個屬性 (Attribute) 則對應到資料表的一個欄位 (Column)。
Django 有個好用的 ORM (Object-Relational Mapping) 機制,讓您可以直接用 Python 程式碼來操作資料庫,不用花力氣寫原生的 SQL 語句,省事很多。
在您的專案中:backend/apps/users/models.py
會定義 User
模型,它就對應到 MySQL 裡的 users
表。依此類推,backend/apps/merchants/models.py
定義 Merchant
模型,對應 merchants
表。
View (視圖):
放在 apps/*/views.py
:View 的工作就是接收網頁來的請求(Request),然後處理完後回傳一個回應(Response)。當您在瀏覽器上輸入一個網址時,Django 就會去找到對應的 View 函數或類別來處理。
View 會負責從 Model 那邊拿資料、跑一些業務邏輯,然後再把結果整理好送出去。
在您的專案中:backend/apps/users/views.py
裡頭,就會有處理使用者註冊、登入、個人資料查詢或更新這些邏輯的程式碼。
URL (路由):
放在 mysite/urls.py
(總路由) 和 apps/*/urls.py
(App 路由):URL 就是定義網站的網址規則,把特定的網址對應到特定的 View。
通常專案的總 urls.py
會把各個 App 的路由都串起來,這樣每個 App 就能各自管理自己的網址,比較不會亂。
在您的專案中:您在瀏覽器上輸入 http://localhost/api/users/profile/
時,這個請求會先經過 backend/core_project/urls.py
,然後再導向到 backend/apps/users/urls.py
,最後才由 users
App 裡頭相對應的 View 來處理。
Admin Site (管理介面):
Django 自帶一個蠻方便的後台管理介面。只要做一些簡單的設定,您就可以透過網頁輕鬆管理資料庫裡的數據,不用自己寫一堆後台管理程式。這對專案開發初期或是日常的數據維護來說,都挺實用的。
在您的專案中:您可以打開瀏覽器,輸入 http://localhost/admin
。一旦您建立好超級用戶並登入,就能在網頁上管理 users
、merchants
等 App 的資料了。
一個 Django 網站的運作,大致上可以套用 MVT (Model-View-Template) 這個模式來理解。雖然您的前端是 Vue.js,沒有直接用到 Django 的 Template 部分,但後端的運作邏輯還是差不多:
Model (模型):
放在
apps/*/models.py
:模型就是把您資料庫的結構,用 Python 的類別 (Class) 來表示。通常一個類別會對應到資料庫裡的一個資料表 (Table),類別裡面的每個屬性 (Attribute) 則對應到資料表的一個欄位 (Column)。Django 有個好用的 ORM (Object-Relational Mapping) 機制,讓您可以直接用 Python 程式碼來操作資料庫,不用花力氣寫原生的 SQL 語句,省事很多。
在您的專案中:
backend/apps/users/models.py
會定義User
模型,它就對應到 MySQL 裡的users
表。依此類推,backend/apps/merchants/models.py
定義Merchant
模型,對應merchants
表。
View (視圖):
放在
apps/*/views.py
:View 的工作就是接收網頁來的請求(Request),然後處理完後回傳一個回應(Response)。當您在瀏覽器上輸入一個網址時,Django 就會去找到對應的 View 函數或類別來處理。View 會負責從 Model 那邊拿資料、跑一些業務邏輯,然後再把結果整理好送出去。
在您的專案中:
backend/apps/users/views.py
裡頭,就會有處理使用者註冊、登入、個人資料查詢或更新這些邏輯的程式碼。
URL (路由):
放在
mysite/urls.py
(總路由) 和apps/*/urls.py
(App 路由):URL 就是定義網站的網址規則,把特定的網址對應到特定的 View。通常專案的總
urls.py
會把各個 App 的路由都串起來,這樣每個 App 就能各自管理自己的網址,比較不會亂。在您的專案中:您在瀏覽器上輸入
http://localhost/api/users/profile/
時,這個請求會先經過backend/core_project/urls.py
,然後再導向到backend/apps/users/urls.py
,最後才由users
App 裡頭相對應的 View 來處理。
Admin Site (管理介面):
Django 自帶一個蠻方便的後台管理介面。只要做一些簡單的設定,您就可以透過網頁輕鬆管理資料庫裡的數據,不用自己寫一堆後台管理程式。這對專案開發初期或是日常的數據維護來說,都挺實用的。
在您的專案中:您可以打開瀏覽器,輸入
http://localhost/admin
。一旦您建立好超級用戶並登入,就能在網頁上管理users
、merchants
等 App 的資料了。
三、資料庫怎麼初始化與管理:SQL 腳本跟 Django 遷移一起用
您這個 booking-platform-django-fastapi
專案在處理資料庫初始化這部分,用了一個比較彈性的方式:
初始 SQL 腳本 (mysql_init_scripts/init_database_schema.sql
):
這個檔案裡頭,就是所有資料庫表格的 CREATE TABLE
語句。
當您第一次用 docker-compose up -d
指令啟動 MySQL 容器時,MySQL 自己會去跑這個腳本,一次就把所有資料庫表格都建好。對於要把一個既有、完整的資料庫結構先建立起來,這個方法還挺方便的。
Django 遷移 (Migrations):
Django 有一套自己的資料庫遷移機制。當您在 Python 的 Model (models.py
) 裡有改動時,可以透過 Django 的指令來生成檔案,這些檔案會記錄資料庫需要怎麼變更。
生成遷移文件:python manage.py makemigrations
這個指令會檢查您的 models.py
文件,把所有變更轉成一些 Python 檔案,這些檔案就像是資料庫的「變更紀錄」。
應用遷移:python manage.py migrate
這個指令會讀這些變更紀錄檔,然後去資料庫執行對應的變更操作。
因為您的專案已經有 init_database_schema.sql
先建好表格了,所以第一次跑 migrate
時,可能會建議您加上 --fake-initial
這個參數。它會跟 Django 說:「這些 App 的初始資料表都已經有了,你記一下就好,不用再重複建立了。」這樣 Django 自己的狀態就能跟資料庫實際狀況同步。
這種方式的好處是,您既能利用 Docker 快速建立資料庫,也能透過 Django 的遷移系統來持續追蹤和管理資料庫的後續變動,彈性不錯。
您這個 booking-platform-django-fastapi
專案在處理資料庫初始化這部分,用了一個比較彈性的方式:
初始 SQL 腳本 (
mysql_init_scripts/init_database_schema.sql
):這個檔案裡頭,就是所有資料庫表格的
CREATE TABLE
語句。當您第一次用
docker-compose up -d
指令啟動 MySQL 容器時,MySQL 自己會去跑這個腳本,一次就把所有資料庫表格都建好。對於要把一個既有、完整的資料庫結構先建立起來,這個方法還挺方便的。
Django 遷移 (Migrations):
Django 有一套自己的資料庫遷移機制。當您在 Python 的 Model (
models.py
) 裡有改動時,可以透過 Django 的指令來生成檔案,這些檔案會記錄資料庫需要怎麼變更。生成遷移文件:
python manage.py makemigrations
這個指令會檢查您的
models.py
文件,把所有變更轉成一些 Python 檔案,這些檔案就像是資料庫的「變更紀錄」。
應用遷移:
python manage.py migrate
這個指令會讀這些變更紀錄檔,然後去資料庫執行對應的變更操作。
因為您的專案已經有
init_database_schema.sql
先建好表格了,所以第一次跑migrate
時,可能會建議您加上--fake-initial
這個參數。它會跟 Django 說:「這些 App 的初始資料表都已經有了,你記一下就好,不用再重複建立了。」這樣 Django 自己的狀態就能跟資料庫實際狀況同步。
這種方式的好處是,您既能利用 Docker 快速建立資料庫,也能透過 Django 的遷移系統來持續追蹤和管理資料庫的後續變動,彈性不錯。
四、啟動您的 Django 後端專案
接下來,我們就來實際操作,啟動這個專案的 Django 後端部分,讓它能跟其他服務一起協同運作。
請在您的 booking-platform-django-fastapi
專案根目錄下,按照以下步驟操作:
啟動所有 Docker 服務:
docker-compose up --build -d
這個指令會把 MySQL、Django 後端、FastAPI 推薦引擎、Redis、Nginx 等所有必要的服務都啟動起來。
稍微等一下,確認 Django 後端服務啟動完成:
您可以打 docker-compose logs django_backend 這個指令,看看 Django 容器的啟動日誌。如果看到類似 Starting development server at http://0.0.0.0:8000/ 或者沒有持續的錯誤訊息,通常就表示 Django 服務已經準備好了。
執行資料庫初始化與遷移 (這個步驟通常只在第一次或需要重設資料庫時才做):
前面提過,這是為了讓 Django 內部對資料庫狀態的認知,跟您用 init_database_schema.sql 建立的實際資料庫結構保持一致。
# 檢查並生成新的遷移文件(如果模型的定義有變動,或這是第一次)
docker exec -it booking-platform-django-fastapi_django_backend python manage.py makemigrations
# 應用所有遷移,針對那些已經用 SQL 腳本建好的 App,做一個「假遷移」
docker exec -it booking-platform-django-fastapi_django_backend python manage.py migrate --fake-initial
為什麼要 makemigrations
? 就算您用 SQL 腳本建好表了,Django 還是需要對應的 Python 遷移檔案來追蹤。如果您有更動 models.py
,就得再跑一次。
為什麼要 migrate --fake-initial
? 如果您是全新啟動,而且已經透過 SQL 腳本把表建好了,Django 會發現這些表已經在那裡了。--fake-initial
就是告訴 Django,把這些初始的遷移記下來就好,不用真的去跑建立表的 SQL,以免重複操作。
建立 Django 超級使用者:
為了能進到 Django 的管理後台(http://localhost/admin),您需要先建立一個超級管理員帳號:
docker exec -it booking-platform-django-fastapi_django_backend python manage.py createsuperuser
照著提示輸入使用者名稱、電子郵件和密碼就可以了。
接下來,我們就來實際操作,啟動這個專案的 Django 後端部分,讓它能跟其他服務一起協同運作。
請在您的 booking-platform-django-fastapi
專案根目錄下,按照以下步驟操作:
啟動所有 Docker 服務:
docker-compose up --build -d
這個指令會把 MySQL、Django 後端、FastAPI 推薦引擎、Redis、Nginx 等所有必要的服務都啟動起來。
稍微等一下,確認 Django 後端服務啟動完成:
您可以打 docker-compose logs django_backend 這個指令,看看 Django 容器的啟動日誌。如果看到類似 Starting development server at http://0.0.0.0:8000/ 或者沒有持續的錯誤訊息,通常就表示 Django 服務已經準備好了。
執行資料庫初始化與遷移 (這個步驟通常只在第一次或需要重設資料庫時才做):
前面提過,這是為了讓 Django 內部對資料庫狀態的認知,跟您用 init_database_schema.sql 建立的實際資料庫結構保持一致。
# 檢查並生成新的遷移文件(如果模型的定義有變動,或這是第一次) docker exec -it booking-platform-django-fastapi_django_backend python manage.py makemigrations # 應用所有遷移,針對那些已經用 SQL 腳本建好的 App,做一個「假遷移」 docker exec -it booking-platform-django-fastapi_django_backend python manage.py migrate --fake-initial
為什麼要
makemigrations
? 就算您用 SQL 腳本建好表了,Django 還是需要對應的 Python 遷移檔案來追蹤。如果您有更動models.py
,就得再跑一次。為什麼要
migrate --fake-initial
? 如果您是全新啟動,而且已經透過 SQL 腳本把表建好了,Django 會發現這些表已經在那裡了。--fake-initial
就是告訴 Django,把這些初始的遷移記下來就好,不用真的去跑建立表的 SQL,以免重複操作。
建立 Django 超級使用者:
為了能進到 Django 的管理後台(http://localhost/admin),您需要先建立一個超級管理員帳號:
docker exec -it booking-platform-django-fastapi_django_backend python manage.py createsuperuser
照著提示輸入使用者名稱、電子郵件和密碼就可以了。
五、簡單逛逛您的 Django 網站
現在,您的 Django 後端應該就能開始運作了!您可以試著:
進入 Django 管理後台:打開瀏覽器,輸入 http://localhost/admin
。用您剛才建立的超級使用者帳號登入後,您應該能看到 Users、Merchants 等 App 的管理介面,可以直接在這邊看看和修改資料庫裡的數據。
試試 API 介面:這個專案透過 Nginx 把 Django 的 API 導向到 /api/
。您可以透過前端介面操作,或是用 Postman、Insomnia 這類工具來測試 API。
看看 FastAPI 推薦引擎的文件:打開 http://localhost:8001/docs
,這是 FastAPI 自動生成的 API 文件,會列出所有可用的推薦 API。
現在,您的 Django 後端應該就能開始運作了!您可以試著:
進入 Django 管理後台:打開瀏覽器,輸入
http://localhost/admin
。用您剛才建立的超級使用者帳號登入後,您應該能看到 Users、Merchants 等 App 的管理介面,可以直接在這邊看看和修改資料庫裡的數據。試試 API 介面:這個專案透過 Nginx 把 Django 的 API 導向到
/api/
。您可以透過前端介面操作,或是用 Postman、Insomnia 這類工具來測試 API。看看 FastAPI 推薦引擎的文件:打開
http://localhost:8001/docs
,這是 FastAPI 自動生成的 API 文件,會列出所有可用的推薦 API。
六、專案代碼庫與關鍵代碼片段
您的專案原始碼在這裡,歡迎大家參考:https://github.com/BpsEason/booking-platform-django-fastapi.git
以下我們會挑一些專案裡重要的 Django 程式碼片段,並加上詳細的註解,希望能幫助您更快理解它們的功能。
您的專案原始碼在這裡,歡迎大家參考:https://github.com/BpsEason/booking-platform-django-fastapi.git
以下我們會挑一些專案裡重要的 Django 程式碼片段,並加上詳細的註解,希望能幫助您更快理解它們的功能。
1. Django 模型範例 (backend/apps/users/models.py
)
這是 User
模型,它定義了使用者在資料庫裡的長相,以及一些相關的處理邏輯。特別要注意的是,我們用了 AbstractBaseUser
和 PermissionsMixin
來建立自訂的使用者模型,這樣會更有彈性。
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
# 自訂 UserManager 類別,用來處理使用者創建的流程
class UserManager(BaseUserManager):
# 建立一般使用者的方法
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('Email 這個欄位一定要填。')
email = self.normalize_email(email) # 把 email 格式統一
user = self.model(email=email, **extra_fields)
user.set_password(password) # 設定密碼,Django 會自動加密
user.save(using=self._db)
return user
# 建立超級管理員的方法
def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault('user_type', 'super_admin') # 預設是超級管理員
extra_fields.setdefault('status', 'active') # 預設狀態是啟用中
# 這裡也可以根據需求加其他預設屬性
return self.create_user(email, password, **extra_fields)
# 自訂 User 模型,繼承了 Django 內建的基礎用戶功能
class User(AbstractBaseUser, PermissionsMixin):
# 定義使用者類型,例如顧客、商家管理員等
USER_TYPE_CHOICES = (
('customer', '顧客'),
('merchant_admin', '商家管理員'),
('staff', '員工'),
('super_admin', '超級管理員'),
)
# 定義使用者狀態,例如啟用、停用、凍結
STATUS_CHOICES = (
('active', '啟用'),
('inactive', '停用'),
('suspended', '凍結'),
)
email = models.EmailField(unique=True) # 電子郵件,而且不能重複
# password_hash 這個欄位不是存明碼,是存密碼加密後的雜湊值
password_hash = models.CharField(max_length=255)
phone_number = models.CharField(max_length=20, unique=True, null=True, blank=True)
first_name = models.CharField(max_length=100, blank=True, null=True)
last_name = models.CharField(max_length=100, blank=True, null=True)
user_type = models.CharField(max_length=20, choices=USER_TYPE_CHOICES)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
last_login_at = models.DateTimeField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True) # 建立時自動填入時間
updated_at = models.DateTimeField(auto_now=True) # 每次更新時自動更新時間
objects = UserManager() # 指定用我們自訂的 UserManager 來管理這個模型
USERNAME_FIELD = 'email' # 設定用哪個欄位來當登入帳號
REQUIRED_FIELDS = ['user_type'] # 建立使用者時,除了帳號密碼外,還要填這些欄位
class Meta:
db_table = 'users' # 指定這個模型對應到資料庫裡的表格名稱就是 'users'
def __str__(self):
return self.email # 在管理介面顯示這個使用者時,會顯示他的 email
這是 User
模型,它定義了使用者在資料庫裡的長相,以及一些相關的處理邏輯。特別要注意的是,我們用了 AbstractBaseUser
和 PermissionsMixin
來建立自訂的使用者模型,這樣會更有彈性。
from django.db import models
from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager
# 自訂 UserManager 類別,用來處理使用者創建的流程
class UserManager(BaseUserManager):
# 建立一般使用者的方法
def create_user(self, email, password=None, **extra_fields):
if not email:
raise ValueError('Email 這個欄位一定要填。')
email = self.normalize_email(email) # 把 email 格式統一
user = self.model(email=email, **extra_fields)
user.set_password(password) # 設定密碼,Django 會自動加密
user.save(using=self._db)
return user
# 建立超級管理員的方法
def create_superuser(self, email, password=None, **extra_fields):
extra_fields.setdefault('user_type', 'super_admin') # 預設是超級管理員
extra_fields.setdefault('status', 'active') # 預設狀態是啟用中
# 這裡也可以根據需求加其他預設屬性
return self.create_user(email, password, **extra_fields)
# 自訂 User 模型,繼承了 Django 內建的基礎用戶功能
class User(AbstractBaseUser, PermissionsMixin):
# 定義使用者類型,例如顧客、商家管理員等
USER_TYPE_CHOICES = (
('customer', '顧客'),
('merchant_admin', '商家管理員'),
('staff', '員工'),
('super_admin', '超級管理員'),
)
# 定義使用者狀態,例如啟用、停用、凍結
STATUS_CHOICES = (
('active', '啟用'),
('inactive', '停用'),
('suspended', '凍結'),
)
email = models.EmailField(unique=True) # 電子郵件,而且不能重複
# password_hash 這個欄位不是存明碼,是存密碼加密後的雜湊值
password_hash = models.CharField(max_length=255)
phone_number = models.CharField(max_length=20, unique=True, null=True, blank=True)
first_name = models.CharField(max_length=100, blank=True, null=True)
last_name = models.CharField(max_length=100, blank=True, null=True)
user_type = models.CharField(max_length=20, choices=USER_TYPE_CHOICES)
status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='active')
last_login_at = models.DateTimeField(null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True) # 建立時自動填入時間
updated_at = models.DateTimeField(auto_now=True) # 每次更新時自動更新時間
objects = UserManager() # 指定用我們自訂的 UserManager 來管理這個模型
USERNAME_FIELD = 'email' # 設定用哪個欄位來當登入帳號
REQUIRED_FIELDS = ['user_type'] # 建立使用者時,除了帳號密碼外,還要填這些欄位
class Meta:
db_table = 'users' # 指定這個模型對應到資料庫裡的表格名稱就是 'users'
def __str__(self):
return self.email # 在管理介面顯示這個使用者時,會顯示他的 email
2. Django Serializer 範例 (backend/apps/users/serializers.py
)
Serializer(序列化器)是 Django REST Framework 的一個核心工具,它主要負責把複雜的資料(比如資料庫裡的模型物件)轉換成 Python 比較好處理的資料類型,然後再變成 JSON 或 XML 格式,方便網路傳輸。反過來,它也能處理從前端傳過來的資料,做驗證和轉換。
from rest_framework import serializers
from .models import User # 從目前的 App 裡引入 User 模型
# UserProfileSerializer 用來顯示使用者的個人資料
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = User # 指定要處理的是 User 模型
# 要在 API 回傳中顯示哪些欄位
fields = ['id', 'username', 'email', 'full_name', 'user_type', 'status', 'created_at']
# 這些欄位是唯讀的,前端傳回來也不能改
read_only_fields = ['username', 'id', 'user_type', 'status', 'created_at']
# UserRegistrationSerializer 用來處理使用者註冊的資料
class UserRegistrationSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'})
class Meta:
model = User
# 註冊時需要哪些欄位
fields = ['email', 'username', 'password', 'password2', 'full_name', 'user_type']
# 密碼欄位只允許寫入,不允許讀取(安全性考量)
extra_kwargs = {'password': {'write_only': True}}
# 自訂驗證方法,確保兩次輸入的密碼是一樣的
def validate(self, attrs):
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError({"password": "兩次輸入的密碼不一致。"})
return attrs
# 建立新使用者的方法
def create(self, validated_data):
# 註冊時,把 password2 這個多餘的欄位從資料中移除
validated_data.pop('password2')
user = User.objects.create_user(
email=validated_data['email'],
username=validated_data['username'],
password=validated_data['password'],
full_name=validated_data.get('full_name', ''), # 取得全名,如果沒有就給空字串
user_type=validated_data.get('user_type', 'customer') # 預設是顧客
)
return user
# PasswordChangeSerializer 用來處理使用者修改密碼的資料
class PasswordChangeSerializer(serializers.Serializer):
old_password = serializers.CharField(required=True, style={'input_type': 'password'})
new_password = serializers.CharField(required=True, style={'input_type': 'password'})
new_password2 = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'})
def validate(self, attrs):
if attrs['new_password'] != attrs['new_password2']:
raise serializers.ValidationError({"new_password": "新密碼和確認密碼不一致。"})
return attrs
Serializer(序列化器)是 Django REST Framework 的一個核心工具,它主要負責把複雜的資料(比如資料庫裡的模型物件)轉換成 Python 比較好處理的資料類型,然後再變成 JSON 或 XML 格式,方便網路傳輸。反過來,它也能處理從前端傳過來的資料,做驗證和轉換。
from rest_framework import serializers
from .models import User # 從目前的 App 裡引入 User 模型
# UserProfileSerializer 用來顯示使用者的個人資料
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model = User # 指定要處理的是 User 模型
# 要在 API 回傳中顯示哪些欄位
fields = ['id', 'username', 'email', 'full_name', 'user_type', 'status', 'created_at']
# 這些欄位是唯讀的,前端傳回來也不能改
read_only_fields = ['username', 'id', 'user_type', 'status', 'created_at']
# UserRegistrationSerializer 用來處理使用者註冊的資料
class UserRegistrationSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'})
class Meta:
model = User
# 註冊時需要哪些欄位
fields = ['email', 'username', 'password', 'password2', 'full_name', 'user_type']
# 密碼欄位只允許寫入,不允許讀取(安全性考量)
extra_kwargs = {'password': {'write_only': True}}
# 自訂驗證方法,確保兩次輸入的密碼是一樣的
def validate(self, attrs):
if attrs['password'] != attrs['password2']:
raise serializers.ValidationError({"password": "兩次輸入的密碼不一致。"})
return attrs
# 建立新使用者的方法
def create(self, validated_data):
# 註冊時,把 password2 這個多餘的欄位從資料中移除
validated_data.pop('password2')
user = User.objects.create_user(
email=validated_data['email'],
username=validated_data['username'],
password=validated_data['password'],
full_name=validated_data.get('full_name', ''), # 取得全名,如果沒有就給空字串
user_type=validated_data.get('user_type', 'customer') # 預設是顧客
)
return user
# PasswordChangeSerializer 用來處理使用者修改密碼的資料
class PasswordChangeSerializer(serializers.Serializer):
old_password = serializers.CharField(required=True, style={'input_type': 'password'})
new_password = serializers.CharField(required=True, style={'input_type': 'password'})
new_password2 = serializers.CharField(write_only=True, required=True, style={'input_type': 'password'})
def validate(self, attrs):
if attrs['new_password'] != attrs['new_password2']:
raise serializers.ValidationError({"new_password": "新密碼和確認密碼不一致。"})
return attrs
3. Django View 範例 (backend/apps/users/views.py
)
View 負責接收從前端來的請求,它會利用 Serializer 來處理資料和驗證,然後跟 Model 互動來讀寫資料庫。這裡我們使用了 Django REST Framework 的 generics
視圖,這些 View 已經幫我們預設好很多常見的 API 操作了。
from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.views import APIView
from .models import User
from .serializers import UserProfileSerializer, UserRegistrationSerializer, PasswordChangeSerializer
from django.contrib.auth.hashers import make_password, check_password
# 使用者註冊 View
# CreateAPIView 適合處理 POST 請求,用於建立新的資源
class UserRegistrationView(generics.CreateAPIView):
queryset = User.objects.all() # 定義這個 View 會操作哪些資料(雖然這裡主要是建立)
serializer_class = UserRegistrationSerializer # 指定要用哪個序列化器來處理資料
permission_classes = [AllowAny] # 允許任何人都可以訪問(因為這是註冊功能)
# 使用者個人資料 View
# RetrieveUpdateAPIView 適合處理 GET(取得單一)和 PUT/PATCH(更新)請求
class UserProfileView(generics.RetrieveUpdateAPIView):
queryset = User.objects.all()
serializer_class = UserProfileSerializer
permission_classes = [IsAuthenticated] # 只有登入的使用者才能訪問
# 覆寫 get_object 方法,確保每個使用者都只能看到自己的個人資料
def get_object(self):
return self.request.user # 直接返回當前發出請求的使用者物件
# 密碼變更 View
# APIView 提供最基本的 View 類別,適合需要高度自訂邏輯的 API
class ChangePasswordView(APIView):
permission_classes = [IsAuthenticated] # 只有登入的使用者才能訪問
# 處理 POST 請求,用於變更密碼
def post(self, request, *args, **kwargs):
serializer = PasswordChangeSerializer(data=request.data)
serializer.is_valid(raise_exception=True) # 驗證傳入的資料,如果驗證失敗會直接拋出錯誤
user = request.user # 取得當前登入的使用者物件
old_password = serializer.validated_data['old_password'] # 取得前端傳來的舊密碼
new_password = serializer.validated_data['new_password'] # 取得前端傳來的新密碼
# 檢查舊密碼是否正確,Django 內建的 check_password 會處理雜湊比對
if not check_password(old_password, user.password):
return Response(
{"old_password": ["舊密碼不正確。"]}, # 回傳錯誤訊息
status=status.HTTP_400_BAD_REQUEST # 回傳 HTTP 400 Bad Request 狀態碼
)
# 如果舊密碼正確,就設定新密碼並儲存使用者物件
user.set_password(new_password) # 設定新密碼,Django 會自動雜湊
user.save() # 儲存到資料庫
return Response(
{"detail": "密碼已成功變更。請重新登入。"}, # 回傳成功訊息
status=status.HTTP_200_OK # 回傳 HTTP 200 OK 狀態碼
)
View 負責接收從前端來的請求,它會利用 Serializer 來處理資料和驗證,然後跟 Model 互動來讀寫資料庫。這裡我們使用了 Django REST Framework 的 generics
視圖,這些 View 已經幫我們預設好很多常見的 API 操作了。
from rest_framework import generics, status
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.views import APIView
from .models import User
from .serializers import UserProfileSerializer, UserRegistrationSerializer, PasswordChangeSerializer
from django.contrib.auth.hashers import make_password, check_password
# 使用者註冊 View
# CreateAPIView 適合處理 POST 請求,用於建立新的資源
class UserRegistrationView(generics.CreateAPIView):
queryset = User.objects.all() # 定義這個 View 會操作哪些資料(雖然這裡主要是建立)
serializer_class = UserRegistrationSerializer # 指定要用哪個序列化器來處理資料
permission_classes = [AllowAny] # 允許任何人都可以訪問(因為這是註冊功能)
# 使用者個人資料 View
# RetrieveUpdateAPIView 適合處理 GET(取得單一)和 PUT/PATCH(更新)請求
class UserProfileView(generics.RetrieveUpdateAPIView):
queryset = User.objects.all()
serializer_class = UserProfileSerializer
permission_classes = [IsAuthenticated] # 只有登入的使用者才能訪問
# 覆寫 get_object 方法,確保每個使用者都只能看到自己的個人資料
def get_object(self):
return self.request.user # 直接返回當前發出請求的使用者物件
# 密碼變更 View
# APIView 提供最基本的 View 類別,適合需要高度自訂邏輯的 API
class ChangePasswordView(APIView):
permission_classes = [IsAuthenticated] # 只有登入的使用者才能訪問
# 處理 POST 請求,用於變更密碼
def post(self, request, *args, **kwargs):
serializer = PasswordChangeSerializer(data=request.data)
serializer.is_valid(raise_exception=True) # 驗證傳入的資料,如果驗證失敗會直接拋出錯誤
user = request.user # 取得當前登入的使用者物件
old_password = serializer.validated_data['old_password'] # 取得前端傳來的舊密碼
new_password = serializer.validated_data['new_password'] # 取得前端傳來的新密碼
# 檢查舊密碼是否正確,Django 內建的 check_password 會處理雜湊比對
if not check_password(old_password, user.password):
return Response(
{"old_password": ["舊密碼不正確。"]}, # 回傳錯誤訊息
status=status.HTTP_400_BAD_REQUEST # 回傳 HTTP 400 Bad Request 狀態碼
)
# 如果舊密碼正確,就設定新密碼並儲存使用者物件
user.set_password(new_password) # 設定新密碼,Django 會自動雜湊
user.save() # 儲存到資料庫
return Response(
{"detail": "密碼已成功變更。請重新登入。"}, # 回傳成功訊息
status=status.HTTP_200_OK # 回傳 HTTP 200 OK 狀態碼
)
4. Django App 的 URL 路由 (backend/apps/users/urls.py
)
每個 App 都有自己專屬的 urls.py
檔案,用來定義這個 App 內部所有功能的網址路徑。
from django.urls import path
from . import views # 從當前目錄引入 views.py 裡的所有視圖
# 定義 URL 模式列表
urlpatterns = [
# 使用者註冊的 API 端點
# 當請求來到 'register/' 時,會由 views.UserRegistrationView 處理
# name='user-register' 是給這個 URL 模式一個名稱,方便在程式中引用
path('register/', views.UserRegistrationView.as_view(), name='user-register'),
# 使用者個人資料的 API 端點 (可用於取得和更新資料)
# 當請求來到 'profile/' 時,會由 views.UserProfileView.as_view(), name='user-profile'),
path('profile/', views.UserProfileView.as_view(), name='user-profile'),
# 使用者密碼變更的 API 端點
# 當請求來到 'change-password/' 時,會由 views.ChangePasswordView.as_view(), name='change-password'),
path('change-password/', views.ChangePasswordView.as_view(), name='change-password'),
]
每個 App 都有自己專屬的 urls.py
檔案,用來定義這個 App 內部所有功能的網址路徑。
from django.urls import path
from . import views # 從當前目錄引入 views.py 裡的所有視圖
# 定義 URL 模式列表
urlpatterns = [
# 使用者註冊的 API 端點
# 當請求來到 'register/' 時,會由 views.UserRegistrationView 處理
# name='user-register' 是給這個 URL 模式一個名稱,方便在程式中引用
path('register/', views.UserRegistrationView.as_view(), name='user-register'),
# 使用者個人資料的 API 端點 (可用於取得和更新資料)
# 當請求來到 'profile/' 時,會由 views.UserProfileView.as_view(), name='user-profile'),
path('profile/', views.UserProfileView.as_view(), name='user-profile'),
# 使用者密碼變更的 API 端點
# 當請求來到 'change-password/' 時,會由 views.ChangePasswordView.as_view(), name='change-password'),
path('change-password/', views.ChangePasswordView.as_view(), name='change-password'),
]
5. Django 專案的總 URL 路由 (backend/core_project/urls.py
)
專案的總 urls.py
扮演著交通樞紐的角色,它負責把來自 Nginx 或是直接送過來的網址請求,導向到不同的 App 去處理。
from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt.views import (
TokenObtainPairView, # 這個 View 是用來讓使用者登入後,獲取 JWT 的訪問令牌 (Access Token) 和刷新令牌 (Refresh Token)
TokenRefreshView, # 這個 View 是用來讓使用者用刷新令牌,獲取新的訪問令牌(避免訪問令牌過期需要重新登入)
)
# 定義專案的總 URL 模式
urlpatterns = [
# Django 內建的管理後台,通常用於開發者管理資料庫數據
path('admin/', admin.site.urls),
# JWT 認證相關的 API 端點
# 登入介面,使用者會 POST 帳號密碼到這裡,成功後取得 JWT
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
# 刷新令牌介面,用於獲取新的訪問令牌
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
# Django REST Framework 提供的 browsable API 登入/登出介面 (通常只在開發模式下會用到)
path('api-auth/', include('rest_framework.urls')),
# 專案中各個自訂 App 的 URL 包含設定
# 如果請求的網址以 '/api/users/' 開頭,就交給 apps.users.urls 去處理後面的路徑
path('api/users/', include('apps.users.urls')),
# 同理,處理商家的相關 API
path('api/merchants/', include('apps.merchants.urls')),
# 同理,處理預約的相關 API
path('api/appointments/', include('apps.appointments.urls')),
# 健康檢查端點,通常用於 Docker 或監控系統檢查服務是否正常運行
path('api/health/', include('apps.common.urls')),
# Prometheus 監控指標 (如果您的專案有安裝並啟用 django-prometheus,這行需要取消註釋)
# path('metrics/', include('django_prometheus.urls')),
]
專案的總 urls.py
扮演著交通樞紐的角色,它負責把來自 Nginx 或是直接送過來的網址請求,導向到不同的 App 去處理。
from django.contrib import admin
from django.urls import path, include
from rest_framework_simplejwt.views import (
TokenObtainPairView, # 這個 View 是用來讓使用者登入後,獲取 JWT 的訪問令牌 (Access Token) 和刷新令牌 (Refresh Token)
TokenRefreshView, # 這個 View 是用來讓使用者用刷新令牌,獲取新的訪問令牌(避免訪問令牌過期需要重新登入)
)
# 定義專案的總 URL 模式
urlpatterns = [
# Django 內建的管理後台,通常用於開發者管理資料庫數據
path('admin/', admin.site.urls),
# JWT 認證相關的 API 端點
# 登入介面,使用者會 POST 帳號密碼到這裡,成功後取得 JWT
path('api/token/', TokenObtainPairView.as_view(), name='token_obtain_pair'),
# 刷新令牌介面,用於獲取新的訪問令牌
path('api/token/refresh/', TokenRefreshView.as_view(), name='token_refresh'),
# Django REST Framework 提供的 browsable API 登入/登出介面 (通常只在開發模式下會用到)
path('api-auth/', include('rest_framework.urls')),
# 專案中各個自訂 App 的 URL 包含設定
# 如果請求的網址以 '/api/users/' 開頭,就交給 apps.users.urls 去處理後面的路徑
path('api/users/', include('apps.users.urls')),
# 同理,處理商家的相關 API
path('api/merchants/', include('apps.merchants.urls')),
# 同理,處理預約的相關 API
path('api/appointments/', include('apps.appointments.urls')),
# 健康檢查端點,通常用於 Docker 或監控系統檢查服務是否正常運行
path('api/health/', include('apps.common.urls')),
# Prometheus 監控指標 (如果您的專案有安裝並啟用 django-prometheus,這行需要取消註釋)
# path('metrics/', include('django_prometheus.urls')),
]
系統架構詳解
這個專案的設計目標是打造一個彈性且可擴展的預約平台。以下是各個主要元件及其職責的詳細說明:
這個專案的設計目標是打造一個彈性且可擴展的預約平台。以下是各個主要元件及其職責的詳細說明:
整體架構概覽
首先,我們用一張圖來大致看看各個服務之間的關係:
graph TD
subgraph 前端層
A[客戶端瀏覽器] -->|HTTP/HTTPS| B(Nginx)
B -->|靜態檔案| E[Vue.js 前端]
end
subgraph 後端層
B -->|反向代理| C[Django 後端]
B -->|反向代理| D[FastAPI 推薦引擎]
C -->|內部 API 呼叫| D
end
subgraph 資料層
C -->|SQL 查詢| F[MySQL 資料庫]
C -->|快取/佇列| G[Redis]
D -->|快取| G
end
subgraph 監控層
H[Prometheus] -->|收集指標| C
H -->|收集指標| D
H -->|收集指標| I[MySQL 匯出器]
J[Grafana] -->|查詢| H
end
這張圖描繪了整個系統的宏觀視野,從使用者介面到資料庫,以及負責監控的工具。
首先,我們用一張圖來大致看看各個服務之間的關係:
graph TD
subgraph 前端層
A[客戶端瀏覽器] -->|HTTP/HTTPS| B(Nginx)
B -->|靜態檔案| E[Vue.js 前端]
end
subgraph 後端層
B -->|反向代理| C[Django 後端]
B -->|反向代理| D[FastAPI 推薦引擎]
C -->|內部 API 呼叫| D
end
subgraph 資料層
C -->|SQL 查詢| F[MySQL 資料庫]
C -->|快取/佇列| G[Redis]
D -->|快取| G
end
subgraph 監控層
H[Prometheus] -->|收集指標| C
H -->|收集指標| D
H -->|收集指標| I[MySQL 匯出器]
J[Grafana] -->|查詢| H
end
這張圖描繪了整個系統的宏觀視野,從使用者介面到資料庫,以及負責監控的工具。
各層細部說明
前端層 (Frontend Layer)
Vue.js 3:這是使用者實際操作的介面,以單頁應用程式 (SPA) 的形式提供。它透過 HTTP/HTTPS 與後端 API 互動,取得資料並呈現給使用者。
Nginx:作為整個應用程式的入口點。
負責直接提供 Vue.js 編譯後的靜態前端檔案,確保網頁能快速載入。
同時扮演反向代理的角色,將來自瀏覽器的 API 請求(例如 http://localhost/api/...
)導向到後端的 Django 服務或 FastAPI 服務。
後端層 (Backend Layer)
Django 後端 (Django Backend):這是核心的業務邏輯處理中心。
採用 Django REST Framework (DRF),提供標準化的 RESTful API 介面。
處理使用者管理(註冊、登入、個人資料、權限控制)。
處理商家管理(商家資訊、服務項目、多租戶邏輯)。這裡的多租戶意味著不同商家會有各自獨立的資料或邏輯,但共用同一個平台。
處理預約排程的建立、查詢、更新等功能。
使用 JWT (JSON Web Tokens) 進行身份驗證,確保 API 請求的安全性。
可以根據需求,內部呼叫 FastAPI 推薦引擎,例如在商家列表頁面,先請推薦引擎算出熱門商家。
FastAPI 推薦引擎 (FastAPI Recommendation Engine):
作為一個獨立的微服務,專門負責處理耗時或複雜的推薦演算法。
採用 FastAPI 框架,由於其高性能特性,適合處理即時或計算密集型的任務。
它可以從 Django 後端取得資料,或是直接從 Redis/MySQL 讀取所需資訊來計算推薦結果。
資料層 (Data Layer)
MySQL 資料庫 (MySQL Database):
這是整個平台主要的持久化儲存。所有重要的業務數據,如使用者帳戶、商家資料、預約記錄、服務項目等,都會儲存在這裡。
Django 的 ORM 會負責與 MySQL 的互動,簡化資料庫操作。
Redis (快取與訊息佇列) (Redis - Caching & Message Queue):
快取 (Caching):用於儲存常用但變動不頻繁的資料,例如熱門商家列表、產品庫存等,減少直接查詢 MySQL 的次數,提升 API 響應速度。
訊息佇列 (Message Queue):可以規劃用於處理背景任務,例如發送通知郵件、處理大量數據匯入匯出等,避免阻塞主應用程式的響應。推薦引擎也可以用 Redis 來快取推薦結果。
監控層 (Monitoring Layer)
Prometheus:
一個時間序列資料庫,專門用來收集應用程式和系統的各類指標數據(Metrics)。
Django 後端和 FastAPI 推薦引擎可以透過各自的函式庫(例如 django-prometheus
和 prometheus-fastapi-instrumentator
)暴露指標,Prometheus 會定期來抓取 (scrape) 這些數據。
也可以透過 MySQL Exporter 來收集 MySQL 資料庫的運行狀態指標。
Grafana:
一個強大的數據可視化工具。
它會連接到 Prometheus,並從中查詢指標數據,然後製作成各種圖表和儀表板,讓開發者或維運人員能即時監控系統的健康狀況、性能表現、以及問題偵測。
部署與開發環境 (Deployment & Development Environment)
Docker & Docker Compose:
這個專案的核心部署工具,將每個服務(Nginx、Django、FastAPI、MySQL、Redis、Prometheus、Grafana)都打包成獨立的容器 (Container)。
Docker Compose 則負責協調這些容器,讓它們能夠在同一個環境中輕鬆啟動、停止、互連,大大簡化了開發環境的搭建和部署流程,確保了開發與生產環境的一致性。
前端層 (Frontend Layer)
Vue.js 3:這是使用者實際操作的介面,以單頁應用程式 (SPA) 的形式提供。它透過 HTTP/HTTPS 與後端 API 互動,取得資料並呈現給使用者。
Nginx:作為整個應用程式的入口點。
負責直接提供 Vue.js 編譯後的靜態前端檔案,確保網頁能快速載入。
同時扮演反向代理的角色,將來自瀏覽器的 API 請求(例如
http://localhost/api/...
)導向到後端的 Django 服務或 FastAPI 服務。
後端層 (Backend Layer)
Django 後端 (Django Backend):這是核心的業務邏輯處理中心。
採用 Django REST Framework (DRF),提供標準化的 RESTful API 介面。
處理使用者管理(註冊、登入、個人資料、權限控制)。
處理商家管理(商家資訊、服務項目、多租戶邏輯)。這裡的多租戶意味著不同商家會有各自獨立的資料或邏輯,但共用同一個平台。
處理預約排程的建立、查詢、更新等功能。
使用 JWT (JSON Web Tokens) 進行身份驗證,確保 API 請求的安全性。
可以根據需求,內部呼叫 FastAPI 推薦引擎,例如在商家列表頁面,先請推薦引擎算出熱門商家。
FastAPI 推薦引擎 (FastAPI Recommendation Engine):
作為一個獨立的微服務,專門負責處理耗時或複雜的推薦演算法。
採用 FastAPI 框架,由於其高性能特性,適合處理即時或計算密集型的任務。
它可以從 Django 後端取得資料,或是直接從 Redis/MySQL 讀取所需資訊來計算推薦結果。
資料層 (Data Layer)
MySQL 資料庫 (MySQL Database):
這是整個平台主要的持久化儲存。所有重要的業務數據,如使用者帳戶、商家資料、預約記錄、服務項目等,都會儲存在這裡。
Django 的 ORM 會負責與 MySQL 的互動,簡化資料庫操作。
Redis (快取與訊息佇列) (Redis - Caching & Message Queue):
快取 (Caching):用於儲存常用但變動不頻繁的資料,例如熱門商家列表、產品庫存等,減少直接查詢 MySQL 的次數,提升 API 響應速度。
訊息佇列 (Message Queue):可以規劃用於處理背景任務,例如發送通知郵件、處理大量數據匯入匯出等,避免阻塞主應用程式的響應。推薦引擎也可以用 Redis 來快取推薦結果。
監控層 (Monitoring Layer)
Prometheus:
一個時間序列資料庫,專門用來收集應用程式和系統的各類指標數據(Metrics)。
Django 後端和 FastAPI 推薦引擎可以透過各自的函式庫(例如
django-prometheus
和prometheus-fastapi-instrumentator
)暴露指標,Prometheus 會定期來抓取 (scrape) 這些數據。也可以透過 MySQL Exporter 來收集 MySQL 資料庫的運行狀態指標。
Grafana:
一個強大的數據可視化工具。
它會連接到 Prometheus,並從中查詢指標數據,然後製作成各種圖表和儀表板,讓開發者或維運人員能即時監控系統的健康狀況、性能表現、以及問題偵測。
部署與開發環境 (Deployment & Development Environment)
Docker & Docker Compose:
這個專案的核心部署工具,將每個服務(Nginx、Django、FastAPI、MySQL、Redis、Prometheus、Grafana)都打包成獨立的容器 (Container)。
Docker Compose 則負責協調這些容器,讓它們能夠在同一個環境中輕鬆啟動、停止、互連,大大簡化了開發環境的搭建和部署流程,確保了開發與生產環境的一致性。
服務間的互動流程
大致的資料流向如下:
使用者在瀏覽器上操作前端介面。
瀏覽器發出的請求會先到達 Nginx。
Nginx 根據請求的路徑判斷:
如果是前端靜態檔案(例如 HTML, CSS, JS),Nginx 會直接提供。
如果是 /api/...
開頭的 API 請求,Nginx 會將請求反向代理到對應的 Django 後端或 FastAPI 推薦引擎。
Django 後端接收到請求後:
可能需要與 MySQL 互動,進行資料的增刪改查。
也可能與 Redis 互動,進行快取讀寫或排隊處理背景任務。
在某些情況下,Django 後端會發出內部 API 呼叫給 FastAPI 推薦引擎,來獲取推薦結果。
FastAPI 推薦引擎接收到請求後:
可能會從 Redis 讀取快取的推薦資料,或從 MySQL 獲取原始數據進行推薦演算法計算。
所有服務(Django、FastAPI、MySQL)都會定期將其運行指標暴露出來。
Prometheus 會定期從這些服務中抓取(Scrape)指標數據並儲存。
Grafana 則從 Prometheus 查詢數據,並以直觀的圖表形式展示系統的健康和性能狀況。
這樣的架構設計,讓各個服務職責明確,有利於團隊協作、擴展性,並提供了必要的監控能力,是個相對成熟的微服務應用模式。
大致的資料流向如下:
使用者在瀏覽器上操作前端介面。
瀏覽器發出的請求會先到達 Nginx。
Nginx 根據請求的路徑判斷:
如果是前端靜態檔案(例如 HTML, CSS, JS),Nginx 會直接提供。
如果是
/api/...
開頭的 API 請求,Nginx 會將請求反向代理到對應的 Django 後端或 FastAPI 推薦引擎。
Django 後端接收到請求後:
可能需要與 MySQL 互動,進行資料的增刪改查。
也可能與 Redis 互動,進行快取讀寫或排隊處理背景任務。
在某些情況下,Django 後端會發出內部 API 呼叫給 FastAPI 推薦引擎,來獲取推薦結果。
FastAPI 推薦引擎接收到請求後:
可能會從 Redis 讀取快取的推薦資料,或從 MySQL 獲取原始數據進行推薦演算法計算。
所有服務(Django、FastAPI、MySQL)都會定期將其運行指標暴露出來。
Prometheus 會定期從這些服務中抓取(Scrape)指標數據並儲存。
Grafana 則從 Prometheus 查詢數據,並以直觀的圖表形式展示系統的健康和性能狀況。
這樣的架構設計,讓各個服務職責明確,有利於團隊協作、擴展性,並提供了必要的監控能力,是個相對成熟的微服務應用模式。
七、常見問題 (Q&A)
這裡收集了一些新手可能會有疑問、或是面試時常被問到的問題。希望能幫助您釐清一些觀念。
Q1: 為什麼選擇 Django 而不是 Flask 或其他 Python 網頁框架?
A1:
「選擇 Django 主要考量到它的『自帶電池』特性,這對開發一個功能相對完整的平台來說非常省時。Django 內建了 ORM(資料庫操作)、Admin Site(後台管理)、使用者認證系統等,這些都是中大型專案幾乎都會用到的功能,不用額外花時間去組合和整合。相較於 Flask 這種更輕量的框架,Django 更適合快速開發複雜且功能全面的應用程式,特別是像這種涉及多個業務模組的預約平台。」
Q2: Django 的 ORM (Object-Relational Mapping) 到底有什麼好處?
A2:
「ORM 最大的好處就是讓您可以用 Python 物件的方式來操作資料庫,不用直接寫 SQL 語句。這使得資料庫操作更直觀、程式碼可讀性更高,也減少了因手寫 SQL 語法錯誤而產生的問題。
再來,ORM 具備資料庫抽象化的能力,意思是如果您將來想把 MySQL 換成 PostgreSQL,通常只需要改動設定檔,而不需要改動大量的程式碼,這提供了很大的彈性。對於團隊開發來說,也能讓不同成員在不熟悉特定資料庫語法的情況下,依然能高效協作。」
Q3: 專案中的資料庫遷移(Migrations)除了第一次建立表格,還有什麼重要性?
A3:
「資料庫遷移是 Django 管理資料庫結構變化的重要機制。除了第一次建立表格,它的主要作用是:
追蹤變化:每次您修改 models.py
中的模型定義(例如新增欄位、修改欄位型別),makemigrations
就會生成一個新的遷移檔案,精確記錄這些資料庫的變動。
版本控制:這些遷移檔案會被納入版本控制(Git),這讓團隊協作時,所有成員都能同步資料庫結構的最新狀態,避免『我的機器上可以跑,你的不行』的問題。
安全更新:當您部署應用程式時,只需要運行 migrate,Django 就會自動執行所有未應用的遷移,安全地更新生產環境的資料庫結構,而不需要手動去跑複雜的 SQL 腳本,降低出錯風險。
簡而言之,遷移就是讓資料庫結構的變動,變得有紀錄、可追溯、且可自動化執行。」
Q4: 這個專案提到了「多租戶」,Django 是如何處理這個概念的?
A4:
「在您的專案中提到的『多租戶』,指的是同一個平台要服務多個商家(租戶),每個商家都有自己的數據,但共用一套程式碼和基礎設施。
處理多租戶通常有幾種策略,在這個專案的初始骨架中,最常見的實現方式會是在資料庫層面,透過在主要資料表(例如 merchants、appointments 等)中加入一個 merchant_id 或 owner_user_id 這樣的欄位,來區分數據屬於哪個商家。
當一個商家管理員登入後,所有對應資料的查詢和操作,都會加上該商家的 ID 作為過濾條件,確保他們只能看到和管理自己的數據。雖然目前專案骨架沒有完整的篩選邏輯,但 models.py 中像 merchants 表連結 owner_user_id 到 users 表,就是為多租戶數據隔離打基礎。」
Q5: FastAPI 推薦引擎如何與 Django 配合實現 AI 驅動的功能?
A5:
「FastAPI 推薦引擎在專案中扮演的角色,就是處理那些需要較高性能或複雜計算的『AI 驅動』功能。
職責分離:Django 負責大部分的 CRUD (增刪改查) 和業務邏輯,而推薦這樣需要大量計算或特定演算法的,就交給 FastAPI。這讓兩個服務可以獨立開發、部署和擴展。
資料互動:FastAPI 可能會從 Django 的資料庫(MySQL)拉取用戶行為數據、商家數據等,作為推薦演算法的輸入。同時,它也可能將計算出的推薦結果快取到 Redis,供前端或 Django 快速取用。
數據流向:當前端需要推薦內容時,可能會直接呼叫 FastAPI 的 API。或者,Django 後端在回傳某些頁面數據前,先向 FastAPI 發出內部請求獲取推薦列表,再一併組裝給前端。透過這種方式,AI 模型的推斷結果就能融入到實際的預約流程中。」
Q6: 完成本地設定後,如果要將這個專案部署到生產環境,有哪些是需要特別注意的?
A6:
「從本地開發到生產環境部署,有一些關鍵點需要特別留意:
安全性:
所有密碼、密鑰 (DJANGO_SECRET_KEY
, JWT_SIGNING_KEY
等) 絕對不能用預設值,而且要改用更安全的管理方式,例如環境變數服務(如 AWS Secrets Manager、Vault)而非直接寫在檔案裡。
DJANGO_DEBUG
務必設為 False
,避免在生產環境中暴露錯誤細節。
嚴格設定 ALLOWED_HOSTS
和 CORS (CORS_ALLOWED_ORIGINS
),只允許來自您正式網域的請求。
持久化與備份:確保資料庫數據卷 (docker_volumes/mysql_data
) 有正確的持久化機制,並且設定定期備份策略。
HTTPS:部署 SSL 憑證,讓所有流量走 HTTPS 加密,提升安全性。通常會透過 Nginx 配置。
擴展性與負載平衡:考慮使用 Docker Swarm 或 Kubernetes 來管理多個服務實例,並搭配負載平衡器來分散流量。
日誌與監控:將所有服務的日誌集中收集,搭配 Grafana 等工具建立完整的監控儀表板,以便即時發現和解決問題。」
這裡收集了一些新手可能會有疑問、或是面試時常被問到的問題。希望能幫助您釐清一些觀念。
Q1: 為什麼選擇 Django 而不是 Flask 或其他 Python 網頁框架?
A1:
「選擇 Django 主要考量到它的『自帶電池』特性,這對開發一個功能相對完整的平台來說非常省時。Django 內建了 ORM(資料庫操作)、Admin Site(後台管理)、使用者認證系統等,這些都是中大型專案幾乎都會用到的功能,不用額外花時間去組合和整合。相較於 Flask 這種更輕量的框架,Django 更適合快速開發複雜且功能全面的應用程式,特別是像這種涉及多個業務模組的預約平台。」
Q2: Django 的 ORM (Object-Relational Mapping) 到底有什麼好處?
A2:
「ORM 最大的好處就是讓您可以用 Python 物件的方式來操作資料庫,不用直接寫 SQL 語句。這使得資料庫操作更直觀、程式碼可讀性更高,也減少了因手寫 SQL 語法錯誤而產生的問題。
再來,ORM 具備資料庫抽象化的能力,意思是如果您將來想把 MySQL 換成 PostgreSQL,通常只需要改動設定檔,而不需要改動大量的程式碼,這提供了很大的彈性。對於團隊開發來說,也能讓不同成員在不熟悉特定資料庫語法的情況下,依然能高效協作。」
Q3: 專案中的資料庫遷移(Migrations)除了第一次建立表格,還有什麼重要性?
A3:
「資料庫遷移是 Django 管理資料庫結構變化的重要機制。除了第一次建立表格,它的主要作用是:
追蹤變化:每次您修改
models.py
中的模型定義(例如新增欄位、修改欄位型別),makemigrations
就會生成一個新的遷移檔案,精確記錄這些資料庫的變動。版本控制:這些遷移檔案會被納入版本控制(Git),這讓團隊協作時,所有成員都能同步資料庫結構的最新狀態,避免『我的機器上可以跑,你的不行』的問題。
安全更新:當您部署應用程式時,只需要運行 migrate,Django 就會自動執行所有未應用的遷移,安全地更新生產環境的資料庫結構,而不需要手動去跑複雜的 SQL 腳本,降低出錯風險。
簡而言之,遷移就是讓資料庫結構的變動,變得有紀錄、可追溯、且可自動化執行。」
Q4: 這個專案提到了「多租戶」,Django 是如何處理這個概念的?
A4:
「在您的專案中提到的『多租戶』,指的是同一個平台要服務多個商家(租戶),每個商家都有自己的數據,但共用一套程式碼和基礎設施。
處理多租戶通常有幾種策略,在這個專案的初始骨架中,最常見的實現方式會是在資料庫層面,透過在主要資料表(例如 merchants、appointments 等)中加入一個 merchant_id 或 owner_user_id 這樣的欄位,來區分數據屬於哪個商家。
當一個商家管理員登入後,所有對應資料的查詢和操作,都會加上該商家的 ID 作為過濾條件,確保他們只能看到和管理自己的數據。雖然目前專案骨架沒有完整的篩選邏輯,但 models.py 中像 merchants 表連結 owner_user_id 到 users 表,就是為多租戶數據隔離打基礎。」
Q5: FastAPI 推薦引擎如何與 Django 配合實現 AI 驅動的功能?
A5:
「FastAPI 推薦引擎在專案中扮演的角色,就是處理那些需要較高性能或複雜計算的『AI 驅動』功能。
職責分離:Django 負責大部分的 CRUD (增刪改查) 和業務邏輯,而推薦這樣需要大量計算或特定演算法的,就交給 FastAPI。這讓兩個服務可以獨立開發、部署和擴展。
資料互動:FastAPI 可能會從 Django 的資料庫(MySQL)拉取用戶行為數據、商家數據等,作為推薦演算法的輸入。同時,它也可能將計算出的推薦結果快取到 Redis,供前端或 Django 快速取用。
數據流向:當前端需要推薦內容時,可能會直接呼叫 FastAPI 的 API。或者,Django 後端在回傳某些頁面數據前,先向 FastAPI 發出內部請求獲取推薦列表,再一併組裝給前端。透過這種方式,AI 模型的推斷結果就能融入到實際的預約流程中。」
Q6: 完成本地設定後,如果要將這個專案部署到生產環境,有哪些是需要特別注意的?
A6:
「從本地開發到生產環境部署,有一些關鍵點需要特別留意:
安全性:
所有密碼、密鑰 (
DJANGO_SECRET_KEY
,JWT_SIGNING_KEY
等) 絕對不能用預設值,而且要改用更安全的管理方式,例如環境變數服務(如 AWS Secrets Manager、Vault)而非直接寫在檔案裡。DJANGO_DEBUG
務必設為False
,避免在生產環境中暴露錯誤細節。嚴格設定
ALLOWED_HOSTS
和 CORS (CORS_ALLOWED_ORIGINS
),只允許來自您正式網域的請求。
持久化與備份:確保資料庫數據卷 (
docker_volumes/mysql_data
) 有正確的持久化機制,並且設定定期備份策略。HTTPS:部署 SSL 憑證,讓所有流量走 HTTPS 加密,提升安全性。通常會透過 Nginx 配置。
擴展性與負載平衡:考慮使用 Docker Swarm 或 Kubernetes 來管理多個服務實例,並搭配負載平衡器來分散流量。
日誌與監控:將所有服務的日誌集中收集,搭配 Grafana 等工具建立完整的監控儀表板,以便即時發現和解決問題。」
結語:Django 的學習之路,剛開始而已
恭喜您,已經大致了解 Django 的基礎以及這個專案的後端架構了!這個 booking-platform-django-fastapi
專案,結合了 Django 的穩定性與 FastAPI 的高性能推薦服務,是個不錯的學習範例。
目前這個專案提供的是一個強健的「骨架」與「核心邏輯片段」,方便您在其上進行更深入的開發。文章中提供的程式碼片段,也都是讓您理解核心概念、並能著手去「填充」這些功能的範例。
接下來,您可以:
更深入了解 Model (backend/apps/*/models.py
):看看不同欄位的型別怎麼設定、資料表之間怎麼建立關聯(一對多、多對多),以及 ORM 的增刪改查基本操作。
鑽研 View (backend/apps/*/views.py
):學習怎麼接收 HTTP 請求、處理資料、以及回傳 JSON 格式的回應(因為您的前端是 Vue.js,通常是走 RESTful API 路線)。
釐清 URL 路由 (backend/core_project/urls.py
& backend/apps/*/urls.py
):搞懂網址的請求是怎麼一步步被導向到對應的 View 的。
探索 Django REST Framework (DRF):這是一個很實用的工具,可以幫助您快速打造 RESTful API,這個專案裡也大量用到了它。
Django 的世界其實挺大的,希望這篇文章能為您的學習之旅提供一些幫助,有個不錯的開端。祝您開發順利,做出好東西!
恭喜您,已經大致了解 Django 的基礎以及這個專案的後端架構了!這個 booking-platform-django-fastapi
專案,結合了 Django 的穩定性與 FastAPI 的高性能推薦服務,是個不錯的學習範例。
目前這個專案提供的是一個強健的「骨架」與「核心邏輯片段」,方便您在其上進行更深入的開發。文章中提供的程式碼片段,也都是讓您理解核心概念、並能著手去「填充」這些功能的範例。
接下來,您可以:
更深入了解 Model (
backend/apps/*/models.py
):看看不同欄位的型別怎麼設定、資料表之間怎麼建立關聯(一對多、多對多),以及 ORM 的增刪改查基本操作。鑽研 View (
backend/apps/*/views.py
):學習怎麼接收 HTTP 請求、處理資料、以及回傳 JSON 格式的回應(因為您的前端是 Vue.js,通常是走 RESTful API 路線)。釐清 URL 路由 (
backend/core_project/urls.py
&backend/apps/*/urls.py
):搞懂網址的請求是怎麼一步步被導向到對應的 View 的。探索 Django REST Framework (DRF):這是一個很實用的工具,可以幫助您快速打造 RESTful API,這個專案裡也大量用到了它。
Django 的世界其實挺大的,希望這篇文章能為您的學習之旅提供一些幫助,有個不錯的開端。祝您開發順利,做出好東西!
沒有留言:
張貼留言