杰瑞科技汇

python viewsets接口

什么是 ViewSet?

我们来看一下传统的 ViewViewSet 的区别。

python viewsets接口-图1
(图片来源网络,侵删)

传统 View (基于函数或类)

在标准的 Django 或 DRF 中,一个资源(User)通常需要多个端点来处理不同的 HTTP 方法。

# urls.py
from django.urls import path
from . import views
urlpatterns = [
    # 获取所有用户
    path('users/', views.UserList.as_view(), name='user-list'),
    # 创建新用户
    path('users/', views.UserList.as_view(), name='user-create'), # 通常和 list 是同一个 view
    # 获取单个用户
    path('users/<int:pk>/', views.UserDetail.as_view(), name='user-detail'),
    # 更新单个用户
    path('users/<int:pk>/', views.UserDetail.as_view(), name='user-update'), # 通常和 detail 是同一个 view
    # 删除单个用户
    path('users/<int:pk>/', views.UserDetail.as_view(), name='user-delete'), # 通常和 detail 是同一个 view
]
# views.py
from rest_framework import generics
from .models import User
from .serializers import UserSerializer
class UserList(generics.ListCreateAPIView):
    """处理 GET (list) 和 POST (create) 请求"""
    queryset = User.objects.all()
    serializer_class = UserSerializer
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
    """处理 GET (retrieve), PUT/PATCH (update), DELETE (destroy) 请求"""
    queryset = User.objects.all()
    serializer_class = UserSerializer

可以看到,我们为 User 资源创建了两个视图类,urls.py 中的路径配置有些重复。

ViewSet 的思想

ViewSet 的核心思想是:将处理同一资源(如 User)的多个逻辑操作(如 list, create, retrieve, update, destroy)封装到一个单独的类中。

这个类不再直接处理 HTTP 请求(如 GETPOST),而是处理动作list, create, retrieve 等,通过一个 Router(路由器)来自动生成 URL 配置。

python viewsets接口-图2
(图片来源网络,侵删)

这带来了两个主要好处:

  1. 代码更整洁:所有与 User 资源相关的逻辑都集中在一个地方。
  2. URL 配置更简洁:你不再需要手动为每个操作编写 URL 路径。

ViewSet 的类型

DRF 提供了两种主要的 ViewSet

a) ViewSet

这是最基础的 ViewSet,它不提供任何默认的动作(如 list, create 等),你需要手动实现 .action() 方法。

适用场景:当你需要完全自定义 API 的行为,或者你的 API 操作不符合 RESTful 规范时(一个自定义的 custom_action)。

# views.py
from rest_framework import viewsets
from rest_framework.response import Response
from .models import User
from .serializers import UserSerializer
class CustomUserViewSet(viewsets.ViewSet):
    """
    一个自定义的 ViewSet,没有默认的 list/create/retrieve 动作
    """
    def list(self, request):
        """自定义的 list 动作"""
        users = User.objects.all()
        serializer = UserSerializer(users, many=True)
        return Response(serializer.data)
    def retrieve(self, request, pk=None):
        """自定义的 retrieve 动作"""
        user = User.objects.get(pk=pk)
        serializer = UserSerializer(user)
        return Response(serializer.data)
    def custom_action(self, request):
        """一个完全自定义的动作,不对应标准的 CRUD"""
        # ... 你的自定义逻辑 ...
        return Response({"message": "This is a custom action!"})

b) GenericViewSet

这是最常用的 ViewSet,它继承了 GenericAPIView,所以它提供了 get_queryset()get_serializer_class() 等核心方法,但不包含任何默认的动作(如 .list().create())。

适用场景:当你想使用 DRF 的通用类(如 mixins)来组合出你想要的动作,但又想利用 ViewSet 的代码组织和 URL 自动生成功能。

# views.py
from rest_framework import viewsets, mixins
from .models import User
from .serializers import UserSerializer
# 组合 mixins 来创建只读和可写的 ViewSet
class ReadOnlyUserViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    """
    一个只读的 User ViewSet
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer
class UserViewSet(mixins.ListModelMixin, mixins.CreateModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet):
    """
    一个支持 list, create, retrieve 的 User ViewSet
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

c) ModelViewSet

这是最方便的 ViewSet,它已经为你内置了所有标准的 CRUD 动作:list, create, retrieve, update, partial_update, destroy

适用场景:当你需要快速为一个 Django 模型创建一个完整的 RESTful API 时,这是首选。

# views.py
from rest_framework import viewsets
from .models import User
from .serializers import UserSerializer
class UserModelViewSet(viewsets.ModelViewSet):
    """
    一个完整的 User ViewSet,提供了所有 CRUD 操作
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer

如何使用 ViewSet 和 Router

ViewSet 不能像普通 View 那样直接绑定到 URL,它需要配合 Router 来使用。

步骤 1: 创建 ViewSet

我们以 ModelViewSet 为例。

# views.py
from rest_framework import viewsets
from .models import User
from .serializers import UserSerializer
class UserViewSet(viewsets.ModelViewSet):
    """
    为 User 模型提供完整的 API 接口。
    """
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer

步骤 2: 配置 Router

urls.py 中,我们不再使用 path,而是使用 Router

# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from . import views
# 创建一个路由器并注册我们的 ViewSet
router = DefaultRouter()
router.register(r'users', views.UserViewSet, basename='user')
# basename 是可选的,但推荐提供,它用于生成 URL 名称。
# 如果不提供,DRF 会尝试从 queryset 或 serializer 的 model Meta 中推断。
# API URL 现在由路由器自动决定
urlpatterns = [
    path('', include(router.urls)),
]

步骤 3: 查看生成的 URL

运行你的项目并访问 python manage.py show_urls(如果你安装了 django-extensions),或者直接在浏览器中访问 /users//users/1/,你会看到类似以下的 URL 结构:

HTTP 方法 URL 动作 ViewSet 方法 用途
GET /users/ list .list() 获取用户列表
POST /users/ create .create() 创建新用户
GET /users/1/ retrieve .retrieve() 获取单个用户详情
PUT /users/1/ update .update() 完整更新单个用户
PATCH /users/1/ partial_update .partial_update() 部分更新单个用户
DELETE /users/1/ destroy .destroy() 删除单个用户

DefaultRouter 还会自动为你生成一个 API 根视图,访问 会列出所有已注册的 API 端点。


高级特性:自定义 Actions

除了标准的 CRUD 动作,你还可以在 ViewSet 中添加自定义的动作,这通过 @action 装饰器实现。

示例:添加一个 activate 动作

假设我们想添加一个端点来激活用户。

# views.py
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from .models import User
from .serializers import UserSerializer
class UserViewSet(viewsets.ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer
    @action(detail=True, methods=['post'])
    def activate(self, request, pk=None):
        """
        激活指定用户的自定义动作。
        `detail=True` 表示这个动作作用于单个实例 (e.g., /users/1/activate/)
        """
        user = self.get_object()
        user.is_active = True
        user.save()
        return Response({'status': 'user activated'}, status=status.HTTP_200_OK)
    @action(detail=False, methods=['get'])
    def recent_users(self, request):
        """
        获取最近注册的用户。
        `detail=False` 表示这个动作作用于集合 (e.g., /users/recent_users/)
        """
        recent_users = self.queryset.order_by('-date_joined')[:5]
        serializer = self.get_serializer(recent_users, many=True)
        return Response(serializer.data)

你的 Router 会自动为这些自定义动作生成新的 URL:

HTTP 方法 URL 动作 ViewSet 方法
POST /users/1/activate/ activate .activate()
GET /users/recent_users/ recent_users .recent_users()

@action 装饰器参数

  • methods: 一个列表,指定该动作支持的 HTTP 方法,['get', 'post']
  • detail: 布尔值。
    • True (默认): 动作作用于单个资源实例,URL 格式为 /<model_name>/<pk>/action_name/
    • False: 动作作用于资源集合,URL 格式为 /<model_name>/action_name/
  • url_path: 自定义 URL 路径的一部分,默认为动作函数名。
  • url_name: 自定义 URL 名称的一部分,默认为动作函数名。

总结与最佳实践

特性 View / APIView ViewSet / GenericViewSet ModelViewSet
代码组织 按功能分散 按资源集中 按资源集中
URL 配置 手动编写 path 自动生成 自动生成
默认动作 所有 CRUD 动作
灵活性 最高 相对较低(但足够用)
主要用途 完全自定义 API 自定义 API 或组合通用操作 快速为模型生成 API

最佳实践建议:

  1. 优先使用 ModelViewSet:对于绝大多数标准的 CRUD API,ModelViewSet 是最快、最简洁的选择。
  2. 使用 GenericViewSet + mixins:当你的 API 需要非标准的组合(只读 API,或者只有 listupdate 但没有 create 的 API)时,使用 GenericViewSet 并组合相应的 mixins
  3. 使用 ViewSet:当你的 API 行为非常特殊,完全不符合 RESTful 规范时,才考虑使用基础的 ViewSet
  4. 善用 @action:当需要添加超出标准 CRUD 范围的功能(如触发特定业务逻辑、生成报表等)时,使用 @action 装饰器来扩展你的 API,保持代码的整洁和可维护性。
  5. 总是使用 Router:一旦你开始使用 ViewSet,就坚持使用 Router 来管理 URL,这会让你受益匪浅。
分享:
扫描分享到社交APP
上一篇
下一篇