如何修改Django序列化器输出,按项目分组合同数据?
Django序列化器按项目分组输出的实现方案
问题描述
我想要修改Django序列化器的输出格式。基于类的视图中有get_projects方法,它返回Contract实例的queryset:
projects = Project.objects.all() contracts = Contract.objects.filter(project__in=projects)
Contract与Project是一对多关系:
class Contract(models.Model): project = models.ForeignKey(Project)
当前使用的MySerializer如下:
class MySerializer(serializers.Serializer): project_guid = serializers.UUIDField(source='guid') project_name = serializers.CharField(source='name') contract_guid = serializers.UUIDField(source='guid') contract_number = serializers.CharField(source='number')
该序列化器的输出为:
[ { "projectGuid": "project_guid_1", "projectName": "project_name_1", "contractGuid": "contract_guid_1", "contractNumber": "contract_number_1" }, { "projectGuid": "project_guid_1", "projectName": "project_name_1", "contractGuid": "contract_guid_2", "contractNumber": "contract_number_2" }, { "projectGuid": "project_guid_2", "projectName": "project_name_2", "contractGuid": "contract_guid_4", "contractNumber": "contract_number_4" }, { "projectGuid": "project_guid_2", "projectName": "project_name_2", "contractGuid": "contract_guid_5", "contractNumber": "contract_number_5" } ]
希望将输出修改为按项目分组合同的格式,示例如下:
[ { "project_guid": "project_guid_1", "project_name": "project_name_1", "contracts": [ { "contract_guid": "contract_guid_1", "contract_number": "contract_number_1" }, { "contract_guid": "contract_guid_2", "contract_number": "contract_number_2" } ] }, { "project_guid": "project_guid_2", "project_name": "project_name_2", "contracts": [ { "contract_guid": "contract_guid_4", "contract_number": "contract_number_4" }, { "contract_guid": "contract_guid_5", "contract_number": "contract_number_5" } ] } ]
请问如何实现这一需求?
解决方案
可以通过嵌套序列化器+优化查询逻辑的方式实现,步骤如下:
1. 定义合同子序列化器
先创建仅处理合同字段的序列化器,用于嵌套展示:
class ContractSerializer(serializers.Serializer): contract_guid = serializers.UUIDField(source='guid') contract_number = serializers.CharField(source='number')
2. 定义项目主序列化器
创建处理项目信息的序列化器,通过SerializerMethodField获取并序列化当前项目的所有合同:
class ProjectSerializer(serializers.Serializer): project_guid = serializers.UUIDField(source='guid') project_name = serializers.CharField(source='name') contracts = serializers.SerializerMethodField() def get_contracts(self, obj): # 从项目对象反向关联获取所有合同 contracts = obj.contract_set.all() return ContractSerializer(contracts, many=True).data
3. 调整视图查询逻辑
将查询起点从合同改为项目,同时用prefetch_related预加载关联合同,避免N+1查询问题:
# 替换原有的contracts查询,改用预加载合同的项目queryset projects = Project.objects.all().prefetch_related('contract_set')
4. 使用项目序列化器输出
在视图中用ProjectSerializer处理项目queryset,返回序列化后的数据:
serializer = ProjectSerializer(projects, many=True) return Response(serializer.data)
可选:用ModelSerializer简化代码
如果模型字段与输出字段匹配度高,可使用ModelSerializer减少重复代码:
class ContractSerializer(serializers.ModelSerializer): class Meta: model = Contract fields = ['guid', 'number'] extra_kwargs = { 'guid': {'label': 'contract_guid'}, 'number': {'label': 'contract_number'} } class ProjectSerializer(serializers.ModelSerializer): contracts = ContractSerializer(many=True, source='contract_set') class Meta: model = Project fields = ['guid', 'name', 'contracts'] extra_kwargs = { 'guid': {'label': 'project_guid'}, 'name': {'label': 'project_name'} }
这样就能得到按项目分组的目标输出格式。
内容的提问来源于stack exchange,提问作者serg
相关产品推荐
相关产品推荐

