如何为序列化程序字段进行分页?

How to do pagination for a serializer field?

我有一项任务需要通过版主 ID 获取统计信息和反馈。 'stats' 字段是通用字段,'feedback' 字段是反馈列表。我可以为 'feedback' 字段分页吗?当然,我可以为统计数据和反馈设置不同的端点,但我不允许这样做。

// GET /api/moderators/:id/feedback
{
    "stats": [
        {
            "name": "123",
            "value": -10
        }
    ],
    "feedback": [
        {
            "id": 1,
            "createdBy": "FN LN",
            "createdAt": "DT",
            "comment": "",
            "score": 5,
            "categories": [
                {
                    "name": "123",
                    "status": "POSITIVE/NEGETIVE/UNSET"
                }
            ],
            "webinarID": 123456
        },
        {
            ...
        }
        
    ]
}

views.py

class TeacherFeedbackViewSet(ViewSet):
    permission_classes = [IsAuthenticated, TeacherFeedbackPerm]
    renderer_classes = [CamelCaseJSONRenderer]

    @base_view
    def list(self, request, pk):
        moderator = get_object_or_404(Moderator, pk=pk)
        serializer = ModeratorFeedback(moderator)
        return Response(serializer.data)

serializers.py

class TeacherFeedbackSerializerDetail(ModelSerializer):
    created_at = DateTimeField(source='datetime_filled')
    created_by = SerializerMethodField(method_name='get_created_by')
    categories = SerializerMethodField(method_name='get_categories')
    webinar_id = IntegerField(source='webinar.id')
    moderator_id = IntegerField(source='moderator.id')

    class Meta:
        model = TeacherFeedback
        fields = ['id', 'created_by', 'created_at', 'categories', 'score', 'comment', 'moderator_id', 'webinar_id']

    def get_categories(self, feedback: TeacherFeedback):
        data = []
        category_names = list(FeedbackCategory.objects.all().values_list('name', flat=True))
        for category in feedback.category.all():
            z = TeacherFeedbackCategory.objects.get(category=category, feedback=feedback)
            data.append({"name": z.category.name, "status": z.status})

        unset = list(map(lambda x: {"name": x, "status": "unset"},
                         list(set(category_names) - set([i["name"] for i in data]))))
        return sorted(data + unset, key=lambda x: x["status"])

    def get_created_by(self, feedback: TeacherFeedback):
        return str(feedback.created_by.teacher)


class ModeratorFeedback(serializers.ModelSerializer):
    stats = serializers.SerializerMethodField(method_name='get_stats_list')
    feedback = TeacherFeedbackSerializerDetail(many=True, source='actual_feedbacks')

    class Meta:
        model = Moderator
        fields = ['stats', 'feedback']

    def get_stats_list(self, moderator: Moderator):
        data = {}
        for feedback in moderator.actual_feedbacks:
            for category in feedback.category.all():
                category_detail = TeacherFeedbackCategory.objects.get(feedback=feedback, category=category)
                if category.name not in data:
                    data[category.name] = [category_detail.status]
                else:
                    data[category.name].append(category_detail.status)
        stats = []
        for k, statuses in data.items():
            weight = 100/len(statuses)
            current_value = 0
            for status in statuses:
                if status == 'positive':
                    current_value += weight
                else:
                    current_value -= weight
            stats.append({"name": k, "value": float("{0:.2f}".format(current_value))})
        return stats

这里为了实现分页,需要分别对statsfeedback数据做serializer

首先你可以定义 ModeratorStats 序列化器。

class ModeratorStats(serializers.ModelSerializer):
    stats = serializers.SerializerMethodField(method_name='get_stats_list')

    class Meta:
        model = Moderator
        fields = ['stats']

    def get_stats_list(self, moderator: Moderator):
        ...

TeacherFeedbackSerializerDetail序列化器用于反馈。 现在可见,

from django.core.paginator import Paginator
from rest_framework.response import Response

class TeacherFeedbackViewSet(ViewSet):
    ...

    @base_view
    def list(self, request, pk):
        moderator = get_object_or_404(Moderator, pk=pk)

        # first get page and size param
        page = int(request.GET.get('page', "1"))
        size = int(request.GET.get('size', "10"))
       
        # here I assumed the foreign key field name is `moderator`
        query_set = TeacherFeedback.objects.filter(moderator__id = pk)
        paginator = Paginator(query_set.order_by('id'), size)
        feedbacks = paginator.page(page)
        
        stats_data = ModeratorStats(moderator).data
        feedbacks = TeacherFeedbackSerializerDetail(feedbacks, many=True).data
        
        return Response({"stats": stats_data, "feedback": feedbacks})

并且在前端,您需要上传分页参数,例如 ...?page=1&size=10