mercredi 9 octobre 2019

Decorator pattern to implement filtering and pagination in django api rest

I am trying to implement filtering and pagination in an rest api developed with Django and python. I need to choose when to use filtering, pagination, both or none of them in my endpoints. To do this, I have thought of combining a template pattern with a decorator pattern.

This is my designed solution, when ModelViewSet is an abstract class provided by Django Rest Framework, DummyViewSet is my endpoint, and FilteringViewSetDecorator and PaginationViewSetDecorator are my decorators.

enter image description here

This is my source code:

class CustomView(viewsets.ModelViewSet):

    __metaclass__ = ABCMeta

    @abstractmethod
    def get_queryset(self, request, **kwargs):
        pass

    def serialize_queryset(self, queryset):
        print("CustomView: _serialize_queryset")
        serializer = self.get_serializer(queryset, many=True)
        return JsonResponse(serializer.data, safe=False)

    def list(self, request, **kwargs):
        print("CustomView: list")
        queryset = self.get_queryset(request, **kwargs)
        return self.serialize_queryset(queryset)


class ViewSetDecorator(CustomView):
    __metaclass__ = ABCMeta

    def __init__(self, viewset):
        print("ViewSetDecorator: __init__")
        self._viewset = viewset

    def get_queryset(self, request, **kwargs):
        print("ViewSetDecorator: _get_queryset")
        return self._viewset.get_queryset(request, **kwargs)


class FilteringViewSet(ViewSetDecorator):

    @staticmethod
    def __build_filters(params):
        filters = []
        for key, value in params.items():
            if key != 'limit' and key != 'offset' and value is not None and value != '':
                query_filter = {key: value}
                filters.append(query_filter)
        return filters

    def get_queryset(self, request, **kwargs):
        print("FilteringViewSet: _get_queryset")
        queryset = super(FilteringViewSet, self).get_queryset(request, **kwargs)
        filters = self.__build_filters(request.GET)
        for query_filter in filters:
            queryset = queryset.filter(**query_filter)
        return queryset


class PaginationViewSet(ViewSetDecorator):

    def get_queryset(self, request, **kwargs):
        print("PaginationViewSet: _get_queryset")
        queryset = super(PaginationViewSet, self).get_queryset(request, **kwargs)
        limited = request.query_params.get('limit')
        if limited is not None:
            return self.paginate_queryset(queryset)
        return queryset

    def serialize_queryset(self, queryset):
        print("PaginationViewSet: _serialize_queryset")
        serializer = self.get_serializer(queryset, many=True)
        return self.get_paginated_response(serializer.data)


class DummyViewSet(CustomView):

    queryset = []
    serializer_class = DummySerializer
    permission_classes = (IsAuthenticated, DummyPermission)

    def get_queryset(self, request, **kwargs):
        dummy_objects = list_dummy_objects()
        return dummy_objects

    def list(self, request, **kwargs):
        pagination_viewset = PaginationViewSet(self)
        return pagination_viewset.list(request, **kwargs)

The problem is that decorator functions need to use attributes from the ModelViewSet superclass that are initialized at the endpoint but are not in the decorator, such as "self.request". Is there any way for the decorator to access this data or am I using design patterns incorrectly?

Thank you.

Error trace:

   File "####################################/viewsets/decorators.py", line 21, in list
     queryset = self.get_queryset(request, **kwargs)
   File "####################################/viewsets/decorators.py", line 64, in get_queryset
     return self.paginate_queryset(queryset)
   File "##################################/local/lib/python2.7/site-packages/rest_framework/generics.py", line 173, in paginate_queryset
     return self.paginator.paginate_queryset(queryset, self.request, view=self)
   AttributeError: 'PaginationViewSet' object has no attribute 'request'

Aucun commentaire:

Enregistrer un commentaire