Logo

新人日誌

首頁關於我部落格

新人日誌

Logo

網站會不定期發佈技術筆記、職場心得相關的內容,歡迎關注本站!

網站
首頁關於我部落格
部落格
分類系列文

© 新人日誌. All rights reserved. 2020-present.

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

最後更新:2025年1月29日Python

本文為 Django 登入註冊功能(後端)系列文,第 3 篇

  1. 新手指南|使用 Django 實現後端會員登入、註冊功能
  2. Django Rest Framework (DRF) 新手指南:為什麼選擇 DRF 建構 API?
  3. Django Rest Framework (DRF) 新手指南:用 ViewSets 快速構建 CRUD API 👈所在位置
  4. 初學者指南:理解 Token 認證及其運作方式
  5. Django REST Framework (DRF) 認證方式、預設權限規則|新手指南
  6. Django Rest Framework 入門指南:如何正確處理 HTTP 狀態碼
  7. 初學者指南:深入了解 Django 的 create_user 方法
  8. 新手指南:深入了解 Django 的 authenticate 方法
  9. Django REST Framework|@permission_classes指導手冊
  10. 使用 Postman 測試 Django 後端的註冊、登入與登出功能

在現代的 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 路徑/mymodel/
操作列出所有資料
API 路徑/mymodel/
操作新增資料
API 路徑/mymodel/<id>/
操作獲取指定 ID 的資料
API 路徑/mymodel/<id>/
操作更新指定 ID 的資料
API 路徑/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() 等)。
  • 支援 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 的基礎,它提供了以下重要功能:

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

如何運作起來?

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

  1. 匹配 URL 和行為
    • 使用 DRF 的路由系統將 URL 路徑(如 /mymodel/ 或 /mymodel/<id>/)對應到 ViewSet 中的行為(如 list() 或 retrieve())。
  2. 調用對應的方法
    • 例如,GET /mymodel/ 會觸發 list() 方法,POST /mymodel/ 會觸發 create() 方法。
  3. 執行資料處理
    • 根據 queryset 和 serializer_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 路徑/mymodel/
對應行為list()
描述列出所有資料
URL 路徑/mymodel/
對應行為create()
描述新增資料
URL 路徑/mymodel/<id>/
對應行為retrieve()
描述查詢指定 ID 的資料
URL 路徑/mymodel/<id>/
對應行為update()
描述修改指定 ID 的資料
URL 路徑/mymodel/<id>/
對應行為partial_update()
描述局部修改指定 ID 的資料
URL 路徑/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!

目前還沒有留言,成為第一個留言的人吧!

發表留言

留言將在審核後顯示。

Python

目錄

  • 什麼是 CRUD?
  • 使用 ViewSets 快速構建 CRUD API
  • 如何用 ModelViewSet 建立 CRUD API
  • 建立模型(Model)
  • 定義序列化器(Serializer)
  • 建立視圖集(ViewSet)
  • 自動生成的 API 功能
  • API 功能詳解
  • 列出所有資料(GET /mymodel/)
  • 新增資料(POST /mymodel/)
  • 查詢單筆資料(GET /mymodel/<id>/)
  • 更新資料(PUT /mymodel/<id>/)
  • 刪除資料(DELETE /mymodel/<id>/)
  • ModelViewSet 組成元件
  • ModelViewSet 的定義
  • 內部組件的作用
  • GenericViewSet 的關鍵功能
  • 如何運作起來?
  • 啟用這些 API
  • 代碼逐步解釋
  • 詳細拆解
  • 生成的 URL 路徑
  • 為什麼需要 DefaultRouter?
  • 完整範例:urls.py
  • 測試 API
  • DRF 的優勢