Django基础教程(九十六)Django序列化之创建一个序列化类:当Django遇上JSON:你的数据“相亲”指南

一、为什么你的数据需要“媒人”?

想象一下这个场景:你精心打造的Django模型对象(比如一个用户档案)准备去前端界面“相亲”,却发现对方只认JSON这种“语言”。两边大眼瞪小眼完全无法沟通!这时候,序列化器就像个专业媒人,热情地站出来:“别急,我帮你们翻译!”

真实开发中的尴尬时刻

  • 前端小哥咆哮:“我要的JSON怎么变成QuerySet天书了?”
  • 移动端同事崩溃:“生日日期格式又双叒叕不对!”
  • 测试小姐姐怒敲桌子:“提交的评分超过100分为什么没校验?”

这些血泪史,都源于缺少一个靠谱的“数据翻译官”。而Django REST Framework(DRF)中的Serializer,正是为解决这些问题而生。它不单是做简单翻译,还承担着数据验证、关系处理、格式控制等重要使命。

二、三分钟搞懂序列化器的“相亲法则”

2.1 核心工作流程(媒人必备技能)
  1. 牵线搭桥:把模型对象的属性匹配给JSON字段
  2. 背景调查:验证数据是否合法合规(比如邮箱格式、年龄范围)
  3. 关系协调:处理外键、多对多等复杂关系(好比协调双方家庭成员)
  4. 矛盾调解:数据转换出错时给出明确错误提示
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序列化的核心技能。记住几个关键点:

  1. 选择合适的序列化器:简单场景用Serializer,模型相关用ModelSerializer
  2. 验证要全面:字段级验证+对象级验证双保险
  3. 性能要考虑:避免N+1查询,合理使用读写权限
  4. 错误要明确:给前端清晰的错误提示信息

现在,你的Django模型对象可以愉快地和JSON"相亲成功"了!下次当看到数据在不同系统间流畅传输时,记得感谢你这个尽职的"数据媒人"。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值