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.
Concept
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).
In views.py
:
from rest_framework import generics
class DeleteProduct(generics.DestroyAPIView):
"""Remove product"""
permission_classes = (permissions.IsAuthenticated,) # Limit to authenticated users only
In urls.py
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 destroy()
method:
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…
Conclusion
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!