Django REST框架与API开发

课程目标

  • 理解Django REST Framework (DRF) 的核心概念
  • 掌握序列化器的定义和使用
  • 学会创建和管理API端点
  • 了解API认证和权限控制

Django REST Framework概述

Django REST Framework (DRF) 是一个基于Django构建的、成熟的Web API工具包。它可以帮你快速实现RESTful风格的接口,同时内置了开发生产级API所需的全套工具:序列化、可浏览界面、认证权限、限流、内容协商,以及完善的文档生态。

核心优势特性:

  1. 强大的序列化系统:无缝对接Django ORM,支持嵌套、自定义字段、数据校验
  2. 友好的可浏览Web界面:无需Postman即可调试接口
  3. 开箱即用的认证权限:Token、Session、JWT、OAuth2可选,还支持自定义
  4. 灵活的限流过滤:IP限流、API限流、DjangoFilterBackend过滤
  5. 丰富的插件生态:缓存、文档、分页、搜索等扩展应有尽有

安装和配置

快速安装

用pip直接安装即可,同时注意与你的Django版本兼容:

pip install djangorestframework

基础settings.py配置

将DRF加入INSTALLED_APPS,并配置默认的认证、权限、渲染器等:

# 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)

序列化器是DRF的核心转换层:负责把Django ORM对象→JSON/XML/HTML,或者反过来把前端提交的请求数据→Python对象/ORM模型实例。

ModelSerializer(推荐首选)

不用手动写字段映射,直接从Django Model自动生成,还能复用Model的字段验证逻辑,只需要自定义额外字段或复杂校验即可:

# 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

嵌套序列化器(详情页专用)

详情页通常需要显示更完整的关联数据,比如作者信息、分类详情、已审核的评论,这时候可以用嵌套的子序列化器:

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视图开发

DRF提供了3种视图编写方式,从简单到复杂灵活选择:

1. 基于函数的视图(@api_view)

适合简单的、逻辑不重复的接口,用装饰器快速实现:

# 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. 基于类的通用视图(generics)

适合CRUD标准接口,减少重复代码,还能细粒度覆盖方法:

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)

视图集是DRF的最高级封装,把CRUD的所有接口(list、create、retrieve、update、partial_update、destroy)整合到一个类里,还能自动生成URL路由,适合复杂的、需要自定义额外接口的业务:

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)

自动生成URL路由

用DefaultRouter自动生成视图集的所有URL,包括自定义接口:

# 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)),
]

课程总结

本节课我们系统学习了Django REST Framework的核心能力:

  1. 序列化器:作为核心转换层,处理数据的序列化、反序列化、校验
  2. 视图开发:三种视图方式(函数→通用类→视图集)灵活应对不同复杂度的接口
  3. 认证权限:支持全局/视图级配置,还能自定义对象级权限
  4. 过滤分页排序:内置工具快速实现常用功能
  5. 自动生成文档和路由:提升开发效率

掌握了DRF,你就能快速构建出符合RESTful规范的生产级Web API,是开发前后端分离应用的必备技能!