As a seasoned API developer, you end up doing very repetitive tasks so you might be looking for tools that makes your development time faster. As a novice, you might be looking for a way to implement best practice and REST standards easily out of the box without too much hesitation.
In both cases, Django Rest Framework (DRF) is a great solution. It is a standard, widely used, and fully featured API framework that will not only save you a lot of time but also show you the right way to develop RESTful APIs. More particularly, DRF proposes generic views, that’s to say pre-built endpoints for your API. Let’s see how to leverage this feature to achieve rapid API development.
I put the below code in a little working Django project right here.
DRF’s Generic views are perfect for simple APIs that basically do CRUD (create, read, update, delete) on the database without too much data processing. For example, let’s say you have a
product table that contains all your store products and you want to expose these products as is to customers through an API, then it’s a perfect use case for the
ListAPIView (see below).
From now on I’m assuming that you installed Python, Django, DRF and that you know tha basics about Django.
Basic Example 1: Reading Data
Let’s create an API endpoint showing all the products customers. In your
views.py do the following:
from rest_framework import generics from .serializers import ProductsSerializer class GetProducts(generics.ListAPIView): """Return all products.""" serializer_class = ProductsSerializer
ProductsSerializer is the serializer that will convert your data from the database to API friendly data. This serializer should be put in
serializers.py and will be in charge of retrieving data from your
Product model and transforming them:
from rest_framework import serializers from .models import Product class ProductsSerializer(serializers.ModelSerializer): """Serialize products.""" class Meta: model = Product fields = ("__all__")
Now in your
urls.py create the route to this endpoint:
from django.urls import path from .views import GetProducts urlpatterns = [ path('get-products/', GetProducts.as_view(), name='get_products'), ]
As you can see this is dead simple as DRF is doing many things for you under the hoods! You now have an endpoint (
/get-products/) that you can consume with
get HTTP requests, and that outputs all products with an API format (usually
json but it depends on your settings).
Basic Example 2: Deleting Data
Now let’s create an endpoint dedicated to deleting a product for authenticated users only. It’s even simpler as it does not require to serialize data (once the product is deleted no data can be returned to user anymore).
from rest_framework import generics class DeleteProduct(generics.DestroyAPIView): """Remove product""" permission_classes = (permissions.IsAuthenticated,) # Limit to authenticated users only
from django.urls import path from .views import DeleteProduct urlpatterns = [ path('delete-product/', DeleteProduct.as_view(), name='delete_product'), ]
Now you have a
/delete-product/ endpoint that you can use to delete one product at a time using
delete HTTP requests, and that only accepts authenticated requests (authentication mechanism depends on your settings).
Customizing Generic Views’ Behavior
Each generic view can be customized by writing a
get_queryset() method. For example let’s say you only want to show products that have an
active flag set to
True in db. You could do this:
from rest_framework import generics from .serializers import ProductsSerializer from .model import Product class GetProducts(generics.ListAPIView): """Return all active products.""" permission_classes = (permissions.IsAuthenticated,) serializer_class = ProductsSerializer def get_queryset(self): """Filter active products.""" return Product.objects.filter(active=True)
get_queryset() is a common method that you have in every generic views. Some generic views also have their own methods to control more precisely the behavior of the endpoint. For example, let’s say that you don’t really want to delete products but just mark them as inactive. You could use the
from django.shortcuts import get_object_or_404 from rest_framework.response import Response from rest_framework import status class DeleteProduct(generics.DestroyAPIView): """Remove product""" permission_classes = (permissions.IsAuthenticated,) def destroy(self, request, pk): """ By default, DestroyAPIView deletes the product from db. Here we only want to flag it as inactive. """ product = get_object_or_404(self.get_queryset(), pk=pk) product.active = False product.save() return Response(status=status.HTTP_204_NO_CONTENT)
In the above example we’re first trying to look for the product that the user wants to delete. If we can’t find it we return a 404 code to user. If the product is successfully marked as inactive, we just return a 204 code to user meaning that the product was successfully deleted.
Generic views are perfect for simple use cases and it’s sometimes wiser to use the classic
APIView for edge cases. For example, let’s say you want not only to return products to the user but also enrich data with other information that is not in the
Product model (e.g. orders related to this product, product manufacturer, etc.). In that case, if you wanted to use generic views, you would have to define new fields in the serializer thanks to additional
get_new_field() methods which can easily make your serializer very ugly…
As you could see, DRF’s generic views make API endpoints development very easy thanks to a bit of magic under the hood. However you should keep in mind that generic views cannot apply to every use cases as sometimes tweaking generic views is harder than developing things by yourself from scratch!
I hope you liked this little how to. I’d love to have your feedbacks!