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.