Django REST Framework Requests and Response

Objective

In this tutorial you will learn about Django REST frameworks Requests and Responses objects.

Requirements

In order to complete this tutorial you will need to have completed the previous tutorial Django Rest Framework Serialization Tutorial Part 2.

Request Objects

REST framework introduces a Request object that extends the HttpRequest object in Django https://docs.djangoproject.com/en/1.10/ref/request-response/. The core functionality of the Request object is the request.data attribute, similar to request.POST, but is more useful when working with Web APIs. The difference is that request.POST only handles form data and for 'POST' method. On the other hand request.data handles any data as well as for 'POST', 'PUT' and 'PATCH' methods.

Response Objects

REST framework also introduces a Reponse objects which is a TemplateResponse object. TemplateResponse objects retains the details of the context that was provided by the view to compute the response and is not computed until it is needed.

return Response(data)

Status Codes

REST framework has a status module that provides more explicit identifiers for each status code. It would be good practice to use these.

Wrappers

there are 2 wrappers you can use to write API views using Django REST framework.

  1. @api_view decorator for working with function based views.
  2. APIView class for working with class based views. 

These wrappers ensures you receive Request instances in your view as well as adding context to Response objects in order for your app to perform content negotiation.

In addition these wrappers provide behaviour such as returning 405 Method Not Allowed responses as well as handling any ParseError exceptions that occurs when accessing request.data that have malformed input. This will cover the edge cases discussed in the previous tutorial.

Adjusting the views

Using the Request, Response and wrappers discussed lets write some more views.

We will no longer need the JSONResponse class we created in the previous tutorial. We will also refactor the views.py file add the following code into your views.py file:

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

@api_view(['GET','POST'])
def datarow_list(request, format=None):
"""
List all datarow, or create a new datarow.
"""
if request.method =='GET':
datarow = DataRow.objects.all()
serializer = DataRowSerializers(datarow, many=True)
return Response(serializer.data)

elif request.method=='POST':
serializer = DataRowSerializers(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)

@api_view(['GET','PUT','DELETE'])
def datarow_detail(request,pk, format=None):
"""
Retrieve, update or delete a DataRow object.
"""
try:
datarow = DataRow.objects.get(pk=pk)
except DataRow.DoesNotExist:
return Response(status=status.HTTP_404_NOT_FOUND)

if request.method == 'GET':
serializer = DataRowSerializers(datarow)
return Response(serializer.data)

elif request.method == 'PUT':
serializer = DataRowSerializers(datarow, data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

elif request.method == 'DELETE':
datarow.delete()
return Response(status=status.HTTP_204_NO_CONTENT)

Since our responses objects are not fixed to a single content type we can support different format suffixes for the API endpoint.  For example it can handle api and json format (please see example later in the tutorial).

Now we will need to update the data_entry.urls.py file to append a set of format_suffix_patterns to handle the different format suffixes. Add the following into the data_entry.urls.py

from rest_framework.urlpatterns import format_suffix_patterns

...

urlpatterns = format_suffix_patterns(urlpatterns, allowed = ['json','api'])

In order to use the API you will need to comment out the 'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly' that was included in the first tutorial. 

Testing the API

curl [docker-machine ip]:8000/datarows/
# [{"id":1,"date":"2017-02-06","gender":"M","favorite_number":5},{"id":2,"date":"2017-02-04","gender":"F","favorite_number":4152},{"id":6,"date":"2017-02-07","gender":"F","favorite_number":321},{"id":7,"date":"2016-03-21","gender":"F","favorite_number":123454321},{"id":8,"date":"2000-01-01","gender":"F","favorite_number":246},{"id":9,"date":"1999-12-31","gender":"M","favorite_number":789}]
NOTE: you will see something else depending on what data you entered.

# Get the data using .api format suffix
curl [docker-machine ip]:8000/datarows.api
# Let me know what you get.
curl [docker-machine ip]:8000/datarows.json
# [{"id":1,"date":"2017-02-06","gender":"M","favorite_number":5},{"id":2,"date":"2017-02-04","gender":"F","favorite_number":4152},{"id":6,"date":"2017-02-07","gender":"F","favorite_number":321},{"id":7,"date":"2016-03-21","gender":"F","favorite_number":123454321},{"id":8,"date":"2000-01-01","gender":"F","favorite_number":246},{"id":9,"date":"1999-12-31","gender":"M","favorite_number":789}]
NOTE: you will see something else depending on what data you entered.

We will next try posting data using the API

curl --data "date=2017-02-16&gender=F&favorite_number=54321" [docker-machine ip]:8000/datarows/
# {"id":11,"date":"2017-02-16","gender":"F","favorite_number":54321}
# We will check that it has been added into the database
curl [docker-machine ip]:8000/datarows/11/
# {"id":11,"date":"2017-02-16","gender":"F","favorite_number":54321}

Conclusion

You know have a functioning API using Django Rest Framework's Request and Response objects and api wrappers. We have extended the API to be able to handle different format suffixes and have explored how to GET and POST data via the API.

Next we will look into Class Based Views. Sign up to Programmathic's mailing list to be updated when the next tutorial is published.

Subscribe to our mailing list

* indicates required