Building a Blog API with Django REST Framework
Unlocking the Power of APIs
APIs (Application Programming Interfaces) enable applications to communicate with each other seamlessly, exchanging data in a standardized format. In this tutorial, we’ll explore how to build a blog API using Django REST Framework, a powerful toolkit for constructing RESTful APIs with Django.
Getting Started
To begin, ensure you have Python 3 installed on your system, along with experience interacting with REST APIs. Familiarity with relational databases, including primary and foreign keys, database models, migrations, and many-to-one and many-to-many relationships, is also essential.
Setting Up the Environment
Create a new API project by setting up a Python environment in your working directory. Install Django and Django REST Framework into the virtual environment. Then, create a new project called blog
and an app called api
.
python -m venv env
source env/bin/activate
pip install django djangorestframework
django-admin startproject blog
cd blog
python manage.py startapp api
Configuring the API
Add rest_framework
and your api
app to blog/blog/settings.py
. This allows you to add other configuration options to your app. Finally, start the local development server.
INSTALLED_APPS = [
#...
'est_framework',
'api',
]
#...
python manage.py runserver
Creating the User API
Set up a user API, which will allow read-only access to the list of users and to single users from a set of API endpoints. Create a UserSerializer
to translate querysets and model instances into JSON data. Define the UserList
and UserDetail
views, which provide read-only access to the list of users and a single user, respectively. Set up the endpoint paths for these views using Django’s URL patterns.
from rest_framework import serializers
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username', 'email']
from rest_framework.response import Response
from rest_framework.views import APIView
class UserList(APIView):
def get(self, request):
users = User.objects.all()
serializer = UserSerializer(users, many=True)
return Response(serializer.data)
class UserDetail(APIView):
def get(self, request, pk):
user = User.objects.get(pk=pk)
serializer = UserSerializer(user)
return Response(serializer.data)
from django.urls import path
urlpatterns = [
path('users/', UserList.as_view()),
path('users//', UserDetail.as_view()),
]
Creating the Post API
With the user API set up, create a complete API for a blog, with endpoints for posts, comments, and categories. Define a Post
model, which inherits from Django’s Model
class, and create a PostSerializer
to serialize the Post
model data. Add a posts
field to the UserSerializer
to complete the many-to-one relationship between posts and users.
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author']
class UserSerializer(serializers.ModelSerializer):
posts = PostSerializer(many=True, read_only=True)
class Meta:
model = User
fields = ['id', 'username', 'email', 'posts']
Creating the Comment API
Add a comment system to your posts. Define a Comment
model, which has many-to-one relationships with users and posts. Create a CommentSerializer
to serialize the Comment
model data. Add a comments
field to the PostSerializer
and UserSerializer
to complete the many-to-one relationships between comments and posts and between comments and users.
class Comment(models.Model):
content = models.TextField()
post = models.ForeignKey(Post, on_delete=models.CASCADE)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class CommentSerializer(serializers.ModelSerializer):
class Meta:
model = Comment
fields = ['id', 'content', 'post', 'author']
class PostSerializer(serializers.ModelSerializer):
comments = CommentSerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author', 'comments']
class UserSerializer(serializers.ModelSerializer):
posts = PostSerializer(many=True, read_only=True)
comments = CommentSerializer(many=True, read_only=True)
class Meta:
model = User
fields = ['id', 'username', 'email', 'posts', 'comments']
Creating the Category API
Finally, create a category system, which allows one or more categories to be added to any post. Define a Category
model, which has a many-to-many relationship with posts. Create a CategorySerializer
to serialize the Category
model data. Add a categories
field to the PostSerializer
to complete the many-to-many relationship between categories and posts.
class Category(models.Model):
name = models.CharField(max_length=255)
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ['id', 'name']
class Post(models.Model):
#...
categories = models.ManyToManyField(Category)
class PostSerializer(serializers.ModelSerializer):
categories = CategorySerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author', 'comments', 'categories']
Authentication and Permissions
To ensure that only authenticated users can modify your app’s data, add permissions to your API. Create a custom IsOwnerOrReadOnly
permission to check whether the requesting user is the owner of the given object. Add these permissions to the Post
views and Comment
views.
from rest_framework.permissions import BasePermission
class IsOwnerOrReadOnly(BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user
class PostList(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [IsOwnerOrReadOnly]
class PostDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [IsOwnerOrReadOnly]
class CommentList(generics.ListCreateAPIView):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
permission_classes = [IsOwnerOrReadOnly]
class CommentDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = Comment.objects.all()
serializer_class = CommentSerializer
permission_classes = [IsOwnerOrReadOnly]