Django REST framework APIView vs Viewset

Viewed 1129

I am currently using an APIView for a url ('/api/events/<int:pk>'), but I can't use this same APIView for the list view as there is no distinction between list (get all) and detail (get 1). This view is unrelated to a model, because I am calling an externel API.

The ViewSet does have distinction between list and retrieve, but if I use that, I HAVE to use the serializer_classes. But, I don't have a serializer on this view.

I know I can create a second APIView so I have 1 for both the detail and the list view, but I find that a little cumbersome. It's cleaner to have it in 1 class.

What to do best?

Example:"

class TestView(APIView):
    """
    Test123123o8
    """
    serializer_class = Test1Serializer

    #this methods acts on the /test GET route
    def get(self, request):
        return Response(Test.objects.all())

    # route that acts on the DETAIL route (for example '/api/test/1'). 
    # How to implement this in the URL?
    def get_detail(self, request, pk):
        return Response(test.objects.get(pk=pk))
    
    def post(self, request, format=None):
        serializer = Test1Serializer(data=request.data)
        if serializer.is_valid():
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
1 Answers

Using APIView

For demo purpose I still use a queryset inside of the retrieve/list methods, but this is not mandatory you should put your own logic there.

class TestAPIView(views.APIView):

    def get(self, request, *args, **kwargs):
        if 'pk' in kwargs:
            return self.retrieve(request, *args, **kwargs)
        else:
            return self.list(request, *args, **kwargs)

    def retrieve(self, request, *args, **kwargs):
        instance = Test.objects.get(pk=kwargs['pk'])
        serializer = TestSerializer(instance=instance)
        return Response(serializer.data)

    def list(self, request, *args, **kwargs):
        queryset = Test.objects.all()
        serializer = TestSerializer(queryset, many=True)
        return Response(serializer.data)

Then in urls.py add this lines:

url(r'^test/$', TestAPIView.as_view(), name='test-list'),
url(r'^test/(?P<pk>[0-9]+)/$', TestAPIView.as_view(), name='test-detail'),

or in newer django versions you can use the path

path('test/', TestAPIView.as_view(), name='test-list'),
path('test/<int:pk>/', TestAPIView.as_view(), name='test-detail),

Using ViewSet

You'll need to add the mapping of the request method to your function in your urls definition.

from rest_framework import viewsets

class TestViewSet(viewsets.ViewSet):

    def retrieve(self, request, *args, **kwargs):
        instance = Test.objects.get(pk=kwargs['pk'])
        serializer = TestSerializer(instance=instance)
        return Response(serializer.data)

    def list(self, request, *args, **kwargs):
        queryset = Test.objects.all()
        serializer = TestSerializer(queryset, many=True)
        return Response(serializer.data)

Then in urls.py add this lines:

url(r'^test/$', TestViewSet.as_view({'get': 'list'}), name='test-list'),
url(r'^test/(?P<pk>[0-9]+)/$', TestViewSet.as_view({'get': 'retrieve'}), name='test-detail'),

or in newer django versions you can use the path

path('test/', TestViewSet.as_view({'get': 'list'}), name='test-list'),
path('test/<int:pk>/', TestViewSet.as_view({'get': 'retrieve'}), name='test-detail),
Related