Home > Software design >  How to do pagination for a serializer field?
How to do pagination for a serializer field?

Time:05-25

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

CodePudding user response:

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.

  • Related