Skip to main content

Command Palette

Search for a command to run...

APIView vs Viewsets

Published
3 min readView as Markdown
APIView vs Viewsets
M

I am a software engineer with 4 years of professional experience, currently specializing in full-stack development.

I work a lot with HTML, CSS, JavaScript, and Python and regularly use React, Vue, and Django to build single-page applications and marketing landing pages.

I also work with React Native to build mobile applications with optimized experiences and for businesses who want to ship products in the market quickly.

Currently, I am a part of a team of 8 as a full-stack engineer where I focus on accessibility, component-driven approaches, and building systems that can scale with ease.

When building your API in Django using Django Rest Framework, you have two choices for writing the controllers: APIView or ViewSets.

APIView

APIView provides a methods handler for HTTP verbs: get, post, put, patch and delete. But you'll need to write a method for each HTTP verb. For example, you are writing a view for a Product resources with a ProductSerializer.

Here's an example of the View with a get() method to return all the products in the database.

from rest_framework.views import APIView


class ProductView(APIView):

    serializer_class = ProductSerializer

    def get(self, request, *args, **kwargs):

        products = Product.objects.all()

        serializer = self.serializer_class(products, many=True)

        return Response(serializer.data)

APIView works best for simple endpoints that won't really require many possibles HTTP requests. What do I mean? Suppose that you want an endpoint to create a product. Pretty simple. We can quickly handle it.

from rest_framework.views import APIView


class ProductView(APIView):

    serializer_class = ProductSerializer

    def get(self, request, *args, **kwargs):

        products = Product.objects.all()

        serializer = self.serializer_class(products, many=True)

        return Response(serializer.data)

    def post(self, request, *args, **kwargs):

        serializer = self.serializer_class(data=request.data)

        serializer.is_valid(raise_exception=True)

        return Response(serializer.data)

We can now create a product, but let's suppose we want to make a certain action on the product, like make it unavailable. To do it, a POST request must be sent to the endpoint and we'll handle the rest with some methods on the Product model.

We can't do that in the ProductView class because there is already a post() method. A solution? Add another APIView ... and this starts becoming quite verbose.🤔 Not to add that you'll have to add two different URL routes for the same resource, which can be a bad API design.

A solution? 💡 ViewSets.

ViewSets

A ViewSet is a class-based view, able to handle all of the basic HTTP requests: GET, POST, PUT, DELETE without hard coding any of the logic. The viewsets module also provides a ModelViewSet class that maps HTTP request actions to CRUD actions in the Database. For example, let's rewrite the ProductView in a ModelViewSet.

class ProductViewSet(ModelViewSet):

    queryset = Product.objects.all()
    serializer_class = ProductSerializer
    http_method_names = ['get', 'post']

Well, this is doing the same job as the ProductView. Interesting, right?🤩 This is even more interesting when we want a certain action on the same resource, no need to add a new class.

class ProductViewSet(ModelViewSet):
...
    @action(detail=True, methods=['post'])
    def unavailable(self, request, *args, **kwargs):
        obj = self.get_object()

        obj.unavailable()

        return Response({'status': 'unavailable'})
...

And that's not all. If you are working with viewsets, instead of URL you'll be working with routers. And here's how to register a viewsets in your router file.

router = routers.DefaultRouter()

router.register(r'products', ProductViewSet)

And with this, you'll have the following structure for the products endpoint.

EndpointHTTP MethodDescription
/products/POSTCreating a new product
/products/GETGet the list of all users
/products/product_id/GETRetrieve a product
/products/product_id/unavailable/POSTMake a product unavailable

Summary

APIView and ViewSet all have their right use cases. But most of the time, if you are only doing CRUD on resources, you can directly use ViewSets to respect the DRY principle. But if you are looking for more complex features, you can go low-level because after all, viewsets are also a subclass of APIView.

More on viewsets and views.

Article posted using bloggu.io. Try it for free.

More from this blog

koladev

73 posts