HomeBack-End & DatabaseDjango Rest API – View – Part2

Django Rest API – View – Part2

In this part we will see about “Views” with DRF

We will cover these concepts from DRF

https://www.django-rest-framework.org/tutorial/2-requests-and-responses/

https://www.django-rest-framework.org/api-guide/requests/

https://www.django-rest-framework.org/api-guide/responses/

https://www.django-rest-framework.org/api-guide/views/#api_view

https://www.django-rest-framework.org/api-guide/generic-views

https://www.django-rest-framework.org/api-guide/viewsets/

You can have a read over them as well from the above links to understand the basics

Response

DRF has introduced a new object “Response” instead of Django’s HttpResponse

The main purpose of this is to be able to return json data types, set headers, response code easily which is required in rest apis’

To see this in action let’s use this in our code

We previously created a route “index” like this

def index(request):
    todo = Todo.objects.get(pk=1)
    ser = TodoSerializer(todo)
    return HttpResponse("Hello, world!") 

If you run this on postman you will see a response

Now lets use “Response”

from rest_framework.response import Response
from rest_framework.decorators import api_view

@api_view()
def index(request):
    todo = Todo.objects.get(pk=1)
    ser = TodoSerializer(todo)
    return Response("Hello, world!") 

P.S. To use “Response” you need to use “api_view” without that you will get error. We will see what is “api_view” decorator later on.

Now if you run the route in postman notice the response

Notice how the response type changed to “JSON” automatically.

This is one of the things “response” does among other things.

Request

Next we the “request” class in DRF. This allows us to read request data and query parameters easily.

Let’s see this in code

@api_view()
def index(request):
    todo = Todo.objects.get(pk=1)
    ser = TodoSerializer(todo)
    return Response(request.query_params) 

This allows us to convert query params to a dict and we can easily use this in our code.

Also to read JSON data in post, put requests we use it like this

@api_view(["POST"])
def dummy(request):
    todo = Todo(task=request.data["task"])
    todo.save()
    return Response(request.data) 

API_VIEW

@api_view decorator is a generic function based decorator used in DRF for rest api’s.

This automatically add “request” object to functions and expects a “response” object.

If you would have noticed traditional django POST request expects CSRF token. But the @api_view decorator removes that as well. 

@api_view decorator by default only works for GET requests for other request types we need to mention it specifically.

Here are few examples on how to use api_view

@api_view(['GET', 'POST'])
def hello_world(request):
    if request.method == 'POST':
        return Response({"message": "Got some data!", "data": request.data})
    return Response({"message": "Hello, world!"})
from rest_framework import status
from rest_framework.decorators import api_view
from rest_framework.response import Response
from snippets.models import Snippet
from snippets.serializers import SnippetSerializer


@api_view(['GET', 'POST'])
def snippet_list(request):
    """
    List all code snippets, or create a new snippet.
    """
    if request.method == 'GET':
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    elif request.method == 'POST':
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Basically in short api_view decorator allows us to create simple function based views.

There are few advanced decorator’s for @api_view which can be seen here https://www.django-rest-framework.org/api-guide/views/#api-policy-decorators

Class Based Views

DRF provide another way to write view similar to above function based view but much more cleaner.

Basically using the class “APIView” we can setup functions for “get”, “post”, “delete” , “put” let’s see an example (P.S. this is not related to our code)

from snippets.models import Snippet
from snippets.serializers import SnippetSerializer
from django.http import Http404
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status


class SnippetList(APIView):
    """
    List all snippets, or create a new snippet.
    """
    def get(self, request, format=None):
        snippets = Snippet.objects.all()
        serializer = SnippetSerializer(snippets, many=True)
        return Response(serializer.data)

    def post(self, request, format=None):
        serializer = SnippetSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class SnippetDetail(APIView):
    """
    Retrieve, update or delete a snippet instance.
    """
    def get_object(self, pk):
        try:
            return Snippet.objects.get(pk=pk)
        except Snippet.DoesNotExist:
            raise Http404

    def get(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet)
        return Response(serializer.data)

    def put(self, request, pk, format=None):
        snippet = self.get_object(pk)
        serializer = SnippetSerializer(snippet, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

    def delete(self, request, pk, format=None):
        snippet = self.get_object(pk)
        snippet.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
from django.urls import path
from rest_framework.urlpatterns import format_suffix_patterns
from snippets import views

urlpatterns = [
    path('snippets/', views.SnippetList.as_view()),
    path('snippets/<int:pk>/', views.SnippetDetail.as_view()),
]

urlpatterns = format_suffix_patterns(urlpatterns)

As we can see above, we are able to create different methods for “Request Types”. This is almost similar to @api_view but just use class based approach rather than function based.

In this as well we can use all decorator classes similar to @api_view e.g

class ListUsers(APIView):
    """
    View to list all users in the system.

    * Requires token authentication.
    * Only admin users are able to access this view.
    """
    authentication_classes = (authentication.TokenAuthentication,)
    permission_classes = (permissions.IsAdminUser,)

    def get(self, request, format=None):
        """
        Return a list of all users.
        """
        usernames = [user.username for user in User.objects.all()]
        return Response(usernames)

Generic Views

Generic views are not but further extension on APIView class with added default behaviors.

So to understand what this is, let’s take an example of a GenericView “ListCreateAPIView

As the name suggest this api view has in-built (default) feature of creating and listing.

So how a Generic View works is that it requires a Queryset, Serializer and PK (primary key) if needed.

Let’s see this in more details. Also to understand this we need to be aware of few basic concepts of routing. But for now, in our existing code let’s make these changes

//todo/urls.py

from django.urls import path , re_path as url

from . import views

from rest_framework import generics

from todo.models import Todo
from todo.serializers import TodoSerializer


urlpatterns = [
    path('dummy', views.dummy, name='dummy'),
    url(r'^test/$', views.index )
    

]

Right now we did something very simple add route url(r’^test/$’, views.index ) using regex . Read more details about it here https://docs.djangoproject.com/en/2.2/topics/http/urls/

Now, if you open post and fire the route /todo/test it should should you the output for “view.index.” nothing special here

next, lets make another change

from django.urls import path , re_path as url

from . import views

from rest_framework import generics

from todo.models import Todo
from todo.serializers import TodoSerializer


urlpatterns = [
    path('dummy', views.dummy, name='dummy'),
    url(r'^test/$', generics.ListCreateAPIView.as_view(queryset=Todo.objects.all(), serializer_class=TodoSerializer), name='todo-list')
]

In this we used a View Class “ListCreateAPIView” as the name suggest it automatically creates actions for “list” and “create” we just need to provide it model and serialize. Now to see this in action

GET route this returns a list of data
Post route adds data

Till now we just understand is very brief the concept of Generic View.

There are multiple types of generic view as can be seen here https://www.django-rest-framework.org/api-guide/generic-views/

Read on more details on the above link as it has lot of information on how to customize GenericViews

Leave a Reply

Your email address will not be published. Required fields are marked *

%d bloggers like this: