当前位置:首页>python>Python学习之路 - 金庸武侠版 第三重:《独孤九剑》(绝学篇 - 8)

Python学习之路 - 金庸武侠版 第三重:《独孤九剑》(绝学篇 - 8)

  • 2026-09-06 14:06:33
Python学习之路 - 金庸武侠版 第三重:《独孤九剑》(绝学篇 - 8)

喜欢你就关注我

绝世武功:独孤九剑(无招胜有招,适合Web开发,变化多端)
独孤九剑·破箭式
Python学习之路 - 金庸武侠版 第三重:《独孤九剑》(绝学篇 - 4)
Python学习之路 - 金庸武侠版 第三重:《独孤九剑》(绝学篇 - 5)
Python学习之路 - 金庸武侠版 第三重:《独孤九剑》(绝学篇 - 6)
Python学习之路 - 金庸武侠版 第三重:《独孤九剑》(绝学篇 - 7)
PART 01.
RootK1tana
Django DRF中的序列化器(续)

01

序列化器(高级)

动态字段(dynamic fields)

根据请求参数动态返回不同字段(如 ?fields=id,title 只返回 id 和 title)

classArticleSerializer(serializers.ModelSerializer):classMeta:        model = Article        fields = ['id', 'title', 'content', 'created_time']def__init__(self, *args, **kwargs):        super().__init__(*args, **kwargs)# 从请求参数获取需要返回的字段        fields = self.context['request'].query_params.get('fields')if fields:            fields = fields.split(',')# 过滤掉不存在的字段            valid_fields = [f for f in fields if f in self.fields]# 保留需要的字段for field_name in list(self.fields):if field_name notin valid_fields:                    self.fields.pop(field_name)

只读与只写字段:

【read_only=True】:字段仅用于序列化(返回给前端),反序列化时忽略(如 id、created_time)

【write_only=True】:字段仅用于反序列化(接收前端输入),序列化时不返回(如 password)

classUserSerializer(serializers.ModelSerializer):    password = serializers.CharField(write_only=True) # 密码只输入,不返回classMeta:        model = User        fields = ['id', 'username', 'password']

嵌套序列化器的深度控制:

通过 depth 参数控制嵌套关联的深度(仅 ModelSerializer 支持),避免过度嵌套导致数据冗余

classArticleSerializer(serializers.ModelSerializer):classMeta:        model = Article        fields = ['id', 'title', 'author']        depth = 1# 嵌套1层:author字段显示User模型的基本字段(如id, username)

02

实际案例

  • 案例一 序列化器(APIView)

    urls.py

    from django.contrib import adminfrom django.urls import pathfrom sers import Viewsurlpatterns = [# 路由    path("sers/book/", views.BookView.as_view()), # 增加、查所有# (!无名分组)    path("sers/book/(\d+)", views.BookDetailView.as_view()) #查单一、改、删]

    models.py

    from django.db import modelsclassBook(models.Model):    title = models.CharField(max_length=32, verbose_name="数据名称")    price = models.IntegerField(verbose_name="价格")    pub_date = models.DateField(verbose_name="出版日期")

    book_serializers.py

    from rest_framework import serializersclassBookSerializers(serializers.Serializer):# 序列化 主键    id = serializers.PrimarilyKey()    title = serializers.CharField(max_length=32)    price = serializers.IntegerField()# 可以自定义 序列化器类 中的字段名,使用source参数指定原字段名    date = serializers.DateField(source="pub_date")defcreate(self, validated_data):# 如果调用 序列化器save方法,必须重写# create方法 或 update方法# 将数据插入到 数据库中return Book.objects.create(**self.validated_data)defupdate(self, instance, validated_data):        Book.objects.filter(pk=instance.id).update(**validated_data)        updated_book = Book.objects.get(pk=id)return updated_book

    views.py

    from django.shortcuts import renderfrom .models import Book # 模型类(实体类)from rest_framework.views import APIViewfrom rest_framework.response import Response # 优化Json响应体from .book_serializers.py import BookSerializersclassBookView(APIView):defget(self, request):# 获取所有书籍信息        book_list = Book.objects.all() # 返回 queryset[book1, book2, ... ]# 构建 序列化器对象:BookSerializers(instance=, data=)# instance参数: 序列化参数# data参数: 反序列化参数        serializer = BookSerializers(instance=book_list, many=True) return Response(serializer.data)defpost(self, request):        serializer = BookSerializers(data=request.data)# !注意:前端传过来的Json数据(data=request.data),必须要做校验# is_valid() 校验函数(校验序列化器中的字段)# 将校验成功的字段,放到:serializer.validated_data 中# 将校验失败的字段,放到:serializer.errors 中# 只要有一个字段校验失败,is_valid()就返回: Falseif serializer.is_valid():          serializer.save() # 序列化器save方法return Response(serializer.data) # 针对self.instance属性做序列化else:# 返回错误信息return Response(serializer.errors)classBookDetailView(APIView):defget(self, request, id):        book = Book.objects.get(pk=id)# 序列化传instance,反序列化传data        serializer = BookSerializers(instance=book, many=False)return Response(serializer.data)defput(self, request, id):# 获取提交的更新数据        update_book = Book.objects.get(pk=id)# 构建序列化器,如果是修改操作的的话,instance和data参数都需要传!        serializer = BookSerializers(instance=update_book, data=request.data)# 校验if serializer.is_valid():          serializer.save() # 序列化器save方法return Response(serializer.data)else:# 返回错误信息return Response(serializer.errors)defdelete(self, request, id):        Book.objects.get(pk=id).delete()return Response()
  • 案例二 自动创建序列化器类

    【ModelSerializer类】支持:一对一、一对多、多对多

    urls.py

    from django.contrib import adminfrom django.urls import pathfrom sers import Viewsurlpatterns = [# 路由    path("sers/book/", views.BookView.as_view()), # 增加、查所有# (!无名分组)    path("sers/book/(\d+)", views.BookDetailView.as_view()) #查单一、改、删]

    models.py

    from django.db import modelsclassBook(models.Model):    title = models.CharField(max_length=32, verbose_name="数据名称")    price = models.IntegerField(verbose_name="价格")    pub_date = models.DateField(verbose_name="出版日期")

    book_serializers.py

    from rest_framework import serializersclassBookSerializers(serializers.ModelSerializer):    data = serializer.DateField(source="pub_date") # 自定义序列化字段名classMeta:        model = Book# fields = "__all__" 表示:对所有字段做序列化# fields = ["title", "price"] 表示:只对这两个字段做序列化# exclude = ["pub_date"] 表示:排除指定字段

    views.py

    from django.shortcuts import renderfrom .models import Book # 模型类(实体类)from rest_framework.views import APIViewfrom rest_framework.response import Response # 优化Json响应体from .book_serializers.py import BookSerializersclassBookView(APIView):defget(self, request):# 获取所有书籍信息        book_list = Book.objects.all() # 返回 queryset[book1, book2, ... ]# 构建 序列化器对象:BookSerializers(instance=, data=)# instance参数: 序列化参数# data参数: 反序列化参数        serializer = BookSerializers(instance=book_list, many=True) return Response(serializer.data)defpost(self, request):        serializer = BookSerializers(data=request.data)# !注意:前端传过来的Json数据(data=request.data),必须要做校验# is_valid() 校验函数(校验序列化器中的字段)# 将校验成功的字段,放到:serializer.validated_data 中# 将校验失败的字段,放到:serializer.errors 中# 只要有一个字段校验失败,is_valid()就返回: Falseif serializer.is_valid():          serializer.save() # 序列化器save方法return Response(serializer.data) # 针对self.instance属性做序列化else:# 返回错误信息return Response(serializer.errors)classBookDetailView(APIView):defget(self, request, id):        book = Book.objects.get(pk=id)# 序列化传instance,反序列化传data        serializer = BookSerializers(instance=book, many=False)return Response(serializer.data)defput(self, request, id):# 获取提交的更新数据        update_book = Book.objects.get(pk=id)# 构建序列化器,如果是修改操作的的话,instance和data参数都需要传!        serializer = BookSerializers(instance=update_book, data=request.data)# 校验if serializer.is_valid():          serializer.save() # 序列化器save方法return Response(serializer.data)else:# 返回错误信息return Response(serializer.errors)defdelete(self, request, id):        Book.objects.get(pk=id).delete()return Response()

03

SerializerMethodField 详解

  • 基本概念

    SerializerMethodField 是一个只读字段,它的值是通过调用序列化器类中的一个方法来获取

  • 基本语法

    field_name = serializers.SerializerMethodField(method_name=None)

    如果不指定 method_name,默认调用 get_<field_name> 方法

   如果指定 method_name,则调用指定的方法

  • 基本用法

    classUserSerializer(serializers.ModelSerializer):# 1. 不指定method_name,默认调用 get_full_name    full_name = serializers.SerializerMethodField()# 2. 指定method_name,调用 get_display_status    status_display = serializers.SerializerMethodField(method_name='get_display_status')classMeta:        model = User        fields = ['id', 'username', 'full_name', 'status_display']defget_full_name(self, obj):"""计算全名"""returnf"{obj.first_name}{obj.last_name}"defget_display_status(self, obj):"""返回状态显示文本"""        status_map = {'A': '活跃','I': '非活跃', 'S': '暂停'        }return status_map.get(obj.status, '未知')

    基于请求上下文的计算:

    classProductSerializer(serializers.ModelSerializer):    is_favorited = serializers.SerializerMethodField()    current_price = serializers.SerializerMethodField()classMeta:        model = Product        fields = ['id', 'name', 'price', 'is_favorited', 'current_price']defget_is_favorited(self, obj):"""检查当前用户是否收藏了该商品"""        request = self.context.get('request')if request and request.user.is_authenticated:return obj.favorites.filter(user=request.user).exists()returnFalsedefget_current_price(self, obj):"""计算当前价格(考虑折扣、用户等级等)"""        request = self.context.get('request')        user = request.user if request elseNone        base_price = obj.price# 用户等级折扣if user and user.membership_level == 'VIP':            base_price *= 0.9# 季节性折扣if self.is_seasonal_sale():            base_price *= 0.8return round(base_price, 2)

    基于关联数据计算:

    classOrderSerializer(serializers.ModelSerializer):    item_count = serializers.SerializerMethodField()    can_cancel = serializers.SerializerMethodField()classMeta:        model = Order        fields = ['id', 'status', 'created_at', 'item_count', 'can_cancel']defget_item_count(self, obj):"""计算订单中的商品种类数量"""return obj.order_items.count()defget_can_cancel(self, obj):"""判断订单是否可以取消"""        cancellable_statuses = ['pending', 'confirmed']return obj.status in cancellable_statuses
  • 高级语法

    带参数的方法字段:

    classProductSerializer(serializers.ModelSerializer):    price_with_currency = serializers.SerializerMethodField()classMeta:        model = Product        fields = ['id', 'name', 'price', 'price_with_currency']defget_price_with_currency(self, obj):"""根据请求返回带货币符号的价格"""        request = self.context.get('request')        currency = request.GET.get('currency', 'USD') if request else'USD'        currency_symbols = {'USD': '$','EUR': '€','CNY': '¥'        }        symbol = currency_symbols.get(currency, '$')returnf"{symbol}{obj.price}"

    性能优化考虑:

    ❌ 可能引起 N+1查询

    classOrderListSerializer(serializers.ModelSerializer):    customer_name = serializers.SerializerMethodField()classMeta:        model = Order        fields = ['id', 'total', 'customer_name']defget_customer_name(self, obj):# 每次都会查询数据库!return obj.customer.name

    ✅ 优化版本

    classOptimizedOrderListSerializer(serializers.ModelSerializer):    customer_name = serializers.SerializerMethodField()classMeta:        model = Order        fields = ['id', 'total', 'customer_name']defget_customer_name(self, obj):# 使用预加载的数据return obj.customer.name

    在视图中预加载数据

    classOrderListView(generics.ListAPIView):    serializer_class = OptimizedOrderListSerializerdefget_queryset(self):return Order.objects.select_related('customer')
  • SerializerMethodField的优缺点

    优点:

    灵活性:可以计算任何复杂的字段值

    只读性:自动成为只读字段,无需额外配置

    上下文感知:可以访问请求上下文

    业务逻辑隔离:将计算逻辑封装在序列化器中

    缺点:

    性能风险:可能引起N+1查询问题

    测试复杂性:需要为每个方法字段编写测试

    不可用于写入:不能用于反序列化

                    篇幅太长,下期再见!

最新文章

随机文章