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

Django DRF如何按外键字段分组聚合并正确序列化数据?

按话题聚合用户点赞数并正确序列化的解决方案

问题背景

现有Django模型支持用户为话题点赞,需通过DRF API返回话题ID、话题名称、总点赞数的聚合数据,但按用户/团队过滤后分组聚合时,多次尝试出现字段缺失或映射错误:

  • 按topic分组后,topic_id不显示,topic_name输出ID字符串
  • 按topic__id、topic__name分组后,序列化器无法识别字段,仅返回upvotes
  • 调整values后,topic_name重复且仍显示ID,未调用__str__方法

现有模型与序列化器

模型代码(修正Django语法错误)

# models.py
from django.db import models
from django.contrib.auth.models import User

class Topic(models.Model):
    name = models.CharField(max_length=255)  # Django无StringField,应为CharField并指定长度

    def __str__(self):
        return str(self.name)

class UserUpvotes(models.Model):
    """记录用户对话题的点赞数"""
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
    upvotes = models.PositiveIntegerField(default=0)

原序列化器代码

# serializers.py
from rest_framework import serializers
from .models import UserUpvotes

class TopicUpvotesSerializer(serializers.ModelSerializer):
    topic_name = serializers.StringRelatedField(source="topic")

    class Meta:
        model = UserUpvotes
        fields = ["topic_id", "topic_name", "upvotes"]

解决方案

1. 调整数据库聚合查询逻辑

聚合查询需先指定分组字段(topic_id和topic__name),再通过Sum聚合点赞数,同时保留过滤逻辑:

# views.py
from django.db.models import Sum
from rest_framework.response import Response
from rest_framework.views import APIView
from .models import UserUpvotes
from .serializers import AggregatedTopicUpvotesSerializer

class AggregatedTopicUpvotesView(APIView):
    def get(self, request):
        # 示例:过滤当前用户的点赞记录,可替换为团队过滤逻辑
        filtered_upvotes = UserUpvotes.objects.filter(user=request.user)
        
        # 按话题ID和名称分组,计算每个话题的总点赞数
        aggregated_data = filtered_upvotes.values(
            "topic_id",
            "topic__name"
        ).annotate(
            upvotes=Sum("upvotes")
        ).order_by("-upvotes")  # 可选:按点赞数降序排列
        
        serializer = AggregatedTopicUpvotesSerializer(aggregated_data, many=True)
        return Response(serializer.data)

2. 改用普通Serializer适配聚合结果

由于聚合返回的是字典(而非UserUpvotes模型实例),不能使用ModelSerializer,需自定义Serializer映射字段:

# serializers.py
from rest_framework import serializers

class AggregatedTopicUpvotesSerializer(serializers.Serializer):
    topic_id = serializers.IntegerField()
    topic_name = serializers.CharField(source="topic__name")
    upvotes = serializers.IntegerField()

关键说明

  • 原ModelSerializer依赖模型字段,而聚合结果中的topic__name并非UserUpvotes的直接字段,因此无法识别
  • 分组时必须显式指定topic_id和topic__name,确保聚合后能获取到所需的话题信息
  • 使用Sum聚合函数计算总点赞数,替代原模型中单个用户的点赞数记录

最终输出示例

[
    {
        "topic_id": 3,
        "topic_name": "Korean Studies",
        "upvotes": 14
    },
    {
        "topic_id": 12,
        "topic_name": "Inflation",
        "upvotes": 3
    }
]

内容的提问来源于stack exchange,提问作者Original BBQ Sauce

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 15:39:14