Django REST Framework allows us to combine the logic for a set of related views in a single view class. They are called Viewsets. ViewSets work exactly same as Generic Views. The only difference is using ViewSet we don’t have to create separate views for getting list of objects and detail of one object. We do not need to configure the urls with ViewSets. Routers generates urls automatically and binds methods for different request method types of ViewSet.

Why we should use ViewSets & Routers.

In larger projects multiple endpoints (URLs) can be difficult to maintain . Besides, same lines of code are repeated in the views. These problems can be solved using ViewSets & Routers.

Advantages of using ViewSet.

  • Can speed up API development
  • Additional layer of abstraction
  • A single viewset can replace multiple views
  • Repeated logic can be combined into a single class
  • Combined logic for a set of related views in a single class
  • Write less code – Promotes DRY (Don’t Repeat Yourself)

Firstly, we will see usage of Viewset class and then ModelViewset class.

viewset.Viewset

It is a simple type of class-based view It does not provides .get() or .post() but provides actions such as .list() .create(). Method handlers for a ViewSet are bound to the corresponding actions. We will implement each of the following methods:

  • list() – Get All Records
  • retrieve() – Get Single Record
  • create() – Create Record
  • update() – Update Record Completely
  • partial_update() – Update Record Partially

views.py

from rest_framework import viewsets
from django.shortcuts import get_object_or_404

class PersonViewset(viewsets.ViewSet):
    queryset = Person.objects.all()
    permission_classes = [IsAuthenticated]

    def list(self, request):
        """
        This will return list of objects.
        """
        serializer_class = PersonSerializers(self.queryset, many=True)
        return Response(serializer_class.data)

    def create(self, request):
        """
        This will create an endpoint for POST request.
        """
        serializer = PersonSerializers(data=request.data)
        if serializer.is_valid():
            serializer.save()
       
        return Response(serializer.data)

    def retrieve(self, request, pk=None):
        """
        Returns a single object
        """
        person = get_object_or_404(self.queryset, pk=pk)
        serializer_class = PersonSerializers(person)
        return Response(serializer_class.data)

    def update(self, request, pk):
        person = Person.objects.get(pk=pk)
        serializer = PersonSerializers(person, data=request.data)
        if serializer.is_valid():
            serializer.save()

        return Response(serializer.data)

    def partial_update(self, request,pk):
        person = Person.objects.get(pk=pk)
        serializer = PersonSerializers(person, data=request.data,partial=True)
        if serializer.is_valid():
            serializer.save()

        return Response(serializer.data)

    def destroy(self, request,pk):
        person = Person.objects.get(pk=pk)
        person.delete()

We will have to do all CRUD operations with this view only. But we will need different endpoints. The endpoints for each different operation should look like that…

ENDPOINTMETHOD ACTION          DESCRIPTION       
person/apiGETlist()          Get all persons    
person/apiPOST   create()        Create new person
person/api/<pk>GETretrieve()      Get person details 
person/api/<pk>PUT    update()       Update person
person/api/<pk>PATCH  partial_update()Partial updateperson
person/api/<pk>DELETE delete()        Delete person

There are several ways to configure urls for viewsets. We can write individual urls for each of them. But we will do it more smartly using routers.

Routers

Automatically generate URL patterns for the developer. Automated way of creating URLs.

Routes for create/retrieve/update/destroy style actions. We no longer need to deal with wiring up the URL conf ourselves.

urls.py

from rest_framework.routers import DefaultRouter
from .views import PersonViewset

# Object of DefaultRouter
router = DefaultRouter()
# register view in router
router.register('api',PersonViewset, basename='person')
urlpatterns = router.urls

Or can be configured like that.

from rest_framework.routers import DefaultRouter
from .views import PersonViewset

# Object of DefaultRouter
router = DefaultRouter()
router.register('api',PersonViewset, basename='person')

urlpatterns = [
    path('', include(router.urls)),
]

We are done with our implementation. Let’s run the server and check if our endpoints work perfectly

List

127.0.0.1:8000/person/api/

Single Instance

127.0.0.1:8000/person/api/<pk>

You can see that this endpoint allows all the HTTP methods to take action.

Create/Update

127.0.0.1:8000/person/api/

Delete

127.0.0.1:8000/person/api/<pk>

ModelViewSet

This is more easy to use than a viewset because it provides default create(), retrieve(), update(), partial_update(), destroy() and list() actions.

from rest_framework.viewsets import ModelViewSet

class PersonModelViewSet(ModelViewSet):
    serializer_class = PersonSerializers
    queryset = Person.objects.all()

The rest of the things will be same as ViewSet. Have fun with ModelViewSet.

+ posts

Author | Python-Django Developer

+ posts

Full-stack Developer (Python | Django | React | React-Native | Angular | Vue)