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

如何用Django Rest Framework序列化器构造目标JSON响应?

解决方案:通过DRF序列化器实现分组及ChartJS适配格式

当然可以用Django Rest Framework(DRF)的序列化器实现你需要的两种数据格式!我会分两步帮你搞定:先实现按meternumber拆分的分组响应,再直接生成Angular ChartJS所需的格式。

一、实现按meternumber分组的响应格式

要得到包含servicenumber、meternumber和嵌套usagedata的结构,我们需要用到嵌套序列化器,同时在视图层先对数据进行分组处理。

1. 定义嵌套序列化器

首先,保留你已有的MeterUsageSerializer,再新增一个用于分组的序列化器:

from rest_framework import serializers
from .models import MeterUsage

# 你原有的基础序列化器
class MeterUsageSerializer(serializers.ModelSerializer):
    class Meta:
        model = MeterUsage
        fields = ('usagedatetime', 'usage')

# 新增分组序列化器
class MeterGroupSerializer(serializers.Serializer):
    servicenumber = serializers.IntegerField()
    meternumber = serializers.IntegerField()
    usagedata = MeterUsageSerializer(many=True)  # 嵌套使用基础序列化器

2. 在视图中分组并序列化数据

为了减少数据库查询次数,我们可以一次性获取所有数据,再通过itertools.groupby在内存中分组(比多次查询更高效):

from rest_framework.views import APIView
from rest_framework.response import Response
from itertools import groupby
from operator import attrgetter
from .models import MeterUsage
from .serializers import MeterGroupSerializer

class GroupedMeterDataView(APIView):
    def get(self, request):
        # 获取所有记录,并按servicenumber、meternumber、usagedatetime排序(groupby依赖有序数据)
        all_records = MeterUsage.objects.all().order_by(
            'servicenumber', 'meternumber', 'usagedatetime'
        )
        
        grouped_data = []
        # 按(servicenumber, meternumber)组合分组
        grouping_key = attrgetter('servicenumber', 'meternumber')
        for (serv_num, meter_num), records_group in groupby(all_records, key=grouping_key):
            # 将分组后的记录序列化为usagedata
            usage_data = MeterUsageSerializer(list(records_group), many=True).data
            grouped_data.append({
                'servicenumber': serv_num,
                'meternumber': meter_num,
                'usagedata': usage_data
            })
        
        # 用分组序列化器处理最终数据
        serializer = MeterGroupSerializer(grouped_data, many=True)
        return Response(serializer.data)

这样返回的响应就完全符合你需要的分组格式了。

二、直接生成ChartJS所需的格式

如果你的前端只需要ChartJS的label-data结构,可以直接在后端构造对应格式,无需先返回分组数据再前端处理。

1. 定义ChartJS专用序列化器

class MeterChartDataSerializer(serializers.Serializer):
    label = serializers.CharField()  # 将meternumber转为字符串作为标签
    data = serializers.ListField(child=serializers.DecimalField(max_digits=38, decimal_places=8))

2. 在视图中构造ChartJS数据

class MeterChartDataView(APIView):
    def get(self, request):
        all_records = MeterUsage.objects.all().order_by(
            'servicenumber', 'meternumber', 'usagedatetime'
        )
        
        chart_data = []
        grouping_key = attrgetter('servicenumber', 'meternumber')
        for (serv_num, meter_num), records_group in groupby(all_records, key=grouping_key):
            # 提取按时间排序的usage值列表
            usage_values = [record.usage for record in records_group]
            chart_data.append({
                'label': str(meter_num),
                'data': usage_values
            })
        
        serializer = MeterChartDataSerializer(chart_data, many=True)
        return Response(serializer.data)

这个接口返回的格式和你需要的chartData完全一致,前端可以直接拿来给ChartJS用。

为什么之前的尝试可能无效?

你之前的问题大概率是没有在视图层先对数据进行分组,而是直接序列化了所有MeterUsage对象,导致返回的是扁平的列表。DRF的序列化器本身不会自动帮你分组,需要你先构造好分组后的数据集,再用序列化器处理结构。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:56:16