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]]这类嵌套数组结构;且两个字段分开查询无法自动关联为一一对应的站点对象,不符合预期结构。
修复方案
推荐直接使用嵌套序列化器实现,不需要单独维护两个查询方法,代码可维护性更高:
- 首先调整
ProjectSiteSerializer,映射字段名匹配需求要求的键名 - 在项目序列化器中直接嵌套引用站点序列化器,关联对应反向查询集即可
修改后代码:
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
相关产品推荐
相关产品推荐

