Refactoring views in Django REST framework

Question:

I am very new to Python and Django. I have this app that returns 4 different types of transport routes (In the code I only showed two, cause they basically are the same…).

These 4 views use the same class-based views, but only the models’ names are different. As they all return the same functionality(get, post, put and delete) I ended up repeating the same code over and over again.

Is there any way I can refactor it simpler?

Any help is appreciated! Thank you 🙂

views.py

********* tube view ***********

class TubeListView(APIView):

    def get(self, _request, format=None):
        tubeRoutes = TubeRoute.objects.all()
        serialized_with_user = NestedTubeRouteSerializer(tubeRoutes, many=True)
        return Response(serialized_with_user.data)

    def post(self, request, format=None):
        request.data['traveler'] = request.user.id
        serializer = TubeRouteSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE)


class TubeDetailView(APIView):

    def get(self, _request, pk, format=None):
        tubeRoute = TubeRoute.objects.get(pk=pk)
        serialized_with_user = NestedTubeRouteSerializer(tubeRoute)
        return Response(serialized_with_user.data)


    def put(self, request, pk, format=None):
        request.data['traveler'] = request.user.id
        tubeRoute = self.get_object(pk)
        if tubeRoute.owner.id != request.user.id:
            return Response(status=status.HTTP_401_UNAUTHORIZED)

        updated_serializer = TubeRouteSerializer(tubeRoute)

        if updated_serializer.is_valid():
            updated_serializer.save()
            return Response(updated_serializer.data, status=status.HTTP_200_OK)
        return Response(updated_serializer.errors, status=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE)


    def delete(self, request, pk, format=None):
        tubeRoute = self.get_object(pk)
        if tubeRoute.owner.id != request.user.id:
            return Response(status=status.HTTP_401_UNAUTHORIZED)
        tubeRoute.delete()
        return Response(status=status.HTTP_200_OK)

********* bus view ***********

class BusListView(APIView):

    def get(self, _request, format=None):
        busRoutes = BusRoute.objects.all()
        serialized_with_user = NestedBusRouteSerializer(busRoutes, many=True)
        return Response(serialized_with_user.data)

    def post(self, request, format=None):
        request.data['traveler'] = request.user.id
        serializer = BusRouteSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE)


class BusDetailView(APIView):

    def get(self, _request, pk, format=None):
        busRoute = BusRoute.objects.get(pk=pk)
        serialized_with_user = NestedBusRouteSerializer(busRoute)
        return Response(serialized_with_user.data)


    def put(self, request, pk, format=None):
        request.data['traveler'] = request.user.id
        busRoute = self.get_object(pk)
        if busRoute.owner.id != request.user.id:
            return Response(status=status.HTTP_401_UNAUTHORIZED)

        updated_serializer = BusRouteSerializer(busRoute)

        if updated_serializer.is_valid():
            updated_serializer.save()
            return Response(updated_serializer.data, status=status.HTTP_200_OK)
        return Response(updated_serializer.errors, status=status.HTTP_416_REQUESTED_RANGE_NOT_SATISFIABLE)


    def delete(self, request, pk, format=None):
        busRoute = self.get_object(pk)
        if busRoute.owner.id != request.user.id:
            return Response(status=status.HTTP_401_UNAUTHORIZED)
        busRoute.delete()
        return Response(status=status.HTTP_200_OK)

Asked By: aichichi

||

Answers:

You should take a look to these class based views in DRF.

For instance, the following code should be enough to replace your first TubeListView:

from rest_framework import generics

class TubeListView(generics.ListCreateAPIView):
    queryset = TubeRoute.objects.all()
    serializer_class = NestedTubeRouteSerializer

    def post(self, request, *args, **kwargs):
        request.data['traveler'] = request.user.id
        return super().post(self, request, *args, **kwargs)

If you don’t need any special behavior, you don’t have to redefine get, post, etc methods. But if you need to change data, for instance in your POST method, you can do your stuff and then call the usual behavior of the superclass with super().post(self, request, *args, **kwargs)

Answered By: Martin Lehoux

I would recommend checking Viewsets in Django Rest Framework, specifically ModelViewset.

The actions provided by the ModelViewSet class are .list(), .retrieve(), .create(), .update(), .partial_update(), and .destroy() which coincide with the following:

  • get —-> .retrieve()
  • list —-> .list()
  • post —-> .create()
  • patch —-> .partial_update()
  • put —-> .update()
  • delete —-> .destroy()

Let me provide a case using the Bus Views

The Modelviewset form would be:

class BusViewset(ModelViewset):
    queryset = BusRoute.objects.all()
    serializer_class = NestedBusRouteSerializer

    def get_queryset(self):
        return self.queryset.filter(
            owner__id=self.request.user.id
        )

For the permission checks you implemented, Django Rest Framework has a Permissions System that takes care of that. For you own use case, a custom permission would be sufficient.

Answered By: Seunfunmi Adegoke