Skip to content

第14章:API开发

14.1 Django REST Framework入门

DRF安装配置

Django REST Framework (DRF) 是一个强大而灵活的工具包,用于构建Web API。

安装和基本配置:

bash
# 安装DRF
pip install djangorestframework

# 可选:安装API文档工具
pip install coreapi
pip install pyyaml
python
# settings.py
INSTALLED_APPS = [
    # ...
    'rest_framework',
    'rest_framework.authtoken',  # 如果使用Token认证
]

# DRF配置
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.TokenAuthentication',
    ],
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticatedOrReadOnly',
    ],
    'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 20,
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ],
    'DEFAULT_FILTER_BACKENDS': [
        'django_filters.rest_framework.DjangoFilterBackend',
        'rest_framework.filters.SearchFilter',
        'rest_framework.filters.OrderingFilter',
    ],
}

# 如果使用Token认证,需要运行迁移
# python manage.py migrate

序列化器

序列化器用于在复杂数据类型(如查询集和模型实例)和Python原生数据类型之间进行转换:

python
# serializers.py
from rest_framework import serializers
from .models import Article, Category, Tag, Comment

class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = Category
        fields = ['id', 'name', 'slug', 'description']

class TagSerializer(serializers.ModelSerializer):
    class Meta:
        model = Tag
        fields = ['id', 'name', 'slug']

class CommentSerializer(serializers.ModelSerializer):
    author = serializers.StringRelatedField(read_only=True)
    
    class Meta:
        model = Comment
        fields = ['id', 'author', 'content', 'created_at']
        read_only_fields = ['author', 'created_at']

class ArticleSerializer(serializers.ModelSerializer):
    author = serializers.StringRelatedField(read_only=True)
    category = CategorySerializer(read_only=True)
    tags = TagSerializer(many=True, read_only=True)
    comments = CommentSerializer(many=True, read_only=True)
    comment_count = serializers.SerializerMethodField()
    
    class Meta:
        model = Article
        fields = [
            'id', 'title', 'slug', 'author', 'category', 'tags',
            'content', 'excerpt', 'featured_image', 'status',
            'view_count', 'created_at', 'updated_at', 'published_at',
            'comments', 'comment_count'
        ]
        read_only_fields = ['author', 'view_count', 'created_at', 'updated_at', 'published_at']
    
    def get_comment_count(self, obj):
        return obj.comments.filter(is_approved=True).count()

# 嵌套序列化器示例
class ArticleListSerializer(serializers.ModelSerializer):
    """用于列表展示的简化序列化器"""
    author = serializers.StringRelatedField()
    category = serializers.StringRelatedField()
    
    class Meta:
        model = Article
        fields = [
            'id', 'title', 'slug', 'author', 'category',
            'excerpt', 'featured_image', 'view_count',
            'created_at', 'published_at'
        ]

# 自定义序列化器字段
class CustomArticleSerializer(serializers.ModelSerializer):
    days_since_published = serializers.SerializerMethodField()
    is_popular = serializers.SerializerMethodField()
    
    class Meta:
        model = Article
        fields = '__all__'
        read_only_fields = ['author']
    
    def get_days_since_published(self, obj):
        if obj.published_at:
            from django.utils import timezone
            return (timezone.now() - obj.published_at).days
        return None
    
    def get_is_popular(self, obj):
        return obj.view_count > 1000

API视图

DRF提供了多种方式来创建API视图:

python
# views.py
from rest_framework import generics, viewsets, status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from django_filters.rest_framework import DjangoFilterBackend
from rest_framework.filters import SearchFilter, OrderingFilter
from .models import Article, Category, Tag
from .serializers import ArticleSerializer, ArticleListSerializer, CategorySerializer

# 基于函数的视图
@api_view(['GET', 'POST'])
@permission_classes([IsAuthenticatedOrReadOnly])
def article_list(request):
    if request.method == 'GET':
        articles = Article.objects.filter(status='published')
        serializer = ArticleListSerializer(articles, many=True)
        return Response(serializer.data)
    
    elif request.method == 'POST':
        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)

# 基于类的视图
class ArticleListView(generics.ListCreateAPIView):
    queryset = Article.objects.filter(status='published')
    serializer_class = ArticleListSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['category', 'tags', 'author']
    search_fields = ['title', 'content', 'excerpt']
    ordering_fields = ['created_at', 'view_count', 'published_at']
    ordering = ['-created_at']

class ArticleDetailView(generics.RetrieveUpdateDestroyAPIView):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    lookup_field = 'slug'
    
    def perform_update(self, serializer):
        # 更新时检查权限
        if self.request.user == serializer.instance.author:
            serializer.save()
        else:
            raise PermissionDenied("您没有权限编辑此文章")

# ViewSet视图集
class ArticleViewSet(viewsets.ModelViewSet):
    queryset = Article.objects.all()
    serializer_class = ArticleSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    filter_backends = [DjangoFilterBackend, SearchFilter, OrderingFilter]
    filterset_fields = ['category', 'tags', 'author', 'status']
    search_fields = ['title', 'content', 'excerpt']
    ordering_fields = ['created_at', 'view_count', 'published_at']
    ordering = ['-created_at']
    lookup_field = 'slug'
    
    def get_queryset(self):
        if self.action == 'list':
            return Article.objects.filter(status='published')
        return Article.objects.all()
    
    def get_serializer_class(self):
        if self.action == 'list':
            return ArticleListSerializer
        return ArticleSerializer
    
    def perform_create(self, serializer):
        serializer.save(author=self.request.user)
    
    def perform_update(self, serializer):
        if self.request.user == serializer.instance.author or self.request.user.is_staff:
            serializer.save()
        else:
            raise PermissionDenied("您没有权限编辑此文章")

# 自定义API视图
class ArticleStatsView(generics.GenericAPIView):
    queryset = Article.objects.all()
    
    def get(self, request, *args, **kwargs):
        stats = {
            'total_articles': Article.objects.count(),
            'published_articles': Article.objects.filter(status='published').count(),
            'total_views': Article.objects.aggregate(
                total_views=models.Sum('view_count')
            )['total_views'] or 0,
            'most_popular_article': Article.objects.order_by('-view_count').first().title if Article.objects.exists() else None
        }
        return Response(stats)

URL路由

配置API路由:

python
# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from rest_framework.authtoken.views import obtain_auth_token
from . import views

# 使用Router自动注册ViewSet
router = DefaultRouter()
router.register(r'articles', views.ArticleViewSet)
router.register(r'categories', views.CategoryViewSet)
router.register(r'tags', views.TagViewSet)

urlpatterns = [
    # 函数视图
    path('articles/', views.article_list, name='article-list'),
    path('articles/<slug:slug>/', views.article_detail, name='article-detail'),
    
    # 类视图
    path('api/articles/', views.ArticleListView.as_view(), name='api-article-list'),
    path('api/articles/<slug:slug>/', views.ArticleDetailView.as_view(), name='api-article-detail'),
    
    # ViewSet路由
    path('api/v2/', include(router.urls)),
    
    # 统计信息
    path('api/stats/', views.ArticleStatsView.as_view(), name='api-stats'),
    
    # 认证
    path('api/auth/token/', obtain_auth_token, name='api-token-auth'),
    
    # API根路径
    path('api/', views.api_root, name='api-root'),
]

# 自定义API根视图
from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse

@api_view(['GET'])
def api_root(request, format=None):
    return Response({
        'articles': reverse('api-article-list', request=request, format=format),
        'categories': reverse('category-list', request=request, format=format),
        'tags': reverse('tag-list', request=request, format=format),
        'stats': reverse('api-stats', request=request, format=format),
    })

通过这些配置,你就拥有了一个功能完整的REST API,支持认证、权限控制、过滤、搜索和分页等功能。

小结

Django REST Framework入门的关键知识点:

  1. ✅ 掌握DRF的安装和基本配置
  2. ✅ 理解序列化器的作用和使用方法
  3. ✅ 学会使用不同类型的API视图(函数视图、类视图、ViewSet)
  4. ✅ 配置API路由和URL模式
  5. ✅ 实现认证和权限控制

DRF为Django提供了强大的API开发能力。

下一篇

我们将深入学习RESTful API设计原则。

14.2 RESTful API设计 →

目录

返回课程目录

Released under the Apache 2.0 License.