Category Archives: Python

Django REST Framework (DRF)- Getting started

This post assumes that you have some background knowledge on python and Django, and you know about setting up virtual environment and getting a Django environment up and running.

I will briefly go about setting up the environment first.

Setup Guide

1. Install virtual environment 
pip install virtualenv
2. setup a project folder 
virtualenv myproj/
OR 
setup project folder with specific python version 
virtualenv myproj --python=/usr/bin/python3.5
3. Activate virtual environment
source myproj/bin/activate

Reference to virtual environment 
http://python-guide-pt-br.readthedocs.io/en/latest/dev/virtualenvs/

4. Once inside Virtual environment, install django  
cd mproj/
pip install django
5. Install rest framework
pip install djangorestframework
6. Optional - Swagger to view APIs
pip install django-rest-swagger
7. Create a django project
django-admin startproject mysite

By now we have a django project ready- mysite. You can open it up in your favorite editor.

Look for settings.py (mysite/mysite) and modify Installed apps to include rest framework

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'rest_framework_swagger',
]

If you are planning to use swagger add these to urls.py (same folder as settings)

from django.conf import settings

at top.
And

if settings.DEBUG:
    from rest_framework_swagger.views import get_swagger_view
    schema_docs_view = get_swagger_view(title='Mysite API')
    urlpatterns += [
        url(r'^__docs__/$', schema_docs_view),
    ]

at the end.

Let’s create a sample app now. Go to shell and create the app

cd mysite/
python manage.py startapp employees

If you will look at the editor, you will find employees app with default folder structure and files added.

You will find an empty models.py. This is where we will define our database entities or tables. Lets get started and create a simple Employee model

import uuid
from django.db import models

class Employee(models.Model):
	id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
	name = models.CharField(max_length=256)
	title = models.CharField(max_length=256)
	department = models.CharField(max_length=256)

Next we need to create a serializer. Serializers help us convert model data to (and from) a required format. More on serializers https://docs.djangoproject.com/en/1.11/topics/serialization/

Create a serializers.py in employees folder (parallel to models.py) and add

from rest_framework import serializers
from .models import Employee

class EmployeeSerializer(serializers.ModelSerializer):
    class Meta:
	    model = Employee
	    fields = (
	        "id",
	        "name",
	        "title",
	        "department"
	    )

You can see we have kept the serializer simple for this example. We are simply telling the serializer to use Employee model and given the fields we will require.

Next we will create a view in views.py

from rest_framework import viewsets
from .serializers import EmployeeSerializer
from .models import Employee

class EmployeeViewSet(viewsets.ModelViewSet):
	serializer_class = EmployeeSerializer
	queryset = Employee.objects.all()

All we have done here is to provide the serializer and queryset. If you want to understand what is happening behind the scenes, you need to look into viewsets.ModelViewSet provided by rest_framework. If you will open this class you will find following code.

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):
    """
    A viewset that provides default `create()`, `retrieve()`, `update()`,
    `partial_update()`, `destroy()` and `list()` actions.
    """
    pass

Lets take a quick look inside one of the mixins (ofcourse you will never modify this code as this is provided by rest_framework. All we will do is to use it).

class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': data[api_settings.URL_FIELD_NAME]}
        except (TypeError, KeyError):
            return {}

You can see, the CreateModelMixin provide us functionality to create a model instance. All it need from our code is to fetch serializer (which we have provided) and it will take care of the rest.

Also if you look closely to mixins provided by ModelViewSet. We have all the mixins required by our REST actions

Post- Create
Get- List/ Retrieve
Put/ Patch – Update
Delete- Destroy

Further reading reference for view – http://www.django-rest-framework.org/api-guide/generic-views/
Once we have our view in place, we need to configure the final chunk, that is url mapping.

Create a urls.py in employees

from django.conf.urls import url
from .views import EmployeeViewSet

urlpatterns = [
	url(
		r'^employees/$',
			EmployeeViewSet.as_view({
			'get': 'list',
			'post': 'create',
		}),
		name='employees',
	),
	url(
		r'^employees/(?P<pk>[a-f0-9-]+)/$',
			EmployeeViewSet.as_view({
		 	'get': 'retrieve',
                        'put': 'partial_update',
                        'delete': 'destroy',		
		}),
		name='employee-details',
	),
]

All we have done here is to map REST urls to our ViewSet methods. You may recollect that we have not actually written implementation of any of the methods to handle actions as they are provided to us by rest_framework.

Note that we have used partia_update for the put action. This means end user need not send all the fields while updating the object. We could have used ‘update’ instead of ‘partial update’ if we always needed to update all fields in the object.

Lastly, we need to tell django where to look for urls. So in mysite/mysite/urls.py, we will add

url(r'^', include('employees.urls')),

in urlpatterns. So it might look like

urlpatterns = [
    url(r'^admin/', admin.site.urls),
    url(r'^', include('employees.urls')),
]

and import

from django.conf.urls import url
from django.conf.urls import include

We are done with coding now. Lets make things to work

Create migration files from models
(myproj) kamal@system:~/myproj/mysite$ python manage.py makemigrations
Create the actual database 
(myproj) kamal@system:~/myproj/mysite$ python manage.py migrate
Finally run the server 
(myproj) kamal@system:~/myproj/mysite$ python manage.py runserver 

Access the swagger in browser
http://localhost:8000/__docs__/