如何用DRF Serializer序列化数据并添加学员参与统计字段
解决Django REST Framework序列化中添加参与者数量字段的问题
Hey there! 我来帮你搞定这个需求,要在课程的序列化结果里加入participant_students_count字段,其实有两种常用的方法,还能兼顾性能,一起来看看:
方法一:使用SerializerMethodField(快速实现)
这种方法适合快速验证需求,直接在序列化器里定义一个自定义字段,然后编写方法来统计数量:
from rest_framework import serializers from .models import Course class CourseSerializer(serializers.ModelSerializer): # 定义自定义字段 participant_students_count = serializers.SerializerMethodField() class Meta: model = Course # 把自定义字段加入返回字段列表 fields = ('name', 'end_date', 'start_date', 'participant_students_count') # 编写字段对应的获取方法,obj就是当前的Course实例 def get_participant_students_count(self, obj): # 注意这里用的是你模型里设置的related_name='courses' return obj.courses.count()
不过要注意,这种方法如果序列化多个课程,会触发N+1查询问题(每个课程都单独查一次参与者数量),数据量大的时候性能会受影响。
方法二:用annotate提前聚合(高性能推荐)
为了避免N+1查询,我们可以在查询课程的时候就用Django的annotate提前计算好每个课程的参与者数量,然后直接在序列化器里使用这个字段:
第一步:在视图中修改查询集
from django.db.models import Count from rest_framework.response import Response from rest_framework.views import APIView from .models import Course from .serializers import CourseSerializer class CourseListView(APIView): def get(self, request): # 用annotate给每个Course对象添加participant_students_count字段 courses = Course.objects.annotate( participant_students_count=Count('courses') # 同样对应你的related_name='courses' ) serializer = CourseSerializer(courses, many=True) # 按照你需要的格式返回数据 return Response({"courses": serializer.data})
第二步:修改序列化器
这时候序列化器不需要额外定义方法,只需要把聚合后的字段加入fields即可:
class CourseSerializer(serializers.ModelSerializer): class Meta: model = Course fields = ('name', 'end_date', 'start_date', 'participant_students_count')
额外建议:优化related_name语义
你当前的CourseParticipant模型中,course字段的related_name='courses'语义有点混淆(从Course反向关联的应该是参与者,不是课程),建议修改成更清晰的名称,比如participants:
class CourseParticipant(models.Model): # 修改related_name为participants,同时on_delete不要用None,推荐用CASCADE course = models.ForeignKey(Course, related_name='participants', on_delete=models.CASCADE) student = models.ForeignKey(Student, related_name='enrolled_courses', on_delete=models.CASCADE) completed = models.BooleanField(null=True, default=False) def __str__(self): return f"{self.student} - {self.course}"
修改后,上面的代码里的Count('courses')就要改成Count('participants'),obj.courses.count()改成obj.participants.count(),代码可读性会好很多~
内容的提问来源于stack exchange,提问作者MrOldSir
相关产品推荐
相关产品推荐

