一、为什么你的数据需要“媒人”?
想象一下这个场景:你精心打造的Django模型对象(比如一个用户档案)准备去前端界面“相亲”,却发现对方只认JSON这种“语言”。两边大眼瞪小眼完全无法沟通!这时候,序列化器就像个专业媒人,热情地站出来:“别急,我帮你们翻译!”
真实开发中的尴尬时刻:
- 前端小哥咆哮:“我要的JSON怎么变成QuerySet天书了?”
- 移动端同事崩溃:“生日日期格式又双叒叕不对!”
- 测试小姐姐怒敲桌子:“提交的评分超过100分为什么没校验?”
这些血泪史,都源于缺少一个靠谱的“数据翻译官”。而Django REST Framework(DRF)中的Serializer,正是为解决这些问题而生。它不单是做简单翻译,还承担着数据验证、关系处理、格式控制等重要使命。
二、三分钟搞懂序列化器的“相亲法则”
2.1 核心工作流程(媒人必备技能)
- 牵线搭桥:把模型对象的属性匹配给JSON字段
- 背景调查:验证数据是否合法合规(比如邮箱格式、年龄范围)
- 关系协调:处理外键、多对多等复杂关系(好比协调双方家庭成员)
- 矛盾调解:数据转换出错时给出明确错误提示
2.2 基础版序列化器(入门级媒人)
from rest_framework import serializers
from myapp.models import Book
class BookSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
title = serializers.CharField(max_length=200)
author = serializers.CharField(max_length=100)
publish_date = serializers.DateField()
price = serializers.DecimalField(max_digits=6, decimal_places=2)
def create(self, validated_data):
return Book.objects.create(**validated_data)
def update(self, instance, validated_data):
instance.title = validated_data.get('title', instance.title)
instance.author = validated_data.get('author', instance.author)
instance.publish_date = validated_data.get('publish_date', instance.publish_date)
instance.price = validated_data.get('price', instance.price)
instance.save()
return instance
这个基础版就像刚入行的媒人,需要手动配置每个字段,还要自己写create和update方法。虽然灵活,但写起来确实费劲。
三、ModelSerializer:你的“婚恋平台”级解决方案
如果觉得基础版太麻烦,DRF还提供了ModelSerializer这个“婚恋平台”——自动匹配,高效快捷!
3.1 智能版序列化器(平台级媒人)
class BookSerializer(serializers.ModelSerializer):
# 自定义计算字段(好比媒人给的附加评价)
discount_price = serializers.SerializerMethodField()
class Meta:
model = Book
fields = ['id', 'title', 'author', 'publish_date', 'price', 'discount_price']
read_only_fields = ['id']
extra_kwargs = {
'price': {'min_value': 0, 'error_messages': {'min_value': '价格不能为负数!'}},
'title': {'trim_whitespace': False}
}
def get_discount_price(self, obj):
# 打9折的计算逻辑
return float(obj.price) * 0.9
优势对比:
- 字段自动生成,不用重复定义
- 自动实现create和update方法
- 默认包含模型级的验证规则
- 支持自定义字段和覆盖默认行为
四、完整实战:从零搭建图书管理API
现在我们来个全流程实战,创建一个带完整CRUD的图书API。
4.1 数据模型(相亲嘉宾资料库)
# models.py
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=200, verbose_name="书名")
author = models.CharField(max_length=100, verbose_name="作者")
publish_date = models.DateField(verbose_name="出版日期")
price = models.DecimalField(max_digits=6, decimal_places=2, verbose_name="价格")
stock = models.IntegerField(default=0, verbose_name="库存")
class Meta:
db_table = 'books'
def __str__(self):
return f"{self.title} - {self.author}"
4.2 序列化器(专业媒人团队)
# serializers.py
from rest_framework import serializers
from .models import Book
import datetime
class BookSerializer(serializers.ModelSerializer):
# 状态字段(根据库存计算)
status = serializers.SerializerMethodField()
# 格式化日期
publish_date = serializers.DateField(format='%Y-%m-%d')
class Meta:
model = Book
fields = '__all__'
read_only_fields = ['id']
def get_status(self, obj):
if obj.stock <= 0:
return "缺货"
elif obj.stock < 10:
return "库存紧张"
else:
return "有货"
def validate_publish_date(self, value):
# 自定义验证:不能出版未来的书籍
if value > datetime.date.today():
raise serializers.ValidationError("出版日期不能是未来!")
return value
def validate(self, data):
# 跨字段验证:经典书籍价格不能太高
if data['publish_date'] < datetime.date(2000, 1, 1) and data['price'] > 100:
raise serializers.ValidationError("经典书籍价格不应超过100元")
return data
4.3 视图层(相亲接待处)
# views.py
from rest_framework import status
from rest_framework.views import APIView
from rest_framework.response import Response
from .models import Book
from .serializers import BookSerializer
class BookListAPI(APIView):
"""处理图书列表的创建和查询"""
def get(self, request):
books = Book.objects.all()
serializer = BookSerializer(books, many=True)
return Response({
'code': 200,
'message': 'success',
'data': serializer.data
})
def post(self, request):
serializer = BookSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response({
'code': 201,
'message': '图书创建成功',
'data': serializer.data
}, status=status.HTTP_201_CREATED)
return Response({
'code': 400,
'message': '数据验证失败',
'errors': serializer.errors
}, status=status.HTTP_400_BAD_REQUEST)
class BookDetailAPI(APIView):
"""处理单本图书的检索、更新和删除"""
def get_object(self, pk):
try:
return Book.objects.get(pk=pk)
except Book.DoesNotExist:
return None
def get(self, request, pk):
book = self.get_object(pk)
if not book:
return Response({
'code': 404,
'message': '图书不存在'
}, status=status.HTTP_404_NOT_FOUND)
serializer = BookSerializer(book)
return Response({
'code': 200,
'message': 'success',
'data': serializer.data
})
def put(self, request, pk):
book = self.get_object(pk)
if not book:
return Response({
'code': 404,
'message': '图书不存在'
}, status=status.HTTP_404_NOT_FOUND)
serializer = BookSerializer(book, data=request.data)
if serializer.is_valid():
serializer.save()
return Response({
'code': 200,
'message': '图书更新成功',
'data': serializer.data
})
return Response({
'code': 400,
'message': '数据验证失败',
'errors': serializer.errors
}, status=status.HTTP_400_BAD_REQUEST)
def delete(self, request, pk):
book = self.get_object(pk)
if not book:
return Response({
'code': 404,
'message': '图书不存在'
}, status=status.HTTP_404_NOT_FOUND)
book.delete()
return Response({
'code': 204,
'message': '图书删除成功'
}, status=status.HTTP_204_NO_CONTENT)
4.4 路由配置(相亲指引牌)
# urls.py
from django.urls import path
from . import views
urlpatterns = [
path('books/', views.BookListAPI.as_view(), name='book-list'),
path('books/<int:pk>/', views.BookDetailAPI.as_view(), name='book-detail'),
]
五、进阶技巧:处理复杂关系就像处理家庭关系
现实中的数据关系,好比复杂的家庭关系网,需要特殊处理。
5.1 嵌套关系处理(带家属相亲)
class AuthorSerializer(serializers.ModelSerializer):
class Meta:
model = Author
fields = ['id', 'name', 'email']
class BookWithAuthorSerializer(serializers.ModelSerializer):
# 嵌套序列化:显示完整作者信息
author_info = AuthorSerializer(source='author', read_only=True)
# 只写字段:创建时只需要作者ID
author_id = serializers.PrimaryKeyRelatedField(
queryset=Author.objects.all(),
write_only=True,
source='author'
)
class Meta:
model = Book
fields = ['id', 'title', 'author_info', 'author_id', 'publish_date', 'price']
5.2 自定义字段验证(媒人的火眼金睛)
def validate_price(self, value):
if value <= 0:
raise serializers.ValidationError("价格必须是正数")
if value > 10000:
raise serializers.ValidationError("价格不能超过10000元")
return value
def validate_title(self, value):
forbidden_words = ['暴力', '色情', '非法']
for word in forbidden_words:
if word in value:
raise serializers.ValidationError(f"标题包含敏感词: {word}")
return value
六、常见踩坑指南(媒人的血泪经验)
6.1 忘了read_only和write_only
# 错误示范
password = serializers.CharField() # 密码会被返回给前端!
# 正确做法
password = serializers.CharField(write_only=True) # 只写不读
6.2 嵌套序列化的循环引用
# 会导致无限递归!
class UserSerializer(serializers.ModelSerializer):
books = BookSerializer(many=True)
class BookSerializer(serializers.ModelSerializer):
author = UserSerializer() # 互相引用,死循环!
6.3 批量操作时的性能问题
# 低效做法:N+1查询问题
books = Book.objects.all()
serializer = BookSerializer(books, many=True)
# 高效做法:使用select_related/prefetch_related
books = Book.objects.select_related('author').prefetch_related('categories').all()
serializer = BookSerializer(books, many=True)
七、测试你的序列化器(媒人上岗考核)
好的媒人要经过严格考核,序列化器也一样需要测试!
# tests.py
from django.test import TestCase
from .models import Book
from .serializers import BookSerializer
import datetime
class BookSerializerTest(TestCase):
def test_valid_book_data(self):
data = {
'title': '测试书籍',
'author': '测试作者',
'publish_date': '2023-01-01',
'price': 50.00,
'stock': 10
}
serializer = BookSerializer(data=data)
self.assertTrue(serializer.is_valid())
def test_invalid_price(self):
data = {
'title': '测试书籍',
'author': '测试作者',
'publish_date': '2023-01-01',
'price': -10, # 负价格,应该失败
'stock': 10
}
serializer = BookSerializer(data=data)
self.assertFalse(serializer.is_valid())
self.assertIn('price', serializer.errors)
八、总结:做个优秀的数据"媒人"
通过今天的学习,你应该已经掌握了Django序列化的核心技能。记住几个关键点:
- 选择合适的序列化器:简单场景用Serializer,模型相关用ModelSerializer
- 验证要全面:字段级验证+对象级验证双保险
- 性能要考虑:避免N+1查询,合理使用读写权限
- 错误要明确:给前端清晰的错误提示信息
现在,你的Django模型对象可以愉快地和JSON"相亲成功"了!下次当看到数据在不同系统间流畅传输时,记得感谢你这个尽职的"数据媒人"。

被折叠的 条评论
为什么被折叠?



