Table of Contents
Introduction
Here you will see how to create a rest api using Django Rest Framework.
I chose to use Django Rest Framework because i am going to use this for a Machine Learning project in which i use Python as the default language, which is the language of Django.
The complete code for this project is in my github here: https://github.com/fernandorodriguespro/rest-api-django-drf-mysql
Preparation
Create the project directory e cd into it
mkdir project-name && cd project-name
Create a virtualenv to isolate our package dependencies locally, and activate the virtual environment
virtualenv -p python3 env && source env/bin/activate
Django Install
Install Django into the virtualenv
pip3 install django
Set up a new project and cd into it
# Note the trailing '.' character django-admin.py startproject project . && cd project
Define a single application for this project
django-admin.py startapp appname && cd ..
Add the created App to INSTALLED_APPS in settings.py, using your favorite IDE
INSTALLED_APPS = [
#...
'project.appname'
]
Remove the default database engine that is automatically set for you. By default Django adds the SQLite, we don’t want that. Remove the definitions from DATABASES in settings.py. It should look like this, after you remove the default key:
DATABASES = {
}
Test if the App is working
python3 manage.py runserver
Go to http://localhost:8000/ to check if you see a Welcome page.
MySQL Install (on Mac)
For this Tutorial i will use MySQL, so we need to install it, on Mac using Homebrew you do this:
# this is a global install brew install mysql
MySQL Setup on Django
Now install mysqlclient to the project
# this is an env install pip3 install mysqlclient
On Mac i received a linker error telling that ld: library not found for -lssl, here is the solution:
LDFLAGS=-L/usr/local/opt/openssl/lib pip3 install mysqlclient # explicitly define the linker openssl path
Add the connection settings in settings.py, your config should look like this:
# project settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'dbname',
'USER': 'root',
'PASSWORD': '',
'HOST': '127.0.0.1', # Or an IP Address that your DB is hosted on
'PORT': '',
},
}
Create the models that will be migrated to the database, more information on django model fields here https://docs.djangoproject.com/en/1.9/ref/models/fields/
# project/appname/models.py
from django.db import models
class University(models.Model):
name = models.CharField(max_length=50)
class Meta:
verbose_name = "University"
verbose_name_plural = "Universities"
def __unicode__(self):
return self.name
class Student(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
university = models.ForeignKey(University, on_delete=models.DO_NOTHING)
class Meta:
verbose_name = "Student"
verbose_name_plural = "Students"
def __unicode__(self):
return '%s %s' % (self.first_name, self.last_name)
Generate these entities in the database
First create a set of rules.
python3 manage.py makemigrations appname
Then apply all the migrations
python3 manage.py migrate
Populate the Database
Django offers the Admin interface, a visual way to check and populate the database. To enable it add this to the app admin.py file.
from django.contrib import admin from .models import University, Student admin.site.register(University) admin.site.register(Student)
Create the admin user
python3 manage.py createsuperuser
Check if the Admin interface is working here http://localhost:8000/admin/
Install Django REST Framework
Install it using PIP3
pip3 install djangorestframework
Declare it in INSTALED_APPS on settings.py
INSTALLED_APPS = [
#...
'rest_framework'
]
Write some model-based serializers (this process converts python objects into json and vice versa, called serialization/deserialization task). To do this, create a file named serializers.py in your appname, and then add the content below.
# appname/serializers.py, this file was created manually
from rest_framework import serializers
from .models import University, Student
class UniversitySerializer(serializers.ModelSerializer):
class Meta:
model = University
fields = '__all__'
class StudentSerializer(serializers.ModelSerializer):
class Meta:
model = Student
fields = '__all__'
More info here: http://www.django-rest-framework.org/api-guide/serializers/#modelserializer
Make a ViewSet (This is something amazing, lots of free added functionality here in ModeViewSet code). It’s a full CRUD set.
# appname/views.py
from django.shortcuts import render
from rest_framework import viewsets
from .models import University, Student
from .serializers import UniversitySerializer, StudentSerializer
class StudentViewSet(viewsets.ModelViewSet):
queryset = Student.objects.all()
serializer_class = StudentSerializer
class UniversityViewSet(viewsets.ModelViewSet):
queryset = University.objects.all()
serializer_class = UniversitySerializer
Define mappings (routes) from http request addresses to the views (controllers). Your project urls.py should look like this:
# project/urls.py
# Attach app-level urls to the general workflow
from django.contrib import admin
from django.urls import path
from django.conf import settings
from django.conf.urls import url, include
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^api/', include('project.appname.urls')),
]
'''
if settings.DEBUG:
from django.conf.urls.static import static
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
RAISES AN ERROR
File "/Users/fernandorodrigues/Documents/projects-angular/arin.ai/project/urls.py", line 18, in
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
File "/Users/fernandorodrigues/Documents/projects-angular/arin.ai/env/lib/python3.6/site-packages/django/conf/urls/static.py", line 21, in static
raise ImproperlyConfigured("Empty static prefix not permitted")
django.core.exceptions.ImproperlyConfigured: Empty static prefix not permitted
'''
Add app “child” routes to appname/urls.py
# create this file
# rerouting all requests that have ‘api’ in the url to the apps.core.urls
from django.conf.urls import url
from rest_framework import routers
from project.appname.views import StudentViewSet, UniversityViewSet
router = routers.DefaultRouter()
router.register(r'students', StudentViewSet)
router.register(r'universities', UniversityViewSet)
urlpatterns = router.urls
Check if the service is functional accessing http://localhost:8000/api/universities/
Here everything must be working fine!!
Creating a Custom View with APIView
In the above example, we created two ViewSets: StudentViewSet and UniversityViewSet. These two classes inherits viewsets.ModelViewSet, which gives us, for free, an entire set of CRUD operations. But sometimes we don’t need all this, we may want to have full control of the View for something more simple.
Here is how to add a custom View for an application. Add this to the views.py of the app.
from rest_framework.views import APIView, Response
class CustomView(APIView):
def get(self, request, format=None):
return Response("Some Get Response")
def post(self, request, format=None):
return Response("Some Post Response")
Create a route for this View
# create this file
# rerouting all requests that have ‘api’ in the url to the <code>apps.core.urls</code>
from django.conf.urls import url
from rest_framework import routers
from project.appname.views import StudentViewSet, UniversityViewSet, CustomView
router = routers.DefaultRouter()
router.register(r'students', StudentViewSet)
router.register(r'universities', UniversityViewSet)
urlpatterns = [
url(r'customview', CustomView.as_view()),
]
urlpatterns += router.urls
Go to http://localhost:8000/api/customview to check if the route is working.
Generating Documentation for this API with Django Rest Swagger
First install the wheel
pip3 install django-rest-swagger
Declare this app in INSTALED_APPS
# project/settings.py
INSTALLED_APPS = [
#...
'rest_framework_swagger'
]
Expose it at routing under api/docs
# appname/urls.py
from django.conf.urls import url
from rest_framework import routers
from project.arin.views import StudentViewSet, UniversityViewSet
from rest_framework_swagger.views import get_swagger_view
router = routers.DefaultRouter()
router.register(r'students', StudentViewSet)
router.register(r'universities', UniversityViewSet)
schema_view = get_swagger_view(title='Pastebin API')
urlpatterns = [
url(r'customview', CustomView.as_view()),
url(r'^docs/', schema_view),
]
urlpatterns += router.urls
Check if the service is functional accessing http://localhost:8000/api/docs/
Sources:
http://www.django-rest-framework.org/
https://tests4geeks.com/django-rest-framework-tutorial/
https://docs.djangoproject.com/en/1.9/ref/models/fields/
https://marcgibbons.com/django-rest-swagger/

Stu
May 24, 2018Fantastic guide. Just what I needed. Thankyou.
Clancy Emanuel
September 12, 2018This was awesome, thanks. Two minor issues on Windows 10 using VS Code:
1. Django wouldn’t install on Windows. Fixed with “pip install –only-binary :all: mysqlclient” (courtesy of kaya’s answer: https://stackoverflow.com/questions/26866147/mysql-python-install-error-cannot-open-include-file-config-win-h)
2. When making the ViewSets for the models, PyLint complained about not being able to import .serializers because the classes did not have an “object” member. Use this link to fix: https://stackoverflow.com/questions/45135263/class-has-no-objects-member
Also, you could consider saying something about inspectdb for legacy databases.
Clancy Emanuel
September 18, 2018#1 I meant to say mysqlclient would not install.
Aguilared
December 5, 2018Brother!!! Excelent…
Aguilared
December 6, 2018MySQL Setup on Django in Windows 10,
pip3 install mysqlclient. This didnt work for me.
Use:
pip install –only-binary :all: mysqlclient
and it went all through, no need for MS Visual C++ 14 Build tools and stuff
Note: for now this doesnt work with Python3.7, i also had to downgrade to Python 3.6.5
André
May 5, 2019Great tutorial thank you.