Django Rest Framework (DRF) 新手指南:用 ViewSets 快速構建 CRUD API
更新日期: 2025 年 1 月 29 日
本文為 Django 登入註冊功能(後端)系列文,第 3 篇
- 新手指南|使用 Django 實現後端會員登入、註冊功能
- Django Rest Framework (DRF) 新手指南:為什麼選擇 DRF 建構 API?
- Django Rest Framework (DRF) 新手指南:用 ViewSets 快速構建 CRUD API 👈所在位置
- 初學者指南:理解 Token 認證及其運作方式
- Django REST Framework (DRF) 認證方式、預設權限規則|新手指南
- Django Rest Framework 入門指南:如何正確處理 HTTP 狀態碼
- 初學者指南:深入了解 Django 的 create_user 方法
- 新手指南:深入了解 Django 的 authenticate 方法
- Django REST Framework|@permission_classes指導手冊
- 使用 Postman 測試 Django 後端的註冊、登入與登出功能
在現代的 Web 開發中,CRUD(Create、Read、Update、Delete)是不可或缺的功能,幾乎所有的應用程式都需要進行資料的新增、查詢、修改和刪除操作。
幸運的是,Django Rest Framework (DRF) 提供了一種高效的方式,幫助開發者快速構建這些功能。
這篇文章將帶你一步步了解 CRUD 的基本概念,並學習如何用 DRF 的 ModelViewSet
來實現完整的 API。
什麼是 CRUD?
CRUD 是操作資料的四個基本功能,每個字母代表一個操作:
- Create:建立新資料(新增)。
- Read:讀取資料(查詢)。
- Update:更新資料(修改)。
- Delete:刪除資料(刪除)。
在 Web 開發中,CRUD 操作通常通過 API 實現,並對應於不同的 HTTP 方法:
- POST:用於建立資料。
- GET:用於查詢資料(單筆或多筆)。
- PUT/PATCH:用於更新資料。
- DELETE:用於刪除資料。
使用 ViewSets 快速構建 CRUD API
Django Rest Framework 的 ViewSets 是一種快速構建 API 的工具。
特別是它的 ModelViewSet,可以在極少的代碼下自動生成完整的 CRUD 功能,讓開發者專注於更高層的邏輯實現。
要使用 ModelViewSet
,只需要指定兩個核心部分:
- 數據來源:定義資料從哪裡查詢,例如 Django 的模型(Model)。
- 資料格式:如何轉換資料,通常是 JSON 格式。
如何用 ModelViewSet 建立 CRUD API
以下是一個簡單的範例,逐步展示如何用 DRF 快速建立 API:
建立模型(Model)
首先,定義一個 Django 模型,代表資料表:
# models.py
from django.db import models
class MyModel(models.Model):
name = models.CharField(max_length=100)
description = models.TextField()
定義序列化器(Serializer)
DRF 的 Serializer 負責將模型資料轉換為 JSON 格式,或從 JSON 格式還原為模型資料。
# serializers.py
from rest_framework import serializers
from .models import MyModel
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = '__all__' # 包含所有欄位
建立視圖集(ViewSet)
使用 DRF 的 ModelViewSet
,幾行代碼即可生成完整的 CRUD 功能:
# views.py
from rest_framework.viewsets import ModelViewSet
from .models import MyModel
from .serializers import MyModelSerializer
class MyModelViewSet(ModelViewSet):
queryset = MyModel.objects.all() # 定義查詢來源
serializer_class = MyModelSerializer # 定義序列化器
自動生成的 API 功能
根據上述代碼,DRF 會自動生成以下 API 功能,無需額外撰寫邏輯:
HTTP 方法 | API 路徑 | 操作 |
---|---|---|
GET | /mymodel/ | 列出所有資料 |
POST | /mymodel/ | 新增資料 |
GET | /mymodel/<id>/ | 獲取指定 ID 的資料 |
PUT | /mymodel/<id>/ | 更新指定 ID 的資料 |
DELETE | /mymodel/<id>/ | 刪除指定 ID 的資料 |
API 功能詳解
列出所有資料(GET /mymodel/
)
- 返回資料表中的所有記錄。
- 範例返回結果:
[
{
"id": 1,
"name": "Test Item",
"description": "This is a test item."
},
{
"id": 2,
"name": "Another Item",
"description": "Another description."
}
]
新增資料(POST /mymodel/
)
- 用於新增一筆資料。
- 請求範例:
{
"name": "New Item",
"description": "Description for new item."
}
- 成功新增後返回新增的完整資料。
查詢單筆資料(GET /mymodel/<id>/
)
- 用於查詢指定 ID 的資料。
- 範例請求:
GET /mymodel/1/
- 返回結果:
{
"id": 1,
"name": "Test Item",
"description": "This is a test item."
}
更新資料(PUT /mymodel/<id>/
)
- 用於修改指定 ID 的資料。
- 請求範例:
{
"name": "Updated Item",
"description": "Updated description."
}
刪除資料(DELETE /mymodel/<id>/
)
- 用於刪除指定 ID 的資料。
- 成功刪除後返回 HTTP 狀態碼 204,無其他內容。
ModelViewSet 組成元件
ModelViewSet
是 Django Rest Framework (DRF) 提供的一個高級類,它的內部邏輯其實是基於其他核心組件(例如 GenericViewSet
和各種「混入類 (Mixins)」)構建的。
這些組件共同實現了 ModelViewSet
的 CRUD 功能。
以下是 ModelViewSet
的結構和內部工作原理。
ModelViewSet 的定義
ModelViewSet
的實現大致如下(簡化版):
from rest_framework.viewsets import GenericViewSet
from rest_framework import mixins
class ModelViewSet(
mixins.CreateModelMixin, # 提供 create() 方法
mixins.RetrieveModelMixin, # 提供 retrieve() 方法
mixins.UpdateModelMixin, # 提供 update() 和 partial_update() 方法
mixins.DestroyModelMixin, # 提供 destroy() 方法
mixins.ListModelMixin, # 提供 list() 方法
GenericViewSet # 提供核心的 ViewSet 功能
):
"""
一個包含所有 CRUD 行為的 ViewSet。
"""
pass
可以看到,ModelViewSet
的核心是通過「混入類 (Mixins)」和 GenericViewSet
實現的。每個 Mixin 提供一個具體的功能,例如處理新增、查詢、更新或刪除。
補充:混入類是什麼
混入(Mixin)是 Python 中一種特殊的類設計模式,可以理解為「功能組件」。
它不同於一般的繼承關係,主要用來「組合」或「混合」不同的功能到一個類中。
讓我用一個簡單的例子來說明:
# 這是一個 Mixin,只實現一個特定功能 class SwimMixin: def swim(self): return "我會游泳" # 這是另一個 Mixin class FlyMixin: def fly(self): return "我會飛" # 基礎類 class Animal: def __init__(self, name): self.name = name def eat(self): return "我會吃東西" # 使用多重繼承來「混入」這些功能 class Duck(Animal, SwimMixin, FlyMixin): pass # 使用示例 donald = Duck("唐老鴨") print(donald.swim()) # 輸出:我會游泳 print(donald.fly()) # 輸出:我會飛 print(donald.eat()) # 輸出:我會吃東西
回到 DRF 的 ModelViewSet:
class ModelViewSet(CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin, ListModelMixin, GenericViewSet): pass
每個 Mixin 都提供了一個特定的功能:
- CreateModelMixin 提供了 create() 方法
- ListModelMixin 提供了 list() 方法
- RetrieveModelMixin 提供了 retrieve() 方法
- 等等…
這種設計的優點是:
- 靈活性:可以根據需要組合不同的功能
- 可重用性:Mixin 可以被多個不同的類使用
- 單一職責:每個 Mixin 只負責一個特定功能
- 好維護:如果某個功能有問題,可以直接修改對應的 Mixin
所以當我們說「混入」時,實際上是在說「將這些獨立的功能組件組合到一個類中」的過程。
這比傳統的繼承更靈活,因為你可以自由組合需要的功能,而不是被迫繼承所有父類的功能。
內部組件的作用
GenericViewSet
- 提供了 ViewSet 的基礎功能,例如 URL 路徑解析、動態方法路由(比如將 HTTP 方法對應到具體的行為)。
- 負責調用相應的操作方法(例如
create()
、list()
等)。 - 支援
queryset
和serializer_class
的自動處理。
各種 Mixin 類
這些 Mixin 類負責實現具體的 CRUD 操作邏輯。
CreateModelMixin
提供新增資料的 create()
方法:
- 驗證用戶端傳遞的資料是否合法。
- 呼叫序列化器的
save()
方法將資料儲存到資料庫。 - 返回新增的資料和適當的 HTTP 狀態碼。
class CreateModelMixin:
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) # 驗證資料
self.perform_create(serializer) # 儲存資料
headers = self.get_success_headers(serializer.data)
return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)
def perform_create(self, serializer):
serializer.save() # 執行儲存
RetrieveModelMixin
提供查詢單筆資料的 retrieve()
方法:
- 根據 URL 中的主鍵(
<id>
)查詢資料 - 返回序列化後的單筆資料。
class RetrieveModelMixin:
def retrieve(self, request, *args, **kwargs):
instance = self.get_object() # 查詢單筆資料
serializer = self.get_serializer(instance)
return Response(serializer.data)
UpdateModelMixin
提供更新資料的 update()
和 partial_update()
方法:
- 處理完整更新(
PUT
)和部分更新(PATCH
)。 - 驗證資料後儲存到資料庫。
class UpdateModelMixin:
def update(self, request, *args, **kwargs):
partial = kwargs.pop('partial', False) # 是否部分更新
instance = self.get_object()
serializer = self.get_serializer(instance, data=request.data, partial=partial)
serializer.is_valid(raise_exception=True) # 驗證資料
self.perform_update(serializer)
return Response(serializer.data)
def perform_update(self, serializer):
serializer.save() # 儲存更新
DestroyModelMixin
提供刪除資料的 destroy()
方法:
- 根據主鍵刪除資料庫中的資料。
class DestroyModelMixin:
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
self.perform_destroy(instance)
return Response(status=status.HTTP_204_NO_CONTENT)
def perform_destroy(self, instance):
instance.delete() # 執行刪除
ListModelMixin
提供列出所有資料的 list()
方法:
查詢 queryset
中的所有資料,並返回序列化後的結果。
class ListModelMixin:
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset()) # 過濾查詢集
page = self.paginate_queryset(queryset) # 支援分頁
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(queryset, many=True)
return Response(serializer.data)
繼承關係圖
+------------------------------------------+
| ModelViewSet |
| (封裝所有 CRUD 操作的視圖集) |
+------------------------------------------+
▲
| 繼承
+--------------------------------+
| GenericViewSet |
| (提供序列化、查詢集等通用功能) |
+--------------------------------+
▲
| 混入
+--------------------------------+
| 五個核心功能 Mixins |
|--------------------------------|
| ■ ListModelMixin (列表) |
| ■ CreateModelMixin (創建) |
| ■ RetrieveModelMixin (檢視) |
| ■ UpdateModelMixin (更新) |
| ■ DestroyModelMixin (刪除) |
+--------------------------------+
▲
| 基於
+--------------------------------+
| Django Rest Framework |
| (DRF 核心功能) |
+--------------------------------+
GenericViewSet
的關鍵功能
GenericViewSet
是 ModelViewSet
的基礎,它提供了以下重要功能:
get_queryset()
- 返回配置的
queryset
,用於查詢資料。 - 支援自定義過濾邏輯。
- 返回配置的
get_serializer_class()
- 返回配置的
serializer_class
,用於處理資料的格式轉換。
- 返回配置的
- HTTP 方法與行為映射
- 解析 HTTP 方法,並將它們映射到 CRUD 操作(如
GET
→list()
或retrieve()
,POST
→create()
)。
- 解析 HTTP 方法,並將它們映射到 CRUD 操作(如
如何運作起來?
當一個 HTTP 請求進入時,ModelViewSet
的工作流程如下:
- 匹配 URL 和行為
- 使用 DRF 的路由系統將 URL 路徑(如
/mymodel/
或/mymodel/<id>/
)對應到 ViewSet 中的行為(如list()
或retrieve()
)。
- 使用 DRF 的路由系統將 URL 路徑(如
- 調用對應的方法
- 例如,
GET /mymodel/
會觸發list()
方法,POST /mymodel/
會觸發create()
方法。
- 例如,
- 執行資料處理
- 根據
queryset
和serializer_class
,執行資料庫操作和格式轉換。
- 根據
- 返回結果
- 返回格式化的 JSON 資料和適當的 HTTP 狀態碼。
┌──────────────────────────────┐
│ HTTP 請求進入 │
│ (如 GET /mymodel/ ) │
└─────────────┬────────────────┘
↓
┌──────────────────────────────┐
│ 1. 匹配 URL 和行為 │
│ │
│ ┌────────────────────────┐ │
│ │ DRF 路由系統 │ │
│ │ /mymodel/ → list() │ │
│ │ /mymodel/1/ → retrieve│ │
│ └────────────────────────┘ │
└─────────────┬────────────────┘
↓
┌──────────────────────────────┐
│ 2. 調用對應的方法 │
│ │
│ ┌────────────────────────┐ │
│ │ GET → list() │ │
│ │ POST → create() │ │
│ │ PUT → update() │ │
│ └────────────────────────┘ │
└─────────────┬────────────────┘
↓
┌──────────────────────────────┐
│ 3. 執行資料處理 │
│ │
│ ┌────────────────────────┐ │
│ │ • 使用 queryset 查詢 │ │
│ │ • 使用 serializer │ │
│ │ 處理資料格式 │ │
│ └────────────────────────┘ │
└─────────────┬────────────────┘
↓
┌──────────────────────────────┐
│ 4. 返回結果 │
│ │
│ ┌────────────────────────┐ │
│ │ • JSON 資料 │ │
│ │ • HTTP 狀態碼 │ │
│ └────────────────────────┘ │
└──────────────────────────────┘
啟用這些 API
Django Rest Framework (DRF) 提供了一個簡單的 路由系統,用於將 ViewSet 類和 URL 路徑進行綁定。
DRF 的路由系統能自動生成 RESTful API 所需的 URL,並且會根據 ViewSet
的定義,對應到不同的 HTTP 方法(如 GET、POST、PUT、DELETE)。
與 Django 傳統的
urlpatterns
定義方式不同,DRF 的路由系統專為 ViewSet 設計,讓 API 開發更加簡潔。
代碼逐步解釋
from rest_framework.routers import DefaultRouter
from .views import MyModelViewSet
# 1. 創建一個 DefaultRouter 實例
router = DefaultRouter()
# 2. 註冊 ViewSet 並定義路徑
router.register(r'mymodel', MyModelViewSet, basename='mymodel')
# 3. 將 router 的 URL 模式加到 urlpatterns 中
urlpatterns = router.urls
詳細拆解
創建一個 DefaultRouter
實例
router = DefaultRouter()
DefaultRouter
是 DRF 提供的工具,用於管理 ViewSet 和 URL 的綁定。- 它會自動生成對應的路由,無需手動定義 URL 模式。
- 與傳統的 Django 路由相比,這樣做更簡單,且專門針對 RESTful API。
註冊 ViewSet
router.register(r'mymodel', MyModelViewSet, basename='mymodel')
router.register()
的作用是將 ViewSet 與 URL 路徑綁定,這裡分成三個部分來看:r'mymodel'
- 定義 API 的路徑前綴,這裡是
mymodel
。 - 訪問
/mymodel/
就會觸發MyModelViewSet
中的操作。
- 定義 API 的路徑前綴,這裡是
MyModelViewSet
- 綁定的 ViewSet 類,負責處理這個路徑的所有請求。
basename='mymodel'
- 指定這組 API 的名稱基礎,通常與前綴相同。
- DRF 會基於這個名稱生成反向查詢(reverse lookup)名稱,比如:
- 列表操作:
mymodel-list
- 單筆操作:
mymodel-detail
- 列表操作:
生成 URL 模式
urlpatterns = router.urls
router.urls
包含了根據ViewSet
自動生成的所有 URL。- 無需手動定義
urlpatterns
,這些路由會直接添加到 Django 的 URL 配置中。
生成的 URL 路徑
註冊完後,DRF 的路由系統會自動生成以下 API 路徑:
HTTP 方法 | URL 路徑 | 對應行為 | 描述 |
---|---|---|---|
GET | /mymodel/ | list() | 列出所有資料 |
POST | /mymodel/ | create() | 新增資料 |
GET | /mymodel/<id>/ | retrieve() | 查詢指定 ID 的資料 |
PUT | /mymodel/<id>/ | update() | 修改指定 ID 的資料 |
PATCH | /mymodel/<id>/ | partial_update() | 局部修改指定 ID 的資料 |
DELETE | /mymodel/<id>/ | destroy() | 刪除指定 ID 的資料 |
你現在可以直接通過以上 URL 測試 API 功能,無需手動定義每一個路徑。
為什麼需要 DefaultRouter
?
在傳統的 Django URL 配置中,你需要手動定義每個路徑。例如:
from django.urls import path
from .views import MyModelViewSet
urlpatterns = [
path('mymodel/', MyModelViewSet.as_view({'get': 'list', 'post': 'create'})),
path('mymodel/<int:pk>/', MyModelViewSet.as_view({'get': 'retrieve', 'put': 'update', 'delete': 'destroy'})),
]
如果有多個 ViewSet,手動維護這些路徑會變得很麻煩。而使用 DRF 的 DefaultRouter
:
- 可以自動生成完整的路由配置。
- API 的維護變得更加輕鬆,尤其是當你有很多類似的 API 時。
完整範例:urls.py
這是完整的路由配置代碼:
from rest_framework.routers import DefaultRouter
from .views import MyModelViewSet
router = DefaultRouter()
router.register(r'mymodel', MyModelViewSet, basename='mymodel')
urlpatterns = router.urls
測試 API
啟動開發伺服器:
python manage.py runserver
然後訪問以下 URL 測試您的 API:
- 列出所有資料:
http://127.0.0.1:8000/mymodel/
- 操作單筆資料:
http://127.0.0.1:8000/mymodel/<id>/
DRF 的優勢
- 節省時間:只需極少代碼即可實現完整的 CRUD 功能。
- 結構清晰:所有邏輯由 DRF 處理,代碼簡潔易讀。
- 靈活擴展:可根據需求覆寫
ModelViewSet
的方法來實現更複雜的邏輯。
透過 DRF 的幫助,即使是新手也能輕鬆構建高效的 API!