Django Rest Framework (DRF) 新手指南:用 ViewSets 快速構建 CRUD API

更新日期: 2025 年 1 月 29 日

在現代的 Web 開發中,CRUD(Create、Read、Update、Delete)是不可或缺的功能,幾乎所有的應用程式都需要進行資料的新增、查詢、修改和刪除操作。

幸運的是,Django Rest Framework (DRF) 提供了一種高效的方式,幫助開發者快速構建這些功能。

這篇文章將帶你一步步了解 CRUD 的基本概念,並學習如何用 DRF 的 ModelViewSet 來實現完整的 API。


什麼是 CRUD?

CRUD 是操作資料的四個基本功能,每個字母代表一個操作:

  1. Create:建立新資料(新增)。
  2. Read:讀取資料(查詢)。
  3. Update:更新資料(修改)。
  4. Delete:刪除資料(刪除)。

在 Web 開發中,CRUD 操作通常通過 API 實現,並對應於不同的 HTTP 方法:

  • POST:用於建立資料。
  • GET:用於查詢資料(單筆或多筆)。
  • PUT/PATCH:用於更新資料。
  • DELETE:用於刪除資料。

使用 ViewSets 快速構建 CRUD API

Django Rest Framework 的 ViewSets 是一種快速構建 API 的工具。

特別是它的 ModelViewSet,可以在極少的代碼下自動生成完整的 CRUD 功能,讓開發者專注於更高層的邏輯實現。

要使用 ModelViewSet,只需要指定兩個核心部分:

  1. 數據來源:定義資料從哪裡查詢,例如 Django 的模型(Model)。
  2. 資料格式:如何轉換資料,通常是 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() 方法
  • 等等…

這種設計的優點是:

  1. 靈活性:可以根據需要組合不同的功能
  2. 可重用性:Mixin 可以被多個不同的類使用
  3. 單一職責:每個 Mixin 只負責一個特定功能
  4. 好維護:如果某個功能有問題,可以直接修改對應的 Mixin

所以當我們說「混入」時,實際上是在說「將這些獨立的功能組件組合到一個類中」的過程。

這比傳統的繼承更靈活,因為你可以自由組合需要的功能,而不是被迫繼承所有父類的功能。

內部組件的作用

GenericViewSet

  • 提供了 ViewSet 的基礎功能,例如 URL 路徑解析、動態方法路由(比如將 HTTP 方法對應到具體的行為)。
  • 負責調用相應的操作方法(例如 create()list() 等)。
  • 支援 querysetserializer_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 的關鍵功能

GenericViewSetModelViewSet 的基礎,它提供了以下重要功能:

  1. get_queryset()
    • 返回配置的 queryset,用於查詢資料。
    • 支援自定義過濾邏輯。
  2. get_serializer_class()
    • 返回配置的 serializer_class,用於處理資料的格式轉換。
  3. HTTP 方法與行為映射
    • 解析 HTTP 方法,並將它們映射到 CRUD 操作(如 GETlist()retrieve()POSTcreate())。

如何運作起來?

當一個 HTTP 請求進入時,ModelViewSet 的工作流程如下:

  1. 匹配 URL 和行為
    • 使用 DRF 的路由系統將 URL 路徑(如 /mymodel//mymodel/<id>/)對應到 ViewSet 中的行為(如 list()retrieve())。
  2. 調用對應的方法
    • 例如,GET /mymodel/ 會觸發 list() 方法,POST /mymodel/ 會觸發 create() 方法。
  3. 執行資料處理
    • 根據 querysetserializer_class,執行資料庫操作和格式轉換。
  4. 返回結果
    • 返回格式化的 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 路徑綁定,這裡分成三個部分來看:
    1. r'mymodel'
      • 定義 API 的路徑前綴,這裡是 mymodel
      • 訪問 /mymodel/ 就會觸發 MyModelViewSet 中的操作。
    2. MyModelViewSet
      • 綁定的 ViewSet 類,負責處理這個路徑的所有請求。
    3. 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:

  1. 列出所有資料:http://127.0.0.1:8000/mymodel/
  2. 操作單筆資料:http://127.0.0.1:8000/mymodel/<id>/

DRF 的優勢

  1. 節省時間:只需極少代碼即可實現完整的 CRUD 功能。
  2. 結構清晰:所有邏輯由 DRF 處理,代碼簡潔易讀。
  3. 靈活擴展:可根據需求覆寫 ModelViewSet 的方法來實現更複雜的邏輯。

透過 DRF 的幫助,即使是新手也能輕鬆構建高效的 API!

Similar Posts