Django REST Framework Serializer 进阶教程

1. 序列化器概述

在 Django REST Framework(DRF)中,序列化器(Serializer)用于将复杂的数据类型(如模型实例)转换为 JSON 格式,以便于 API 返回给客户端。此外,序列化器还可以用于验证请求数据。

2. 基本的 ModelSerializer 使用

  1. models.py 中定义了一个 Department 模型,用来表示部门的信息。在这个模型中,我们包括了 full_name 字段(存储部门的完整名称),以及 level 字段(表示部门的层级)。
python 复制代码
# models.py
from django.db import models

class Department(models.Model):
    # 部门的全名,使用 `CharField` 存储
    full_name = models.CharField(max_length=255)
    # 部门的层级,默认为 0
    level = models.IntegerField(default=0)
    
    def __str__(self):
        return self.full_name
  1. ModelSerializerSerializer 的一个子类,专门用于将 Django 模型实例转化为 JSON 格式,且支持自动化字段映射。
例子:
python 复制代码
from rest_framework import serializers
from .models import Department

class DepartmentSerializer(serializers.ModelSerializer):
    class Meta:
        model = Department
        fields = '__all__'

这里,DepartmentSerializer 会自动根据 Department 模型的字段生成相应的序列化字段。

3. 使用 SerializerMethodField 动态计算字段

SerializerMethodField 是 DRF 提供的一种特殊字段类型,用于动态计算某个字段的值。你可以定义一个方法,方法名称是 get_<field_name>,该方法会自动被调用来计算字段的值。

示例:

假设 Department 模型有一个字段 full_name,表示部门的全名(例如:"公司-技术-后端"),我们希望计算一个 level 字段,表示该部门的层级。

python 复制代码
from rest_framework import serializers

class DepartmentSerializer(serializers.ModelSerializer):
    # level 字段会动态计算
    level = serializers.SerializerMethodField(read_only=True)

    class Meta:
        model = Department
        fields = '__all__'

    # 动态计算 level 字段
    def get_level(self, obj):
        full_name = obj.full_name
        # 按照 '-' 分割 full_name
        full_name_split = full_name.split('-')
        
        # 计算层级:忽略第一个部分(例如"公司")
        return len(full_name_split) - 1

在这个示例中:

  • level 字段是一个 SerializerMethodField,它不会直接从模型中读取,而是通过 get_level 方法动态计算。
  • get_level 方法接收 obj(即当前的 Department 实例),根据 full_name 字段来计算层级。
  • full_name.split('-') 会将部门的全名拆分为多个部分,然后计算层级。

4. 使用自定义 __init__ 方法动态控制字段

有时,我们可能需要根据外部条件来动态修改序列化器中的字段。例如,某些字段可能在某些情况下不需要序列化。我们可以通过重写 __init__ 方法来实现这一点。

示例:
python 复制代码
class DepartmentSerializer(serializers.ModelSerializer):
    level = serializers.SerializerMethodField(read_only=True)

    def __init__(self, *args, **kwargs):
        fields = kwargs.pop('fields', [])
        super().__init__(*args, **kwargs)
        if fields:
            # 删除未指定的字段
            allowed = set(fields)
            existing = set(self.fields)
            for field_name in existing - allowed:
                self.fields.pop(field_name)

    def get_level(self, obj):
        full_name = obj.full_name
        full_name_split = full_name.split('-')
        return len(full_name_split) - 1

    class Meta:
        model = Department
        fields = '__all__'
        depth = 1
        read_only_fields = ('id',)

在上面的代码中:

  • __init__ 方法接受一个 fields 参数,该参数是一个字段列表,表示要包含的字段。
  • 如果 fields 参数不为空,它会从已有的字段中删除未指定的字段,从而控制序列化器返回的字段。

5. 实战案例:使用 SerializerMethodField 和自定义字段控制

背景:

假设我们要设计一个 API,返回部门的层级结构,并且根据不同的条件返回不同的字段。我们需要动态计算部门的层级,并且能够根据请求控制返回哪些字段。

模型:
python 复制代码
# models.py
from django.db import models

class Department(models.Model):
    full_name = models.CharField(max_length=255)
    level = models.IntegerField(default=0)
    
    def __str__(self):
        return self.full_name
视图:
python 复制代码
# views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .models import Department
from .serializers import DepartmentSerializer

class DepartmentView(APIView):
    def get(self, request):
        # 假设我们根据参数决定返回哪些字段
        fields = request.query_params.getlist('fields', [])
        
        # 获取所有部门数据
        departments = Department.objects.all()
        
        # 使用序列化器返回数据
        serializer = DepartmentSerializer(departments, many=True, fields=fields)
        return Response(serializer.data, status=status.HTTP_200_OK)
URL 配置:
python 复制代码
# urls.py
from django.urls import path
from .views import DepartmentView

urlpatterns = [
    path('departments/', DepartmentView.as_view(), name='departments-list'),
]

6. 测试:

假设我们有以下部门数据:

full_name level
公司-技术-后端 2
公司-财务 1
公司-人力资源 1
请求 1:返回所有字段
复制代码
GET /departments/

返回:

json 复制代码
[
  {
    "full_name": "公司-技术-后端",
    "level": 2
  },
  {
    "full_name": "公司-财务",
    "level": 1
  },
  {
    "full_name": "公司-人力资源",
    "level": 1
  }
]
请求 2:仅返回 full_namelevel 字段
复制代码
GET /departments/?fields=full_name&fields=level

返回:

json 复制代码
[
  {
    "full_name": "公司-技术-后端",
    "level": 2
  },
  {
    "full_name": "公司-财务",
    "level": 1
  },
  {
    "full_name": "公司-人力资源",
    "level": 1
  }
]

7. 总结

  • SerializerMethodField:用来动态计算字段的值,可以根据模型实例的内容或请求参数来决定字段的值。
  • get_<field_name> 方法:定义字段的计算逻辑。
  • 动态字段控制 :通过在序列化器的 __init__ 方法中动态修改字段来控制序列化器返回哪些字段。
  • 实战案例 :通过 fields 参数控制返回字段,并动态计算层级。

这个案例展示了如何在 Django REST Framework 中灵活地使用序列化器来动态计算和控制返回字段,适用于多种 API 场景。

相关推荐
小陈工1 小时前
Python Web开发入门(十七):Vue.js与Python后端集成——让前后端真正“握手言和“
开发语言·前端·javascript·数据库·vue.js·人工智能·python
A__tao6 小时前
Elasticsearch Mapping 一键生成 Java 实体类(支持嵌套 + 自动过滤注释)
java·python·elasticsearch
研究点啥好呢6 小时前
Github热门项目推荐 | 创建你的像素风格!
c++·python·node.js·github·开源软件
迷藏4946 小时前
**发散创新:基于Rust实现的开源合规权限管理框架设计与实践**在现代软件架构中,**权限控制(RBAC)** 已成为保障
java·开发语言·python·rust·开源
明日清晨7 小时前
python扫码登录dy
开发语言·python
bazhange7 小时前
python如何像matlab一样使用向量化替代for循环
开发语言·python·matlab
人工干智能7 小时前
科普:python中你写的模块找不到了——`ModuleNotFoundError`
服务器·python
unicrom_深圳市由你创科技7 小时前
做虚拟示波器这种实时波形显示的上位机,用什么语言?
c++·python·c#
小敬爱吃饭7 小时前
Ragflow Docker部署及问题解决方案(界面为Welcome to nginx,ragflow上传文件失败,Docker中的ragflow-cpu-1一直重启)
人工智能·python·nginx·docker·语言模型·容器·数据挖掘
宸津-代码粉碎机7 小时前
Spring Boot 4.0虚拟线程实战调优技巧,最大化发挥并发优势
java·人工智能·spring boot·后端·python