#django REST framework and API development

Course Objectives

  • Understand the core concepts of django REST Framework (DRF)
  • Master the definition and use of serializers
  • Learn to create and manage API endpoints
  • Understand API authentication and permission control

django REST Framework Overview

django REST Framework (DRF) is a mature Web API toolkit built on django. It can help you quickly implement RESTful-style interfaces, and it also has built-in a full set of tools needed to develop production-level APIs: serialization, browsable interface, authentication permissions, current limiting, content negotiation, and a complete document ecosystem.

Core advantage features:

  1. Powerful serialization system: seamlessly connects to Django ORM, supports nesting, custom fields, and data verification
  2. Friendly browsable web interface: debug the interface without Postman
  3. Out-of-the-box authentication permissions: Token, Session, JWT, and OAuth2 are optional, and customization is also supported.
  4. Flexible current limiting filtering: IP current limiting, API current limiting, djangoFilterBackend filtering
  5. Rich plug-in ecosystem: Extensions such as cache, document, paging, search, etc. are all available

Installation and configuration

Quick installation

You can install it directly with pip, and make sure it is compatible with your django version:

pip install djangorestframework

Basic settings.py configuration

Add DRF to INSTALLED_APPS and configure default authentication, permissions, renderer, etc.:

# settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',  # 必须的DRF核心
    'myapp',  # 你的业务应用
]

# DRF全局配置
REST_FRAMEWORK = {
    # 全局认证方式:优先Token,再Session(方便可浏览界面登录)
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ],
    # 全局权限:默认需要登录,部分接口可覆盖
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ],
    # 全局分页:PageNumberPagination(经典页码),每页20条
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 20,
    # 全局渲染器:JSON(生产)+ BrowsableAPI(开发调试)
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ],
}

Serializers

The serializer is the core conversion layer of DRF: responsible for converting django ORM objects → JSON/XML/HTML, or conversely converting request data submitted by the front end → Python objects/ORM model instances.

There is no need to manually write field mappings. It is automatically generated directly from the Django Model. You can also reuse the Model's field validation logic. You only need to customize additional fields or complex verification:

# serializers.py
from rest_framework import serializers
from .models import Article, Category

# 先写子序列化器,后面可以嵌套
class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ['id', 'name', 'slug', 'created_at']
        read_only_fields = ['slug', 'created_at']  # 这些字段只能后端生成,不能前端提交

class ArticleSerializer(serializers.ModelSerializer):
    # 自定义额外字段:从关联对象获取
    author_name = serializers.CharField(source='author.username', read_only=True)
    category_name = serializers.CharField(source='category.name', read_only=True)
    # SerializerMethodField:完全自定义获取逻辑
    tags_list = serializers.SerializerMethodField()
    
    class Meta:
        model = Article
        fields = [
            'id', 'title', 'content', 'summary', 'author', 'author_name',
            'category', 'category_name', 'status', 'tags', 'tags_list',
            'created_at', 'updated_at', 'view_count'
        ]
        read_only_fields = ['author', 'created_at', 'updated_at', 'view_count']
    
    # SerializerMethodField对应的get_xxx方法
    def get_tags_list(self, obj):
        """把标签对象列表转换为标签名列表"""
        return [tag.name for tag in obj.tags.all()]
    
    # 单个字段验证:validate_<field_name>
    def validate_title(self, value):
        """验证标题长度"""
        if len(value) < 5:
            raise serializers.ValidationError("标题至少需要5个字符")
        return value
    
    def validate_content(self, value):
        """验证内容长度"""
        if len(value) < 10:
            raise serializers.ValidationError("内容至少需要10个字符")
        return value
    
    # 重写create方法:处理多对多标签
    def create(self, validated_data):
        """自定义文章创建逻辑,因为标签是多对多不能直接create"""
        tags_data = validated_data.pop('tags', [])
        article = Article.objects.create(**validated_data)
        
        if tags_data:
            article.tags.set(tags_data)
        
        return article
    
    # 重写update方法:处理多对多标签
    def update(self, instance, validated_data):
        """自定义文章更新逻辑"""
        tags_data = validated_data.pop('tags', None)
        
        # 更新普通字段
        for attr, value in validated_data.items():
            setattr(instance, attr, value)
        instance.save()
        
        # 只有前端提交了tags才更新
        if tags_data is not None:
            instance.tags.set(tags_data)
        
        return instance

Nested serializer (dedicated to details page)

Details pages usually need to display more complete associated data, such as author information, category details, and reviewed comments. In this case, you can use a nested sub-serializer:

class ArticleDetailSerializer(serializers.ModelSerializer):
    # StringRelatedField:直接显示关联对象的__str__()
    author = serializers.StringRelatedField(read_only=True)
    tags = serializers.StringRelatedField(many=True, read_only=True)
    # 嵌套完整的子序列化器
    category = CategorySerializer(read_only=True)
    comments = serializers.SerializerMethodField()
    
    class Meta:
        model = Article
        fields = [
            'id', 'title', 'content', 'summary', 'author', 'category',
            'status', 'tags', 'comments', 'created_at', 'updated_at', 'view_count'
        ]
    
    def get_comments(self, obj):
        """只返回已审核的评论"""
        from myapp.serializers import CommentSerializer  # 避免循环导入
        comments = obj.comments.filter(is_approved=True)
        return CommentSerializer(comments, many=True).data

API view development

DRF provides 3 ways to write views, with flexible choices from simple to complex:

1. Function-based view (@api_view)

It is suitable for simple interfaces with non-repetitive logic and can be quickly implemented using decorators:

# views.py
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework import status
from rest_framework.permissions import IsAuthenticated, AllowAny
from .models import Article
from .serializers import ArticleSerializer, ArticleDetailSerializer

@api_view(['GET'])
@permission_classes([AllowAny])
def article_list(request):
    """获取已发布的文章列表(支持简单手动分页)"""
    articles = Article.objects.filter(status='published').order_by('-created_at')
    
    # 手动分页(推荐全局或视图级配置自动分页,这里仅演示)
    from rest_framework.pagination import PageNumberPagination
    paginator = PageNumberPagination()
    paginator.page_size = 10
    result_page = paginator.paginate_queryset(articles, request)
    
    serializer = ArticleSerializer(result_page, many=True)
    return paginator.get_paginated_response(serializer.data)

@api_view(['GET'])
@permission_classes([AllowAny])
def article_detail(request, pk):
    """获取文章详情(自动增加浏览量)"""
    try:
        article = Article.objects.get(pk=pk, status='published')
        article.view_count += 1
        article.save(update_fields=['view_count'])  # 只更新浏览量,性能更好
        
        serializer = ArticleDetailSerializer(article)
        return Response(serializer.data)
    except Article.DoesNotExist:
        return Response({'error': '文章不存在'}, status=status.HTTP_404_NOT_FOUND)

@api_view(['POST'])
@permission_classes([IsAuthenticated])
def create_article(request):
    """认证用户创建文章"""
    serializer = ArticleSerializer(data=request.data)
    if serializer.is_valid():
        serializer.save(author=request.user)  # 自动关联当前登录用户
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

2. Class-based generic views (generics)

Suitable for CRUD standard interface, reducing duplicate code, and can also cover methods in fine granularity:

from rest_framework import generics, permissions, status
from rest_framework.response import Response
from django.shortcuts import get_object_or_404

class ArticleListCreateView(generics.ListCreateAPIView):
    """文章列表+创建接口(通用视图自动处理分页、权限等)"""
    # 注意:get_queryset()方法会覆盖queryset属性,这里可以留空或写默认
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]  # 读允许所有人,写需要登录
    
    # 自定义查询逻辑:支持搜索、分类筛选、标签筛选
    def get_queryset(self):
        queryset = Article.objects.filter(status='published')
        
        search = self.request.query_params.get('search', '')
        if search:
            queryset = queryset.filter(title__icontains=search)
        
        category = self.request.query_params.get('category', '')
        if category:
            queryset = queryset.filter(category__name=category)
        
        tag = self.request.query_params.get('tag', '')
        if tag:
            queryset = queryset.filter(tags__name=tag)
        
        return queryset.order_by('-created_at')
    
    # 自定义创建逻辑:自动关联当前登录用户
    def perform_create(self, serializer):
        serializer.save(author=self.request.user)

class ArticleDetailView(generics.RetrieveUpdateDestroyAPIView):
    """文章详情+更新+删除接口(通用视图自动处理单个对象)"""
    queryset = Article.objects.all()
    # 读用详情序列化器,写用普通序列化器
    def get_serializer_class(self):
        if self.request.method == 'GET':
            return ArticleDetailSerializer
        return ArticleSerializer
    
    # 自定义权限:读允许所有人,写需要作者或管理员
    def get_permissions(self):
        if self.request.method in ['PUT', 'PATCH', 'DELETE']:
            return [permissions.IsAuthenticated()]
        return [permissions.AllowAny()]
    
    # 检查对象级权限
    def check_object_permissions(self, request, obj):
        super().check_object_permissions(request, obj)
        if request.method in ['PUT', 'PATCH', 'DELETE']:
            if obj.author != request.user and not request.user.is_staff:
                self.permission_denied(
                    request,
                    message='您没有权限执行此操作',
                    code='permission_denied'
                )

##ViewSets View set is the highest level package of DRF. It integrates all CRUD interfaces (list, create, retrieve, update, partial_update, destroy) into one class. It can also automatically generate URL routing, which is suitable for complex businesses that require customizing additional interfaces:

from rest_framework import viewsets, permissions, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django_filters.rest_framework import djangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from .models import Article
from .serializers import ArticleSerializer, ArticleDetailSerializer

class ArticleViewSet(viewsets.ModelViewSet):
    """文章视图集(支持所有CRUD+自定义接口+自动路由)"""
    queryset = Article.objects.all()
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]
    # 内置过滤、搜索、排序
    filter_backends = [djangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['status', 'category', 'author']
    search_fields = ['title', 'content', 'tags__name']
    ordering_fields = ['created_at', 'view_count', 'like_count']
    ordering = ['-created_at']
    
    # 动态选择序列化器
    def get_serializer_class(self):
        if self.action == 'retrieve':
            return ArticleDetailSerializer
        return ArticleSerializer
    
    # 自定义查询逻辑:普通用户只能看已发布,管理员看所有
    def get_queryset(self):
        queryset = super().get_queryset()
        if not self.request.user.is_staff:
            queryset = queryset.filter(status='published')
        return queryset
    
    # 自定义额外接口:detail=True表示针对单个对象,url会变成/articles/{pk}/like/
    @action(detail=True, methods=['post'], permission_classes=[permissions.IsAuthenticated])
    def like(self, request, pk=None):
        """点赞文章"""
        article = self.get_object()
        article.like_count += 1
        article.save(update_fields=['like_count'])
        return Response({'status': 'liked', 'likes': article.like_count})
    
    @action(detail=True, methods=['post'], permission_classes=[permissions.IsAuthenticated])
    def unlike(self, request, pk=None):
        """取消点赞"""
        article = self.get_object()
        if article.like_count > 0:
            article.like_count -= 1
            article.save(update_fields=['like_count'])
        return Response({'status': 'unliked', 'likes': article.like_count})
    
    # 自定义额外接口:detail=False表示针对整个集合,url会变成/articles/my_articles/
    @action(detail=False, methods=['get'])
    def my_articles(self, request):
        """获取当前用户的全部文章"""
        if not request.user.is_authenticated:
            return Response({'error': '请先登录'}, status=status.HTTP_401_UNAUTHORIZED)
        articles = self.queryset.filter(author=request.user)
        serializer = self.get_serializer(articles, many=True)
        return Response(serializer.data)

Automatically generate URL routing

Use DefaultRouter to automatically generate all URLs for the viewset, including custom interfaces:

# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import ArticleViewSet

router = DefaultRouter()
router.register(r'articles', ArticleViewSet, basename='article')

urlpatterns = [
    path('', include(router.urls)),
]

Course Summary

In this lesson, we systematically learned the core capabilities of django REST Framework:

  1. Serializer: As the core conversion layer, it handles the serialization, deserialization and verification of data.
  2. View development: three view methods (function → general class → view set) to flexibly cope with interfaces of different complexity
  3. Authentication permissions: supports global/view-level configuration, and can also customize object-level permissions
  4. Filtering, paging and sorting: built-in tools to quickly implement common functions
  5. Automatically generate documents and routing: Improve development efficiency

Mastering DRF, you can quickly build a production-level Web API that complies with RESTful specifications. It is an essential skill for developing front-end and back-end separation applications!