djangodjango-rest-frameworkdjango-serializerdjango-pagination

How to do pagination for a serializer field?


I have a task where I need to get stats and feedback by moderator ID. The 'stats' field is general, the 'feedback' field is a list of feedbacks. Can I make pagination for 'feedback' field? Of course I can make different endpoints for stats and feedback, but I'm not allowed to do this.

// 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


Solution

  • In order to realize the pagination here, you need to make serializer for stats and feedback data respectively.

    First you can define the ModeratorStats serializer.

    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):
            ...
    

    And TeacherFeedbackSerializerDetail serializer is for the feedback. Now in view,

    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})
    

    And in frontend, you need to upload pagination params like ...?page=1&size=10.