DRF 类视图 #

一、类视图概述 #

1.1 什么是类视图 #

类视图使用Python类来组织视图代码,通过方法名对应HTTP方法,实现更好的代码组织和复用。

text
函数视图 vs 类视图
┌─────────────────────────────────────┐
│           函数视图                   │
│  - 使用if判断HTTP方法               │
│  - 代码集中在一个函数               │
│  - 难以复用                         │
└─────────────────────────────────────┘
┌─────────────────────────────────────┐
│           类视图                     │
│  - 方法名对应HTTP方法               │
│  - 代码按方法组织                   │
│  - 易于继承和复用                   │
└─────────────────────────────────────┘

1.2 基本语法 #

python
from rest_framework.views import APIView
from rest_framework.response import Response

class HelloView(APIView):
    def get(self, request):
        return Response({'message': 'Hello, World!'})
    
    def post(self, request):
        return Response({'message': 'POST request'})

二、APIView基础 #

2.1 HTTP方法映射 #

python
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status

class ArticleView(APIView):
    def get(self, request):
        return Response({'method': 'GET'})
    
    def post(self, request):
        return Response({'method': 'POST'}, status=status.HTTP_201_CREATED)
    
    def put(self, request):
        return Response({'method': 'PUT'})
    
    def patch(self, request):
        return Response({'method': 'PATCH'})
    
    def delete(self, request):
        return Response(status=status.HTTP_204_NO_CONTENT)

2.2 URL配置 #

python
from django.urls import path
from .views import ArticleView

urlpatterns = [
    path('articles/', ArticleView.as_view()),
]

2.3 带参数的视图 #

python
class ArticleDetailView(APIView):
    def get(self, request, pk):
        article = get_object_or_404(Article, pk=pk)
        serializer = ArticleSerializer(article)
        return Response(serializer.data)

urlpatterns = [
    path('articles/<int:pk>/', ArticleDetailView.as_view()),
]

三、完整CRUD示例 #

3.1 列表视图 #

python
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from django.shortcuts import get_object_or_404
from .models import Article
from .serializers import ArticleSerializer

class ArticleListView(APIView):
    def get(self, request):
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)
    
    def post(self, request):
        serializer = ArticleSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

3.2 详情视图 #

python
class ArticleDetailView(APIView):
    def get_object(self, pk):
        return get_object_or_404(Article, pk=pk)
    
    def get(self, request, pk):
        article = self.get_object(pk)
        serializer = ArticleSerializer(article)
        return Response(serializer.data)
    
    def put(self, request, pk):
        article = self.get_object(pk)
        serializer = ArticleSerializer(article, data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    def patch(self, request, pk):
        article = self.get_object(pk)
        serializer = ArticleSerializer(article, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    def delete(self, request, pk):
        article = self.get_object(pk)
        article.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)

四、类属性和方法 #

4.1 类属性 #

python
class ArticleListView(APIView):
    serializer_class = ArticleSerializer
    permission_classes = [IsAuthenticated]
    authentication_classes = [TokenAuthentication]
    
    def get_serializer(self, *args, **kwargs):
        return self.serializer_class(*args, **kwargs)
    
    def get(self, request):
        articles = Article.objects.all()
        serializer = self.get_serializer(articles, many=True)
        return Response(serializer.data)

4.2 重写方法 #

python
class ArticleListView(APIView):
    def initial(self, request, *args, **kwargs):
        super().initial(request, *args, **kwargs)
        print('请求开始处理')
    
    def finalize_response(self, request, response, *args, **kwargs):
        response['X-Custom-Header'] = 'value'
        return super().finalize_response(request, response, *args, **kwargs)
    
    def handle_exception(self, exc):
        response = super().handle_exception(exc)
        response.data['error_code'] = 'CUSTOM_ERROR'
        return response
    
    def get(self, request):
        articles = Article.objects.all()
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)

4.3 生命周期方法 #

python
class ArticleView(APIView):
    def initial(self, request, *args, **kwargs):
        print('1. initial - 请求预处理')
        super().initial(request, *args, **kwargs)
    
    def get(self, request):
        print('2. get - 处理GET请求')
        return Response({'message': 'ok'})
    
    def finalize_response(self, request, response, *args, **kwargs):
        print('3. finalize_response - 响应处理')
        return super().finalize_response(request, response, *args, **kwargs)
    
    def handle_exception(self, exc):
        print('4. handle_exception - 异常处理')
        return super().handle_exception(exc)

五、认证和权限 #

5.1 类级别设置 #

python
from rest_framework.permissions import IsAuthenticated
from rest_framework.authentication import TokenAuthentication

class ArticleView(APIView):
    authentication_classes = [TokenAuthentication]
    permission_classes = [IsAuthenticated]
    
    def get(self, request):
        return Response({'user': request.user.username})

5.2 方法级别设置 #

python
from rest_framework.decorators import permission_classes
from rest_framework.permissions import IsAdminUser

class ArticleView(APIView):
    def get(self, request):
        return Response({'message': '公开访问'})
    
    @permission_classes([IsAdminUser])
    def post(self, request):
        return Response({'message': '管理员专属'})

5.3 自定义权限检查 #

python
class ArticleDetailView(APIView):
    def check_object_permission(self, request, obj):
        if request.method in ['PUT', 'PATCH', 'DELETE']:
            if obj.author != request.user:
                self.permission_denied(request)
        return True
    
    def get(self, request, pk):
        article = get_object_or_404(Article, pk=pk)
        self.check_object_permission(request, article)
        serializer = ArticleSerializer(article)
        return Response(serializer.data)

六、分页实现 #

6.1 手动分页 #

python
from rest_framework.pagination import PageNumberPagination

class ArticleListView(APIView):
    def get(self, request):
        articles = Article.objects.all()
        
        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)

6.2 自定义分页 #

python
class CustomPagination(PageNumberPagination):
    page_size = 20
    page_size_query_param = 'page_size'
    max_page_size = 100

class ArticleListView(APIView):
    def get(self, request):
        articles = Article.objects.all()
        
        paginator = CustomPagination()
        result_page = paginator.paginate_queryset(articles, request)
        
        serializer = ArticleSerializer(result_page, many=True)
        return paginator.get_paginated_response(serializer.data)

七、过滤和搜索 #

7.1 查询参数过滤 #

python
class ArticleListView(APIView):
    def get_queryset(self):
        queryset = Article.objects.all()
        
        category = self.request.query_params.get('category')
        if category:
            queryset = queryset.filter(category_id=category)
        
        author = self.request.query_params.get('author')
        if author:
            queryset = queryset.filter(author__username=author)
        
        is_published = self.request.query_params.get('is_published')
        if is_published:
            queryset = queryset.filter(is_published=is_published == 'true')
        
        return queryset
    
    def get(self, request):
        articles = self.get_queryset()
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)

7.2 搜索功能 #

python
from django.db.models import Q

class ArticleSearchView(APIView):
    def get(self, request):
        keyword = request.query_params.get('keyword', '')
        
        articles = Article.objects.all()
        
        if keyword:
            articles = articles.filter(
                Q(title__icontains=keyword) |
                Q(content__icontains=keyword) |
                Q(author__username__icontains=keyword)
            )
        
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)

7.3 排序 #

python
class ArticleListView(APIView):
    def get(self, request):
        articles = Article.objects.all()
        
        ordering = request.query_params.get('ordering', '-created_at')
        if ordering:
            articles = articles.order_by(ordering)
        
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)

八、代码复用 #

8.1 基类视图 #

python
class BaseAPIView(APIView):
    serializer_class = None
    model_class = None
    
    def get_serializer(self, *args, **kwargs):
        return self.serializer_class(*args, **kwargs)
    
    def get_queryset(self):
        return self.model_class.objects.all()
    
    def get_object(self, pk):
        return get_object_or_404(self.model_class, pk=pk)

class ArticleListView(BaseAPIView):
    serializer_class = ArticleSerializer
    model_class = Article
    
    def get(self, request):
        articles = self.get_queryset()
        serializer = self.get_serializer(articles, many=True)
        return Response(serializer.data)

8.2 Mixin类 #

python
class SerializerMixin:
    serializer_class = None
    
    def get_serializer(self, *args, **kwargs):
        return self.serializer_class(*args, **kwargs)

class PermissionMixin:
    permission_classes = [IsAuthenticated]
    
    def check_permissions(self, request):
        for permission in self.permission_classes:
            if not permission().has_permission(request, self):
                self.permission_denied(request)

class ArticleListView(SerializerMixin, PermissionMixin, APIView):
    serializer_class = ArticleSerializer
    
    def get(self, request):
        articles = Article.objects.all()
        serializer = self.get_serializer(articles, many=True)
        return Response(serializer.data)

8.3 组合视图 #

python
class CacheMixin:
    cache_timeout = 60 * 5
    
    def get_cached_data(self, key, queryset):
        from django.core.cache import cache
        data = cache.get(key)
        if data is None:
            data = list(queryset.values())
            cache.set(key, data, self.cache_timeout)
        return data

class ArticleListView(CacheMixin, APIView):
    def get(self, request):
        articles = self.get_cached_data('articles_all', Article.objects.all())
        serializer = ArticleSerializer(articles, many=True)
        return Response(serializer.data)

九、异常处理 #

9.1 统一异常处理 #

python
from rest_framework.exceptions import APIException

class ArticleView(APIView):
    def handle_exception(self, exc):
        if isinstance(exc, Article.DoesNotExist):
            return Response(
                {'error': '文章不存在'},
                status=status.HTTP_404_NOT_FOUND
            )
        
        if isinstance(exc, PermissionDenied):
            return Response(
                {'error': '没有权限'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        return super().handle_exception(exc)
    
    def get(self, request, pk):
        article = Article.objects.get(pk=pk)
        serializer = ArticleSerializer(article)
        return Response(serializer.data)

9.2 自定义异常 #

python
class ArticleNotFound(APIException):
    status_code = 404
    default_detail = '文章不存在'
    default_code = 'article_not_found'

class ArticleView(APIView):
    def get(self, request, pk):
        try:
            article = Article.objects.get(pk=pk)
        except Article.DoesNotExist:
            raise ArticleNotFound()
        
        serializer = ArticleSerializer(article)
        return Response(serializer.data)

十、实用示例 #

10.1 用户资料视图 #

python
class UserProfileView(APIView):
    permission_classes = [IsAuthenticated]
    
    def get(self, request):
        serializer = UserSerializer(request.user)
        return Response(serializer.data)
    
    def put(self, request):
        serializer = UserSerializer(request.user, data=request.data, partial=True)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

10.2 统计视图 #

python
class ArticleStatsView(APIView):
    def get(self, request, pk):
        article = get_object_or_404(Article, pk=pk)
        
        stats = {
            'views': article.views,
            'likes': article.likes.count(),
            'comments': article.comments.count(),
            'shares': article.shares.count()
        }
        
        return Response(stats)

10.3 批量操作视图 #

python
class ArticleBatchView(APIView):
    permission_classes = [IsAdminUser]
    
    def post(self, request):
        action = request.data.get('action')
        ids = request.data.get('ids', [])
        
        articles = Article.objects.filter(id__in=ids)
        
        if action == 'publish':
            articles.update(is_published=True)
            return Response({'message': f'已发布{articles.count()}篇文章'})
        
        elif action == 'unpublish':
            articles.update(is_published=False)
            return Response({'message': f'已取消发布{articles.count()}篇文章'})
        
        elif action == 'delete':
            count = articles.count()
            articles.delete()
            return Response({'message': f'已删除{count}篇文章'})
        
        return Response(
            {'error': '无效的操作'},
            status=status.HTTP_400_BAD_REQUEST
        )

十一、类视图最佳实践 #

11.1 视图组织 #

python
class ArticleListView(APIView):
    serializer_class = ArticleSerializer
    
    def get(self, request):
        articles = self.get_queryset()
        serializer = self.get_serializer(articles, many=True)
        return Response(serializer.data)
    
    def post(self, request):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data, status=status.HTTP_201_CREATED)
    
    def get_queryset(self):
        return Article.objects.all()
    
    def get_serializer(self, *args, **kwargs):
        return self.serializer_class(*args, **kwargs)

11.2 方法拆分 #

python
class ArticleDetailView(APIView):
    def get(self, request, pk):
        article = self.get_object(pk)
        self.check_permissions(request, article)
        serializer = self.get_serializer(article)
        return Response(serializer.data)
    
    def put(self, request, pk):
        article = self.get_object(pk)
        self.check_permissions(request, article)
        serializer = self.get_serializer(article, data=request.data)
        serializer.is_valid(raise_exception=True)
        serializer.save()
        return Response(serializer.data)
    
    def get_object(self, pk):
        return get_object_or_404(Article, pk=pk)
    
    def check_permissions(self, request, obj):
        if obj.author != request.user:
            self.permission_denied(request)
    
    def get_serializer(self, *args, **kwargs):
        return ArticleSerializer(*args, **kwargs)

十二、总结 #

本章学习了DRF类视图:

  • APIView基础:HTTP方法映射
  • 类属性和方法:配置和生命周期
  • 认证和权限:类级别和方法级别设置
  • 分页过滤:实现分页和搜索
  • 代码复用:基类和Mixin模式

类视图提供了更好的代码组织方式,让我们继续学习通用视图!

最后更新:2026-03-28