You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DRF中如何通过SerializerMethodField返回指定结构的字段

问题说明

当前代码运行后返回的嵌套数组结构为:
{ project_name:test, site_id:[[1],[2]],site_name:[[test1],[test2]] }
期望返回的站点结构为对象数组,每个元素包含站点名和站点ID,格式如下:
[{sitename:test1, siteid:1}, {sitename:test2, siteid:2}]

涉及的原有代码如下:

class ProjectSite(models.Model):
    site_id = models.AutoField(primary_key=True, unique=True)
    site_name = models.CharField(max_length=200,name='project_site_name')
    project_id = models.ForeignKey(Project, on_delete=models.CASCADE, blank=True, null=True, 
    related_name="projectid")

class ProjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = Project
        fields = '__all__'

class ProjectSiteSerializer(serializers.ModelSerializer):
    class Meta:
        model = ProjectSite
        fields = '__all__'

class Serializer(serializers.ModelSerializer):
    ...
    site_ids = serializers.SerializerMethodField(read_only = True)
    site_name = serializers.SerializerMethodField(read_only = True)
    
    ...

    def get_site_ids(self, obj):
        return list(obj.projectid.values_list('id').distinct())
    def get_site_name(self, obj):
        return list(obj.projectid.values_list('site_name').distinct())
问题原因

原有实现分开查询站点ID和站点名称两个独立列表,values_list单字段查询返回的是元组形式的结果集,因此会出现[[1],[2]]这类嵌套数组结构;且两个字段分开查询无法自动关联为一一对应的站点对象,不符合预期结构。

修复方案

推荐直接使用嵌套序列化器实现,不需要单独维护两个查询方法,代码可维护性更高:

  1. 首先调整ProjectSiteSerializer,映射字段名匹配需求要求的键名
  2. 在项目序列化器中直接嵌套引用站点序列化器,关联对应反向查询集即可

修改后代码:

from rest_framework import serializers
from .models import Project, ProjectSite

class ProjectSiteSerializer(serializers.ModelSerializer):
    # 映射模型字段到接口需要返回的键名
    sitename = serializers.CharField(source='project_site_name')
    siteid = serializers.IntegerField(source='site_id')
    class Meta:
        model = ProjectSite
        # 仅返回需要的两个字段
        fields = ('sitename', 'siteid')

class ProjectDetailSerializer(serializers.ModelSerializer):
    # 关联反向查询的站点集合,many=True表示返回数组
    site_list = ProjectSiteSerializer(source='projectid', many=True, read_only=True)
    
    class Meta:
        model = Project
        # 按需填写需要返回的项目字段,示例保留project_name
        fields = ('project_name', 'site_list')

如果不想调整原有序列化器结构,也可以合并两个序列化方法字段为单个,直接在查询时组装目标结构:

from django.db.models import F

class Serializer(serializers.ModelSerializer):
    site_list = serializers.SerializerMethodField(read_only=True)
    
    def get_site_list(self, obj):
        # 一次性查询站点id和名称,直接组装为目标键值对结构
        return list(
            obj.projectid
            .values('site_id', 'project_site_name')
            .distinct()
            .annotate(
                sitename=F('project_site_name'),
                siteid=F('site_id')
            )
            .values('sitename', 'siteid')
        )
    
    class Meta:
        model = Project
        fields = ('project_name', 'site_list')

注意:模型中site_name字段自定义了数据库字段名为project_site_name,查询和字段映射时需要使用该名称,避免字段不存在报错。

内容的提问来源于stack exchange,提问作者Vas

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.02 20:48:27