[Fixed]-How to Paginate within an action in Django Rest Framework

5👍

First move the response data out into it’s own function because you will be using it twice. A better solution would be to create a serializer for the action.

def get_response_data(paginated_queryset):
    data = {
        'id_interface': interface.id_interface,
        'id_EquipoOrigen': interface.id_EquipoOrigen_id,
        'EquipoOrigen': interface.id_EquipoOrigen.nombre,
        'LocalidadOrigen': interface.id_EquipoOrigen.localidad,
        'CategoriaOrigen': interface.id_EquipoOrigen.categoria,
        'id_PuertoOrigen': interface.id_PuertoOrigen_id,
        'PuertoOrigen': interface.id_PuertoOrigen.nombre,
        'estatus': interface.estatus,
        'etiqueta_prtg': interface.etiqueta_prtg,
        'grupo': interface.grupo,
        'if_index': interface.if_index,
        'bw': interface.bw,
        'bw_al': interface.bw_al,
        'id_prtg': interface.id_prtg,
        'ospf': interface.ospf,
        'description': interface.description,
        'id_EquipoDestino': interface.id_EquipoDestino_id,
        'EquipoDestino': interface.id_EquipoDestino.nombre,
        'LocalidadDestino': interface.id_EquipoDestino.localidad,
        'CategoriaDestino': interface.id_EquipoDestino.categoria,
        'id_PuertoDestino': interface.id_PuertoDestino_id,
        'PuertoDestino': interface.id_PuertoDestino.nombre,
        'ultima_actualizacion': interface.ultima_actualizacion,
    } for interface in queryset
    return data

Then you would paginate in the same way as in rest framework’s built in mixins.ListModelMixin.

@action(methods=['get'],detail=False,url_path='registros-data-table',url_name='registros_data_table')
def registros_data_table(self, request):
    queryset = Interfaces.objects.all()

    page = self.paginate_queryset(queryset)
    if page is not None:
        data = self.get_response_data(page)
        return self.get_paginated_response(data)

    data = self.get_response_data(queryset)
    return Response(data)

24👍

Similar to how you would do it for list(...) for ModelViewSets.

rest_framework.viewsets/ModelViewSet.html#list

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

It would just be

class InterfacesViewSet(viewsets.ModelViewSet):
    queryset=Interfaces.objects.all()
    serializer_class=InterfaceSerializer
    pagination_class=PostPageNumberPagination

    @action(methods=['get'],detail=False,url_path='registros-data-table',url_name='registros_data_table')
    def registros_data_table(self, request):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

Output would look like

{
    "count": 1023
    "next": "https://api.example.org/accounts/?page=5",
    "previous": "https://api.example.org/accounts/?page=3",
    "results": [
       …
    ]
}

This is something I am using myself as a mixin for paginated responses.
Throw this guy on anything with a GenericViewSet.

class PaginatedResponseMixin(object):
    def paginated_response(self, queryset=[], serializer_class=None, pagination_class=None,
                           context={}, **kwargs):
        context['request'] = self.request
        queryset = queryset or self.queryset
        pagination_class = pagination_class or self.pagination_class
        paginator = pagination_class()
        serializer_class = serializer_class or self.get_serializer_class()
        for k, v in kwargs.items():
            setattr(paginator, k, v)
        page = paginator.paginate_queryset(queryset, self.request, view=self)
        serializer = serializer_class(page, context=context, many=True)
        return paginator.get_paginated_response(serializer.data)

kwargs uses drf argument for the paginator.

Use like

class PostViewSet(PaginatedResponseMixin, viewsets.ModelViewSet):
    queryset = ...
    serializer = ...

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        return self.paginated_response(queryset, self.get_serializer_class(), CursorPagination, page_size=4)
        return self.paginated_response() # takes default from ViewSet
        return self.paginated_response(page_size=4) # takes partial arguments
👤zyeek

1👍

Adding to other solutions if they did not work, if you want pagination to appear always instead of only when num items < size of page you could use.

def registros_data_table(self, request):
    queryset = self.filter_queryset(self.get_queryset())
    page = self.paginate_queryset(queryset)
    if page is not None:
        serializer = self.get_serializer(page, many=True)
        return self.get_paginated_response(serializer.data)

    serializer = self.get_serializer(page, many=True)
    return self.get_paginated_response(serializer.data)
👤Duilio

Leave a comment