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

如何修复Serializer无法移除不符合completed==total_videos条件实例的问题?

问题修复:过滤满足completed == total_videos的序列化实例

问题原因分析

你当前的实现存在两个核心问题:

  1. validate方法误用:该方法仅用于验证输入数据(比如创建/更新资源时的请求参数),对序列化输出的实例列表完全没有过滤作用。
  2. to_representation返回None的局限性:序列化列表时,DRF会将返回的None序列化为null值保留在结果中,而非直接移除该条目,所以你会看到包含null的完整列表,而非过滤后的有效数据。

解决方案

方案一:视图层查询集过滤(推荐,性能最优)

直接在数据库层面过滤出满足条件的实例,避免内存中处理大量无效数据:

from django.db.models import Count, Q, F
from rest_framework import generics

class YourCourseCompletionView(generics.ListAPIView):
    serializer_class = LearnerVideoMarkUsCompleteMyCourseSerializer

    def get_queryset(self):
        # 获取当前请求关联的学习者(需根据实际用户关联逻辑调整)
        learner = self.request.user.learner

        # 通过annotate聚合计算每个课程的总视频数和已完成数,再过滤匹配的实例
        return LearnerVideoMarkUsComplete.objects.filter(learner=learner).annotate(
            # 计算课程总视频数(关联路径与get_total_videos保持一致)
            total_videos=Count(
                'course__coursesectiondetails__section_id__coursemodulevideos__video__videodetails',
                distinct=True
            ),
            # 计算当前学习者该课程的已完成视频数
            completed=Count(
                'learner__learner_video_markascomplete',
                filter=Q(
                    learner__learner_video_markascomplete__course=F('course'),
                    learner__learner_video_markascomplete__mark_us_complete=True
                ),
                distinct=True
            )
        ).filter(total_videos=F('completed'))

方案二:自定义ListSerializer过滤(适合无法修改视图的场景)

通过重写ListSerializer的to_representation方法,在序列化前过滤掉不满足条件的实例:

from rest_framework import serializers

# 自定义列表序列化器
class FilteredCourseCompletionListSerializer(serializers.ListSerializer):
    def to_representation(self, data):
        # 先过滤出满足条件的实例
        filtered_instances = []
        for instance in data:
            total = self.child.get_total_videos(instance)
            done = self.child.get_completed(instance)
            if done == total:
                filtered_instances.append(instance)
        # 对过滤后的实例执行序列化
        return super().to_representation(filtered_instances)

# 修改原序列化器,指定列表序列化器
class LearnerVideoMarkUsCompleteMyCourseSerializer(serializers.ModelSerializer):
    course = CourseSerializerForEnrolledCourseService()
    total_videos = serializers.SerializerMethodField()
    completed = serializers.SerializerMethodField()
    id = serializers.UUIDField(source="course.id")

    def get_total_videos(self, data):
        total_video_count = VideoDetails.objects.filter(
            video__coursemodulevideos__module_id__coursesectionmodule__section_id__coursesectiondetails__course=data.course
        ).count()
        return total_video_count

    def get_completed(self, data):
        progress = data.learner.learner_video_markascomplete.filter(
            course=data.course, mark_us_complete=True
        ).count()
        return progress

    class Meta:
        model = LearnerVideoMarkUsComplete
        fields = ("course", "id", "completed", "total_videos")
        # 指定自定义的列表序列化器
        list_serializer_class = FilteredCourseCompletionListSerializer

额外优化建议

  • 可以将get_total_videos和get_completed中的查询逻辑优化为缓存或annotate预计算,避免每个实例重复执行查询,提升序列化性能。
  • 关联查询路径较长(如video__coursemodulevideos__module_id__...),可给相关外键字段添加索引,优化数据库查询速度。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 09:13:15