如何在Django REST Framework中正确序列化数据,实现按Ticker聚合返回
问题描述
我是Django REST Framework的新手,当前接口返回的数据格式不符合需求,需要将返回数据按Ticker分组,把对应daily_pe聚合为数组。
现有代码
models.py
class Price(models.Model): ticker = models.CharField(max_length=15, null=False, blank=False) price_date=models.DateField(null=False, blank=False) daily_pe = models.FloatField(blank=True, null=True) class Meta: db_table= 'price' unique_together = ('ticker', 'price_date') def __str__(self): return "%s %s %s " % (self.ticker, self.price_date, self.daily_pe)
serializers.py
class ChartSerilizer(serializers.ModelSerializer): class Meta: model = Price fields = ['ticker', 'daily_pe']
views.py
class peChartViewSet(APIView): def get(self, request): queryset = Price.objects.all() serializer = ChartSerilizer(queryset, many=True) return JsonResponse(serializer.data, safe=False)
当前返回格式
[ { "ticker": "AAPL", "daily_pe": 25.144920802584345 }, { "ticker": "AAPL", "daily_pe": 25.043920960028977 }, { "ticker": "WMT", "daily_pe": 24.930801136366966 }, { "ticker": "WMT", "daily_pe": 25.504480242081453 }, ]
期望返回格式
[ { "Ticker": "AAPL", "daily_pe": [25.144920802584345, 25.043920960028977] }, { "Ticker": "WMT", "daily_pe": [24.930801136366966,25.504480242081453] } ]
解决方案
方法一:数据库层面聚合(推荐)
利用Django的聚合函数直接在查询阶段完成分组和聚合,效率更高。如果使用PostgreSQL,可直接用ArrayAgg:
修改views.py
from django.contrib.postgres.aggregates import ArrayAgg from rest_framework.response import Response class peChartViewSet(APIView): def get(self, request): # 按ticker分组,聚合daily_pe为数组 queryset = Price.objects.values('ticker').annotate( daily_pe=ArrayAgg('daily_pe') ).order_by('ticker') # 转换键名并整理格式 result = [{"Ticker": item['ticker'], "daily_pe": item['daily_pe']} for item in queryset] return Response(result)
注:若使用MySQL,可替换为
GroupConcat,之后再将字符串分割为数组,示例:from django.db.models import Func, Value class GroupConcat(Func): function = 'GROUP_CONCAT' template = "%(function)s(%(expressions)s SEPARATOR ',')" # 查询时 queryset = Price.objects.values('ticker').annotate( daily_pe_str=GroupConcat('daily_pe') ).order_by('ticker') result = [ {"Ticker": item['ticker'], "daily_pe": list(map(float, item['daily_pe_str'].split(',')))} for item in queryset ]
方法二:Python层面分组处理
如果不想依赖数据库特定函数,可先获取全量数据,再在代码中完成分组:
修改views.py
from collections import defaultdict from rest_framework.response import Response class peChartViewSet(APIView): def get(self, request): queryset = Price.objects.all() serializer = ChartSerilizer(queryset, many=True) # 按ticker分组聚合daily_pe grouped_data = defaultdict(list) for item in serializer.data: grouped_data[item['ticker']].append(item['daily_pe']) # 转换为期望格式 result = [{"Ticker": ticker, "daily_pe": pes} for ticker, pes in grouped_data.items()] return Response(result)
可选:自定义序列化器规范输出
若需要更严谨的字段校验和序列化流程,可新增自定义序列化器:
新增serializers.py内容
class GroupedChartSerializer(serializers.Serializer): Ticker = serializers.CharField() daily_pe = serializers.ListField(child=serializers.FloatField())
配合方法二修改views.py
class peChartViewSet(APIView): def get(self, request): queryset = Price.objects.all() serializer = ChartSerilizer(queryset, many=True) grouped_data = defaultdict(list) for item in serializer.data: grouped_data[item['ticker']].append(item['daily_pe']) result = [{"Ticker": ticker, "daily_pe": pes} for ticker, pes in grouped_data.items()] # 使用自定义序列化器处理输出 grouped_serializer = GroupedChartSerializer(result, many=True) return Response(grouped_serializer.data)
内容的提问来源于stack exchange,提问作者shyam yadav
相关产品推荐
相关产品推荐

