DRF Serializers 基礎:序列化與反序列化完全解析 | Django 教學
DRF Serializers(序列化器)是 Django REST Framework 的核心元件,扮演著 Python 物件與 JSON 之間的轉換橋樑。無論是將資料庫的 Model 實例轉換成 API 回應的 JSON 格式,還是將客戶端傳來的 JSON 資料驗證後寫入資料庫,都需要透過 Serializer 來完成。你可以把它理解為 API 層的 Form——Django Form 處理 HTML 表單輸入,Serializer 處理 API 資料的輸入與輸出。本文將從序列化(Serialization)與反序列化(Deserialization)的基本概念出發,帶你掌握 ModelSerializer、欄位類型與選項、驗證機制等核心知識。
序列化 vs 反序列化:Serializer 的雙向轉換
在理解 Serializer 之前,我們需要先釐清兩個核心概念:
- 序列化(Serialization):將 Python 物件(通常是 Model 實例)轉換為 JSON 等可傳輸格式,用於 API 回應輸出
- 反序列化(Deserialization):將客戶端傳來的 JSON 資料轉換為 Python 物件,經過驗證後寫入資料庫
整個流程可以用以下示意圖來理解:
序列化(輸出):Model Instance → Serializer → JSON → HTTP Response
反序列化(輸入):HTTP Request → JSON → Serializer → 驗證 → Model Instance
具體來說,序列化的流程是:
Django ORM Model Instance
↓
Serializer(instance=obj)
↓
serializer.data
↓
{"id": 1, "title": "Hello World", "author": "Alice"}
↓
Response(HTTP 回應)
反序列化的流程則是:
Request Body(HTTP 請求)
↓
{"title": "New Post", "content": "..."}
↓
Serializer(data=request.data)
↓
serializer.is_valid() ← 驗證(Validation)
↓
serializer.save() ← 呼叫 create() 或 update()
↓
Model Instance 儲存到資料庫
基本 Serializer 類別:手動宣告欄位
最基本的 Serializer 需要你手動宣告每個欄位,並實作 create() 和 update() 方法。先來看我們會用到的 Model:
# models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(
'auth.User', on_delete=models.CASCADE, related_name='posts'
)
is_published = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
用基本 Serializer 手動定義序列化器:
# serializers.py
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.Serializer):
"""基本 Serializer:需要手動宣告每個欄位"""
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=200)
content = serializers.CharField()
is_published = serializers.BooleanField(default=False)
created_at = serializers.DateTimeField(read_only=True)
def create(self, validated_data):
"""反序列化時呼叫 serializer.save() 會觸發此方法"""
return Post.objects.create(**validated_data)
def update(self, instance, validated_data):
"""當 serializer 有傳入 instance 時,save() 會觸發此方法"""
instance.title = validated_data.get('title', instance.title)
instance.content = validated_data.get('content', instance.content)
instance.is_published = validated_data.get(
'is_published', instance.is_published
)
instance.save()
return instance
基本 Serializer 的優點是完全可控,但缺點也很明顯——需要大量重複的程式碼。當你的 Serializer 欄位與 Model 高度對應時,ModelSerializer 是更好的選擇。
ModelSerializer:自動對應 Model
ModelSerializer 繼承自 Serializer,它能根據 Model 自動產生欄位定義,並提供預設的 create() 和 update() 方法,大幅減少程式碼量。
# serializers.py
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author', 'is_published', 'created_at']
read_only_fields = ['id', 'created_at']
這短短幾行就等同於前面基本 Serializer 的所有程式碼。ModelSerializer 自動完成了以下工作:
- 根據 Model 欄位自動產生對應的 Serializer 欄位
- 自動產生
validators(例如 unique 約束的驗證) - 提供預設的
create()和update()實作
Meta 類別詳解
Meta 類別是 ModelSerializer 的核心設定,以下是最常用的選項:
model:指定要對應的 Model 類別。
fields:指定要包含哪些欄位。有兩種寫法:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
# 寫法一:明確列出每個欄位(推薦)
fields = ['id', 'title', 'content', 'author', 'is_published', 'created_at']
# 寫法二:包含所有欄位
# fields = '__all__'
建議使用 明確列出欄位 的方式。
'__all__'雖然方便,但當 Model 新增敏感欄位(如password_hash)時,它會自動被暴露在 API 中,造成安全風險。明確列出欄位可以讓你完全掌控 API 回傳的內容。
如果你想排除特定欄位,也可以用 exclude:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
exclude = ['updated_at'] # 排除 updated_at,其餘全部包含
read_only_fields:指定哪些欄位為唯讀,這些欄位只會出現在序列化輸出中,不會接受反序列化的輸入:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author', 'created_at']
read_only_fields = ['id', 'author', 'created_at']
extra_kwargs:為自動產生的欄位額外指定選項,不需要重新宣告整個欄位:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author', 'is_published', 'created_at']
read_only_fields = ['id', 'created_at']
extra_kwargs = {
'content': {'required': True, 'min_length': 10},
'author': {'write_only': False},
'title': {'max_length': 200},
}
常用欄位類型
DRF 提供了豐富的欄位類型,對應不同的資料需求:
基本欄位
| 欄位類型 | 說明 | 對應 Model 欄位 |
|---|---|---|
CharField | 字串 | CharField、TextField |
IntegerField | 整數 | IntegerField、SmallIntegerField |
FloatField | 浮點數 | FloatField |
BooleanField | 布林值 | BooleanField |
DateTimeField | 日期時間 | DateTimeField |
DateField | 日期 | DateField |
EmailField | 電子郵件(含格式驗證) | EmailField |
URLField | URL(含格式驗證) | URLField |
關聯欄位(Relational Fields)
關聯欄位用來處理 ForeignKey 和 ManyToManyField 等關聯關係:
from rest_framework import serializers
from .models import Post, Category
class PostSerializer(serializers.ModelSerializer):
# PrimaryKeyRelatedField:顯示/接受關聯物件的 ID
author = serializers.PrimaryKeyRelatedField(read_only=True)
# StringRelatedField:顯示關聯物件的 __str__() 結果(唯讀)
category_name = serializers.StringRelatedField(source='category')
# SlugRelatedField:使用指定欄位(如 username)來表示關聯
# author_username = serializers.SlugRelatedField(
# slug_field='username',
# source='author',
# read_only=True,
# )
class Meta:
model = Post
fields = ['id', 'title', 'author', 'category_name']
PrimaryKeyRelatedField 是最常用的關聯欄位,讀取時顯示 ID,寫入時也接受 ID 來建立關聯。
欄位選項
每個欄位都可以設定多種選項來控制其行為:
| 選項 | 說明 | 範例 |
|---|---|---|
read_only | 僅出現在序列化輸出,不接受輸入 | serializers.IntegerField(read_only=True) |
write_only | 僅接受輸入,不出現在序列化輸出 | serializers.CharField(write_only=True) |
required | 反序列化時是否為必填 | serializers.CharField(required=True) |
default | 未提供值時使用的預設值 | serializers.BooleanField(default=False) |
source | 指定此欄位對應的 Model 屬性或方法 | serializers.CharField(source='get_full_name') |
source 是一個特別實用的選項,它能讓 Serializer 欄位名稱與 Model 欄位名稱不同:
class PostSerializer(serializers.ModelSerializer):
# 將 Serializer 的 writer 欄位對應到 Model 的 author 欄位
writer = serializers.PrimaryKeyRelatedField(
source='author', read_only=True
)
# 透過 source 存取關聯物件的屬性
author_email = serializers.EmailField(
source='author.email', read_only=True
)
class Meta:
model = Post
fields = ['id', 'title', 'writer', 'author_email']
read_only 和 write_only 的典型應用場景:
class UserSerializer(serializers.ModelSerializer):
# 密碼只在建立時接受輸入,永遠不會出現在回應中
password = serializers.CharField(write_only=True, min_length=8)
# 建立日期只在回應中顯示,不接受輸入
date_joined = serializers.DateTimeField(read_only=True)
class Meta:
model = User
fields = ['id', 'username', 'email', 'password', 'date_joined']
驗證機制(Validation)
Serializer 提供三個層級的驗證機制,從單一欄位到跨欄位的聯合驗證都能處理。
欄位級驗證(Field-level Validation)
定義 validate_<field_name> 方法來驗證單一欄位:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content', 'is_published']
def validate_title(self, value):
"""驗證 title 欄位:方法名稱必須為 validate_ 加上欄位名稱"""
if len(value) < 5:
raise serializers.ValidationError("標題至少需要 5 個字元。")
if 'spam' in value.lower():
raise serializers.ValidationError("標題包含禁止詞彙。")
return value.strip() # 可回傳處理後的值
物件級驗證(Object-level Validation)
覆寫 validate() 方法進行跨欄位的聯合驗證:
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content', 'is_published']
def validate(self, attrs):
"""跨欄位驗證:可存取所有已驗證的欄位值"""
if attrs.get('is_published') and len(attrs.get('content', '')) < 100:
raise serializers.ValidationError(
"已發布的文章內容至少需要 100 個字元。"
)
return attrs
validators 參數
DRF 也提供了可重用的 驗證器(Validators),透過 validators 參數附加到欄位或 Meta 類別中:
from rest_framework.validators import UniqueValidator, UniqueTogetherValidator
class PostSerializer(serializers.ModelSerializer):
title = serializers.CharField(
max_length=200,
validators=[
# UniqueValidator:確保欄位值在資料表中唯一
UniqueValidator(
queryset=Post.objects.all(),
message="已有相同標題的文章存在。"
)
]
)
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author']
validators = [
# UniqueTogetherValidator:確保多個欄位的組合唯一
UniqueTogetherValidator(
queryset=Post.objects.all(),
fields=['title', 'author'],
message="同一作者不能有重複標題的文章。"
)
]
這三個層級的驗證會按照以下順序依序執行:
- 欄位本身的驗證(型別檢查、required、max_length 等)
- 欄位級驗證(
validate_<field_name>方法) - 物件級驗證(
validate方法)
序列化資料與錯誤處理
掌握 serializer.data、serializer.errors 和 serializer.is_valid() 是使用 Serializer 的關鍵。
serializer.data:取得序列化結果
from .models import Post
from .serializers import PostSerializer
# 序列化單一物件
post = Post.objects.get(pk=1)
serializer = PostSerializer(post)
print(serializer.data)
# 輸出:{'id': 1, 'title': 'Hello World', 'content': '...', ...}
# 序列化多個物件(加上 many=True)
posts = Post.objects.all()
serializer = PostSerializer(posts, many=True)
print(serializer.data)
# 輸出:[{'id': 1, ...}, {'id': 2, ...}, ...]
serializer.is_valid():執行驗證
反序列化時,必須先呼叫 is_valid() 通過驗證後才能存取 validated_data 或呼叫 save():
# 反序列化:驗證輸入資料
data = {'title': 'New Post', 'content': 'Hello DRF!'}
serializer = PostSerializer(data=data)
# 方式一:手動處理驗證失敗
if serializer.is_valid():
serializer.save()
else:
print(serializer.errors) # 取得錯誤訊息
# 方式二:驗證失敗時自動拋出例外(推薦)
serializer.is_valid(raise_exception=True)
serializer.save()
# 若驗證失敗,DRF 會自動回傳 HTTP 400 回應
在實際的 API View 中,raise_exception=True 是更常用的方式,因為 DRF 的例外處理器會自動將 ValidationError 轉換為格式一致的 HTTP 400 回應。
serializer.errors:取得錯誤訊息
data = {'title': '', 'content': ''}
serializer = PostSerializer(data=data)
serializer.is_valid()
print(serializer.errors)
# 輸出:
# {
# 'title': [ErrorDetail(string='此欄位不可為空白。', code='blank')],
# 'content': [ErrorDetail(string='此欄位不可為空白。', code='blank')]
# }
錯誤訊息以字典形式呈現,key 是欄位名稱,value 是該欄位的錯誤清單。物件級驗證的錯誤則會出現在 non_field_errors 這個 key 下。
完整範例:Post API 的 Serializer
最後,讓我們把所有概念串起來,建立一個完整的 Post API Serializer:
# serializers.py
from rest_framework import serializers
from rest_framework.validators import UniqueValidator
from .models import Post
class PostSerializer(serializers.ModelSerializer):
"""文章序列化器:涵蓋欄位選項、自訂欄位與驗證機制"""
# SerializerMethodField:動態計算的唯讀欄位
author_name = serializers.SerializerMethodField()
# 自訂驗證器
title = serializers.CharField(
max_length=200,
validators=[
UniqueValidator(
queryset=Post.objects.all(),
message="已有相同標題的文章。"
)
]
)
class Meta:
model = Post
fields = [
'id', 'title', 'content', 'author', 'author_name',
'is_published', 'created_at', 'updated_at',
]
read_only_fields = ['id', 'author', 'created_at', 'updated_at']
extra_kwargs = {
'content': {'required': True, 'min_length': 10},
}
def get_author_name(self, obj):
"""SerializerMethodField 的取值方法,命名規則為 get_<field_name>"""
return obj.author.get_full_name() or obj.author.username
def validate_title(self, value):
"""欄位級驗證:標題不得包含禁止詞彙"""
forbidden_words = ['spam', 'test123']
for word in forbidden_words:
if word in value.lower():
raise serializers.ValidationError(
f"標題包含禁止詞彙:{word}"
)
return value.strip()
def validate(self, attrs):
"""物件級驗證:發布文章時內容不得過短"""
if attrs.get('is_published') and len(attrs.get('content', '')) < 100:
raise serializers.ValidationError(
"已發布的文章內容至少需要 100 個字元。"
)
return attrs
在 View 中使用這個 Serializer:
# views.py
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from .models import Post
from .serializers import PostSerializer
@api_view(['GET', 'POST'])
def post_list(request):
if request.method == 'GET':
# 序列化:Model → JSON
posts = Post.objects.select_related('author').all()
serializer = PostSerializer(posts, many=True)
return Response(serializer.data)
elif request.method == 'POST':
# 反序列化:JSON → Model
serializer = PostSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(author=request.user) # 透過 save() 傳入額外欄位
return Response(serializer.data, status=status.HTTP_201_CREATED)
@api_view(['GET', 'PUT', 'DELETE'])
def post_detail(request, pk):
try:
post = Post.objects.select_related('author').get(pk=pk)
except Post.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
serializer = PostSerializer(post)
return Response(serializer.data)
elif request.method == 'PUT':
# 更新時傳入 instance 與 data
serializer = PostSerializer(post, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data)
elif request.method == 'DELETE':
post.delete()
return Response(status=status.HTTP_204_NO_CONTENT)
API 的輸入與輸出範例:
// POST /api/posts/ — 建立文章(輸入)
{
"title": "DRF Serializers 入門教學",
"content": "Django REST Framework 的 Serializer 是 API 開發的核心元件...",
"is_published": true
}
// 回應(輸出)
{
"id": 1,
"title": "DRF Serializers 入門教學",
"content": "Django REST Framework 的 Serializer 是 API 開發的核心元件...",
"author": 42,
"author_name": "Alice Chen",
"is_published": true,
"created_at": "2026-06-11T10:30:00Z",
"updated_at": "2026-06-11T10:30:00Z"
}
總結
DRF Serializer 是 Django REST Framework 中最核心的元件之一,扮演著 Python 物件與 JSON 之間雙向轉換的橋樑。讓我們回顧本文的重點:
- 序列化 vs 反序列化:序列化是將 Model 實例轉為 JSON 輸出,反序列化是將 JSON 輸入驗證後轉為 Model 實例。
- Serializer vs ModelSerializer:優先使用 ModelSerializer,它能自動從 Model 產生欄位並提供預設的
create()/update()方法。只有在資料不對應 Model 或需要完全客製化時,才使用基本 Serializer。 - Meta 類別:透過
model、fields、read_only_fields、extra_kwargs控制 ModelSerializer 的行為。建議明確列出fields而非使用'__all__'。 - 欄位類型與選項:DRF 提供豐富的欄位類型對應不同資料需求,
read_only、write_only、required、default、source等選項可精細控制欄位行為。 - 三層驗證機制:欄位級驗證(
validate_<field_name>)處理單一欄位,物件級驗證(validate)處理跨欄位邏輯,validators參數提供可重用的驗證器。 - 核心 API:
serializer.data取得序列化結果、serializer.is_valid(raise_exception=True)執行驗證、serializer.errors取得錯誤訊息。
掌握了 Serializer 的基礎知識,你就具備了使用 DRF 建構 RESTful API 的核心能力。在後續的文章中,我們將進一步探索巢狀序列化(Nested Serializers)、自訂欄位與進階驗證等主題。