Django REST Framework basics
Django REST Framework is a framework for creating human and computer readable Django views for data - very often representations of Django models - as Web APIs. Broadly, it aims to help implement RESTful API endpoints, but it can be used to implement a wide range of different kinds of API endpoints.
To get started, install Django REST Framework into your project.
Django REST Framework installation
In your django-tutorial/requirements.txt
file you should add the following text:
django-tutorial/requirements.txt
djangorestframework==3.13.1
Now, run pip install -r requirements.txt
to install Django REST Framework.
command-line
(myvenv) ~$ pip install -r requirements.txt
Collecting djangorestframework==3.13.1 (from -r requirements.txt (line 1))
Downloading djangorestframework-3.13.1-py3-none-any.whl (7.9MB)
Installing collected packages: djangorestframework
Successfully installed djangorestframework-3.13.1
In this tutorial we are using Django REST Framework version 3.13.1 - however, the last Django REST Framework version to support Python 2.7 was 3.9.1, this means that in Kolibri there may be some subtle differences. The tutorial will point out when these might arise.
We have seen how to extend Django using our own blog
app - but we can also extend Django using installable libraries like Django REST Framework. To tell Django that we are using Django REST Framework in our project, we need to add the rest_framework
app to our INSTALLED_APPS
setting.
We do that in the file mysite/settings.py
-- open it in your code editor. We need to find INSTALLED_APPS
and add a line containing 'rest_framework',
just above ]
. So the final product should look like this:
mysite/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog',
'rest_framework',
]
Building an API for blog posts
We've been displaying our posts in web pages, but if we want to make the data available to a Javascript driven frontend that dynamically loads the data, or as a public web API for other programs to access and pull posts from, we need to build an API that serves up the data from the post models.
To do this, we can create a web API to return either lists of blog posts, or a single blog post at a time. In order to manage the two different views to provide this kind of response, Django REST Framework introduces a concept called a ViewSet
- a Python class that then exposes multiple Django views, in the case we are concerned about, one to handle GET requests for a list of blog posts, and one to handle GET requests for a single blog post.
Let's re-open up this file in our code editor to add a ViewSet
:
blog/views.py
from django.shortcuts import render
from django.utils import timezone
from rest_framework import permissions
from rest_framework import viewsets
from .models import Post
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html', {'posts': posts})
class PostViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows posts to be viewed or edited.
"""
queryset = Post.objects.all().order_by('published_date')
permission_classes = [permissions.IsAuthenticated]
Let's look at the properties of the PostViewSet
class. Firstly, it inherits from the ModelViewSet
class which is setup to handle Django Models specifically. We tell it which model to use by setting the queryset
property, which we have set to all of the Post
objects, ordered by the published date.
The permission_classes
property, tells Django REST Framework who is allowed to access this web API. permissions.isAuthenticated
is a standard Django REST Framework permission class that requires that the user be logged in - there are others that require the user to be an administrator, or you can create custom permission classes that do object level permission checks to make sure that you have permission to access the specific data.
The only thing we haven't defined is what data from the Post
objects we should be returning in the endpoint. To do this, Django REST Framework uses Serializer
classes to define which data from objects are returned from the API. We can add a definition for a serializer into the file:
blog/views.py
from django.shortcuts import render
from django.utils import timezone
from rest_framework import permissions
from rest_framework import serializers
from rest_framework import viewsets
from .models import Post
def post_list(request):
posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
return render(request, 'blog/post_list.html', {'posts': posts})
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'author', 'title', 'text', 'created_date', 'published_date']
class PostViewSet(viewsets.ModelViewSet):
"""
API endpoint that allows posts to be viewed or edited.
"""
queryset = Post.objects.all().order_by('published_date')
permission_classes = [permissions.IsAuthenticated]
serializer_class = PostSerializer
Here we are using the Django REST Framework ModelSerializer
which takes advantage of the fact that it is serializing a Django model to make some inferences about what data is being serialized. Because of this, we are able to use configuration with the Meta
class, to define which Django model we are serializing, and which fields we should serialize. Note that we have included the id
field, which we did not explicitly specify on the Django model, but which Django adds for us automatically. Django REST Framework will inspect the Post
model and use a sensible default for serializing the data for each field.
Now we have a complete ViewSet
with a Serializer
- but we are still not able to access this. To do that we have to map URLs to the views in the ViewSet
much like we did for the Django views we defined earlier. Reopen the blogs/urls.py
file and create a Django REST Framework Router
to register the ViewSet
against:
blog/urls.py
from django.urls import include
from django.urls import path
from rest_framework import routers
from . import views
router = routers.DefaultRouter()
router.register(r'post', views.PostViewSet, basename="post-api")
urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
path('api/', include(router.urls)),
]
This will mount URLs for the PostViewSet
under api/post
for retrieving a list of posts and api/post/<pk>
for retrieving individual posts. Run the server and go to http://127.0.0.1:8000/api/post to view the browsable Django REST Framework API showing the posts.