The Django REST Framework (DRF) allows you create REST based APIs quickly and simply, providing a range of features such authentication, error handling, serialization and full CRUD (Create Replace Update Delete) based operations to your database.
Within this article will look at how to create a very basic API. The API will take in a JSON input, validate and return a response back to the client.
Table of Contents
Installing DRF
The first thing you will need to do is install DRF.
pip install Django==1.8 pip install djangorestframework==3.3.2
Once done, lets create a Django project and app.
django-admin startproject myproject cd myproject/ python manage.py startapp myapp
Settings.py
Within your settings file add DRF and your new Django app.
INSTALLED_APPS = [ + 'rest_framework', + 'myapp', ...
Serializers.py
Serializers allow you to build a logical representation of your data, in turn allowing you to,
- Convert incoming data from JSON to Python data structures and vice-versa.
- Perform data validation, this is extremely similar to how the Forms and ModelForm classes work within Django.
Below shows you an example. We simply create a serializer, defining a key named ’email’ and a value that must contain a dictionary.
from rest_framework import serializers class MyAPISerializer(serializers.Serializer): email = serializers.DictField()
Once your data has been passed to your serializer serializer.is_valid()can be called. Errors can then be returned by callingserializer.errors. Below shows can example,
serializer = MyAPISerializer(data={'wrong_key': 'bad_value'}) serializer.is_valid() # False serializer.errors # {'email': [u'This field is required.']}
Views.py
Lets next look at views.py. First we apply a decorator to only allow GET and POST requests. Our data is then passed to our serializer we check if the data is valid, if it is a HTTP 200 response is returned back to the client. If the data is not valid the error is returned.
from rest_framework.decorators import api_view @api_view(['GET','POST']) def myapi(request): serializer = MyAPISerializer(data=request.data) if serializer.is_valid(): return Response(serializer.data, status=status.HTTP_200_OK) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
Exceptions
Django DRF contains a number of built in exceptions, such as ParseError, ValidationError and PermissionError. When these exceptions are raised, for example due to malformed input, a JSON response with the appropriate details and response code is returned.
Additionally, these exceptions can be manually raised. It is also worth noting, at the point of raising a custom exceptions you can also amend the response details returned.
raise NotAuthenticated(detail='User not authenticated')
Custom exceptions can also be created by subclassing APIException. Below shows an example,
from rest_framework.exceptions import APIException class FluxError(APIException): status_code = 503 default_detail = 'Flux Error. Please check flux capacitor'
Examples
Based on the previous Django configuration (views, serializers and views) and our new knowledge of the DRF exceptions, lets see some examples. Within our examples we will send the correct input to our API , the incorrect input and then view the returned responses. First all lets run Djangos built in webserver.
python manage.py runserver 0.0.0.0:8002 &
Correct Input
Next we use httpie to send in a HTTP POST request. Here we provide the required data, this was previously set withinserializer.py.
echo '{"email":{"john":"%MINIFYHTML6824ac9e491d4ea2545ff3e533672e7b7%"}}' | http [10/Mar/2016 23:17:24]"POST /myapi HTTP/1.1" 200 33 HTTP/1.0 200 OK Allow: POST, OPTIONS, GET Content-Type: application/json Date: Thu, 10 Mar 2016 23:17:24 GMT Server: WSGIServer/0.1 Python/2.7.5 Vary: Accept, Cookie X-Frame-Options: SAMEORIGIN { "email": { "john": "%MINIFYHTML6824ac9e491d4ea2545ff3e533672e7b8%" } }
Malformed Input
Now, if we send in some malformed JSON the DRF raises an exception, this generates a HTTP response with the appropriate error.
Below the quotes are removed from ’email’.
echo '{email:{"john":"%MINIFYHTML6824ac9e491d4ea2545ff3e533672e7b9%"}}' | http
[11/Mar/2016 20:20:56]"POST /myapi HTTP/1.1" 400 107
HTTP/1.0 400 BAD REQUEST
Allow: POST, OPTIONS, GET
Content-Type: application/json
Date: Fri, 11 Mar 2016 20:20:56 GMT
Server: WSGIServer/0.1 Python/2.7.5
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN
{
"detail": "JSON parse error - Expecting property name enclosed in double quotes: line 1 column 2 (char 1)"
}
Lets change the data that is sent in. We change the input so that the JSON is different to what is defined within the serializer.py.
echo '{"bananas":{"john":"%MINIFYHTML6824ac9e491d4ea2545ff3e533672e7b10%"}}' | http
[11/Mar/2016 20:18:35]”POST /myapi HTTP/1.1″ 400 37
HTTP/1.0 400 BAD REQUEST
Allow: POST, OPTIONS, GET
Content-Type: application/json
Date: Fri, 11 Mar 2016 20:18:35 GMT
Server: WSGIServer/0.1 Python/2.7.5
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN {
“email”: [
“This field is required.”
]
}
Not Found Catchall
Now everything above is all well and good, but what happens when the client sends in a request for a URI that is not configured within urls.py ?
Though a 404 is returned (shown below) the response is not presented as JSON due to the request not making it through to DRF, this is because the error is raised within urls.py.
echo '{"email":{"john":"%MINIFYHTML6824ac9e491d4ea2545ff3e533672e7b11%"}}' | http http://127.0.0.1:8002/wronguri | more
[19/Mar/2016 21:49:39]"POST /wronguri HTTP/1.1" 404 2036
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>Page not found at /wronguri</title>
<meta name="robots" content="NONE,NOARCHIVE">
To resolve this a catchall rule is placed within your urls.py and assigned to a view that will return the necessary response.
Below is an example of the configuration,
// views.py from rest_framework.exceptions import APIException,NotFound from rest_framework.decorators import api_view @api_view(['GET']) def not_found(request): raise NotFound('Resource Not Found') // urls.py (placed at the bottom) + url(r'^.*$', 'myproject.views.not_found'),
Lets retest. Now we see that the response is returned as JSON.
echo '{"email":{"john":"%MINIFYHTML6824ac9e491d4ea2545ff3e533672e7b12%"}}' | http http://127.0.0.1:8002/wronguri
[19/Mar/2016 22:10:44]"POST / HTTP/1.1" 404 31
HTTP/1.0 404 NOT FOUND
Allow: POST, OPTIONS, GET
Content-Type: application/json
Date: Sat, 19 Mar 2016 22:10:44 GMT
Server: WSGIServer/0.1 Python/2.7.5
Vary: Accept, Cookie
X-Frame-Options: SAMEORIGIN
{
"detail": "Resource Not Found"
}
- How to Configure a BIND Server on Ubuntu - March 15, 2018
- What is a BGP Confederation? - March 6, 2018
- Cisco – What is BGP ORF (Outbound Route Filtering)? - March 5, 2018
Want to become a Django expert?
Here is our hand-picked selection of the best courses you can find online:
The Complete Web Development Bootcamp course
Django Practical Guide course
Django Full Stack Developer Bootcamp
and our recommended certification practice exams:
AlphaPrep Practice Tests - Free Trial