前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >从入门到"精通"Django REST Framework-(六)

从入门到"精通"Django REST Framework-(六)

原创
作者头像
rxg456
发布2025-03-01 00:11:37
发布2025-03-01 00:11:37
7700
代码可运行
举报
文章被收录于专栏:想法独特的Dev+Ops
运行总次数:0
代码可运行

一. 什么是视图集?

视图集是 DRF 提供的用于统一管理多个相关视图逻辑的类。它将常见的 CRUD 操作(如 list, create, retrieve, update, destroy)封装在一个类中,并支持通过路由器(Router)自动生成 RESTful 风格的 URL。

核心特点

  • 一个类处理多个动作(如 GET /users/GET /users/{id}/)。
  • Mixin 类结合,快速实现标准化操作。
  • 支持自定义扩展(如 @action 装饰器)。

二. 为什么要使用视图集?

优势:

  1. 代码复用:无需为每个动作(列表、详情、创建等)单独写视图类。
  2. 路由自动化:通过 Router 自动生成 URL(如 /users//users/{id}/)。
  3. 标准化接口:天然支持 RESTful 设计,适合快速开发 CRUD API。
  4. 灵活扩展:通过 Mixin 组合功能,或添加自定义动作。

缺点:

  • 灵活性受限:不适合需要高度定制化逻辑的场景(如非 RESTful 接口)。

三. 视图集与 Mixin 的关系

视图集类

继承关系

支持的 Mixin

功能描述

ViewSet

直接继承 APIView

无,需手动实现所有方法(如 list, create

最基础的视图集,完全自由但需大量手动编码

GenericViewSet

继承 GenericAPIView(提供 queryset, serializer_class 等核心功能)

需手动组合 Mixin(如 ListModelMixin, CreateModelMixin

灵活组合 Mixin,适合需要自定义逻辑的场景

ModelViewSet

继承 GenericViewSet + 所有 CRUD Mixin

ListModelMixin, CreateModelMixin, RetrieveModelMixin, UpdateModelMixin, DestroyModelMixin

全功能视图集,自动支持 CRUD 操作

ReadOnlyModelViewSet

继承 GenericViewSet + 只读 Mixin

ListModelMixin, RetrieveModelMixin

仅支持 list(列表)和 retrieve(详情)操作

四. ViewSet 视图集与路由的基础用法

定义视图集

代码语言:python
代码运行次数:0
复制
from rest_framework.viewsets import ViewSet
from rest_framework.response import Response

class UserViewSet(ViewSet):
    def list(self, request):
        users = User.objects.all()
        serializer = UserSerializer(users, many=True)
        return Response(serializer.data)

路由绑定

使用 Router 自动生成路由:

代码语言:python
代码运行次数:0
复制
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
urlpatterns = router.urls

生成的路由:

  • GET /users/list()
  • POST /users/create()
  • 其他动作需手动实现或通过 Mixin 添加。

五. 注册路由介绍

区别

特性

DefaultRouter

SimpleRouter

API 根视图

自动生成 / 的根视图(列出所有注册的路由)

不生成根视图

格式后缀

支持 .json.api 等格式后缀(如 /users.json

不支持格式后缀

URL 路径风格

严格以斜杠 / 结尾(如 /users/

可配置是否包含斜杠(默认包含)

额外功能

提供更丰富的超链接 API 展示

轻量级,只生成基础路由

为什么路由器能自动生成列表和详情页的 API?

DRF 的路由器(如 DefaultRouterSimpleRouter)通过以下机制自动生成 URL:

  1. 视图集的标准化方法undefined视图集(如 ModelViewSet)定义了标准化的方法(list, create, retrieve, update, destroy),这些方法对应 RESTful 的 CRUD 操作:
    • list()GET /users/(获取列表)
    • create()POST /users/(创建对象)
    • retrieve()GET /users/{pk}/(获取单个对象)
    • update()PUT /users/{pk}/(全量更新)
    • partial_update()PATCH /users/{pk}/(部分更新)
    • destroy()DELETE /users/{pk}/(删除对象)
  2. 路由器的预定义映射规则undefined路由器内部预定义了 HTTP 方法视图集方法 的映射关系,例如:
代码语言:python
代码运行次数:0
复制
# SimpleRouter 的默认路由规则
routes = [
    # 列表路由(不带 {pk})
    Route(
        url=r'^{prefix}/$',
        mapping={'get': 'list', 'post': 'create'},
        name='{basename}-list',
        detail=False,
    ),
    # 详情路由(带 {pk})
    Route(
        url=r'^{prefix}/{lookup}/$',
        mapping={
            'get': 'retrieve',
            'put': 'update',
            'patch': 'partial_update',
            'delete': 'destroy'
        },
        name='{basename}-detail',
        detail=True,
    ),
]
  • prefix:注册时的 URL 前缀(如 users)。
  • lookup:对象标识符(默认是 pk)。
  • mapping:HTTP 方法与视图集方法的映射。
  1. 自动检测视图集支持的方法undefined当调用 router.register() 注册视图集时,路由器会检查视图集是否实现了特定方法:
    • 如果视图集包含 list 方法 → 生成列表路由(GET /users/)。
    • 如果视图集包含 create 方法 → 允许 POST /users/
    • 如果视图集包含 retrieve 方法 → 生成详情路由(GET /users/{pk}/)。
    • 其他方法(update, destroy)同理。
实现原理(源码简化版)

SimpleRouter 为例,其核心逻辑如下:

代码语言:python
代码运行次数:0
复制
class SimpleRouter:
    def get_urls(self):
        urls = []
        # 遍历所有预定义的路由规则(如列表路由、详情路由)
        for route in self.routes:
            # 动态生成 URL 正则表达式和视图函数
            url = route.url.format(prefix=self.prefix, lookup=self.lookup)
            view = self.viewset.as_view(route.mapping)
            urls.append(url(path=url, view=view, name=route.name))
        return urls
  • 动态生成 URL 模式:

路由器通过字符串模板(如 r'^{prefix}/$')生成具体的 URL 正则表达式。例如,注册 users 时,会生成 r'^users/$'r'^users/{pk}/$'

  • 动态绑定 HTTP 方法到视图集:

通过 viewset.as_view(mapping) 将 HTTP 方法(如 GET)映射到视图集的对应方法(如 list)。

示例:自定义路由规则

假设需要为 UserViewSet 添加一个 search 动作:

1. 定义视图集
代码语言:python
代码运行次数:0
复制
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action

class UserViewSet(ModelViewSet):
    @action(detail=False, methods=['get'], url_path='search')
    def search_users(self, request):
        # 实现搜索逻辑
        return Response(...)
2. 自定义路由器规则
代码语言:python
代码运行次数:0
复制
from rest_framework.routers import SimpleRouter, Route

class CustomRouter(SimpleRouter):
    routes = [
        # 默认的列表路由和详情路由
        *SimpleRouter.routes,
        # 添加自定义路由
        Route(
            url=r'^{prefix}/search/$',
            mapping={'get': 'search_users'},
            name='{basename}-search',
            detail=False,
        ),
    ]

router = CustomRouter()
router.register(r'users', UserViewSet)
3. 生成的 URL
  • GET /users/list()
  • GET /users/search/search_users()
  • GET /users/{pk}/retrieve()

路由总结
  • 为什么能自动生成路由:DRF 的路由器通过预定义的规则(如 list 对应 GET /users/),结合视图集的方法检测,动态生成 URL。
  • DefaultRouter vs SimpleRouter:
  • DefaultRouter 提供更完整的 RESTful 支持(API 根视图、格式后缀)。
  • SimpleRouter 更轻量,适合简单场景。
  • 扩展性:undefined可以通过继承路由器并修改 routes 规则,实现自定义 URL 设计。

六. api-root 的作用

  • 功能DefaultRouter 自动生成的根路径(/)会列出所有注册的视图集端点。
  • 示例:访问 / 返回:
代码语言:json
复制
{
  "users": "http://localhost:8000/users/",
  "groups": "http://localhost:8000/groups/"
}
  • 自定义:通过视图集的 basename 控制链接名称。

七. 其他视图集的使用场景

GenericViewSet使用场景

  • 适用场景:需要灵活组合 Mixin 的场景(如仅支持部分操作)。
  • 示例
代码语言:python
代码运行次数:0
复制
from rest_framework.mixins import ListModelMixin, CreateModelMixin
class UserViewSet(ListModelMixin, CreateModelMixin, GenericViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

支持 GET /users/(列表)和 POST /users/(创建)。

ModelViewSet使用场景

  • 适用场景:标准 CRUD 接口(如后台管理 API)。
  • 示例
代码语言:python
代码运行次数:0
复制
class UserViewSet(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

自动支持 GET, POST, PUT, PATCH, DELETE

ReadOnlyModelViewSet使用场景

  • 适用场景:只读接口(如公开的数据查询 API)。
  • 示例
代码语言:python
代码运行次数:0
复制
class UserViewSet(ReadOnlyModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

仅支持 GET /users/(列表)和 GET /users/{id}/(详情)。

八. 视图集中 @action 装饰器的使用

@action 是 DRF 中用于在视图集(ViewSet)中定义自定义动作的核心装饰器,可以将任意方法暴露为 API 端点。下面通过更多场景详细说明其用法。

1. 基本用法

核心参数说明:
  • detail:
  • True:操作单个对象(如 /users/{id}/action_name/)。
  • False:操作列表或集合(如 /users/action_name/)。
  • methods: 允许的 HTTP 方法(如 ['get', 'post'])。
  • url_path: 自定义 URL 路径(默认用方法名)。
  • url_name: 路由名称(用于反向解析)。
示例 1:简单 GET 请求(列表级)
代码语言:python
代码运行次数:0
复制
from rest_framework.decorators import action
from rest_framework.response import Response

class UserViewSet(ModelViewSet):
    queryset = User.objects.all()
    serializer_class = UserSerializer

    # 获取所有管理员用户
    @action(detail=False, methods=['get'])
    def admins(self, request):
        admins = User.objects.filter(is_admin=True)
        serializer = self.get_serializer(admins, many=True)
        return Response(serializer.data)
  • 路由GET /users/admins/
  • 说明detail=False 表示操作对象是整个用户列表的筛选。
示例 2:带 POST 请求的列表级动作
代码语言:python
代码运行次数:0
复制
class UserViewSet(ModelViewSet):
    @action(detail=False, methods=['post'], url_path='bulk-delete')
    def bulk_delete(self, request):
        ids = request.data.get('ids', [])
        User.objects.filter(id__in=ids).delete()
        return Response({'status': '批量删除成功'})
  • 路由POST /users/bulk-delete/
  • 说明
  • 通过 url_path 自定义 URL 路径。
  • 从请求体中获取 ids 列表,批量删除用户。

2. 复杂示例(带参数)

示例 3:操作单个对象(detail=True
代码语言:python
代码运行次数:0
复制
class UserViewSet(ModelViewSet):
    @action(detail=True, methods=['post'], url_path='activate')
    def activate_user(self, request, pk=None):
        user = self.get_object()  # 自动根据 pk 获取对象
        user.is_active = True
        user.save()
        return Response({'status': '用户已激活'})
  • 路由POST /users/{pk}/activate/
  • 说明
  • detail=True 表示操作单个用户实例。
  • pk 参数自动从 URL 中捕获(如 /users/5/activate/ 中的 5)。
示例 4:混合 GET 和 POST 方法
代码语言:python
代码运行次数:0
复制
class ProductViewSet(ModelViewSet):
    @action(detail=True, methods=['get', 'post'], url_path='price-history')
    def price_history(self, request, pk=None):
        product = self.get_object()
        if request.method == 'GET':
            # 获取价格历史
            history = PriceHistory.objects.filter(product=product)
            serializer = PriceHistorySerializer(history, many=True)
            return Response(serializer.data)
        elif request.method == 'POST':
            # 添加新价格记录
            serializer = PriceHistorySerializer(data=request.data)
            serializer.is_valid(raise_exception=True)
            serializer.save(product=product)
            return Response(serializer.data)
  • 路由
  • GET /products/{pk}/price-history/ → 获取历史价格
  • POST /products/{pk}/price-history/ → 添加新价格
  • 说明:同一个动作处理多个 HTTP 方法。
示例 5:带 URL 参数的复杂逻辑
代码语言:python
代码运行次数:0
复制
class OrderViewSet(ModelViewSet):
    @action(detail=True, methods=['get'], url_path='items/(?P<category>[^/.]+)')
    def filter_items_by_category(self, request, pk=None, category=None):
        order = self.get_object()
        items = order.items.filter(category=category)
        serializer = OrderItemSerializer(items, many=True)
        return Response(serializer.data)
  • 路由GET /orders/{pk}/items/electronics/ → 筛选订单中电子类商品
  • 说明
  • url_path 中使用正则表达式捕获参数(category)。
  • 参数通过方法参数传递(category)。

3. 高级技巧

动态权限控制
代码语言:python
代码运行次数:0
复制
class UserViewSet(ModelViewSet):
    @action(detail=True, methods=['post'], permission_classes=[IsAdminUser])
    def promote_to_admin(self, request, pk=None):
        user = self.get_object()
        user.is_admin = True
        user.save()
        return Response({'status': '用户已升级为管理员'})
  • 说明:通过 permission_classes 覆盖视图集的默认权限。
自定义序列化器
代码语言:python
代码运行次数:0
复制
class UserViewSet(ModelViewSet):
    def get_serializer_class(self):
        if self.action == 'change_password':
            return ChangePasswordSerializer
        return UserSerializer

    @action(detail=True, methods=['post'])
    def change_password(self, request, pk=None):
        user = self.get_object()
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user.set_password(serializer.validated_data['password'])
        user.save()
        return Response({'status': '密码已修改'})
  • 说明:根据动作动态选择序列化器。

4. 常见问题

Q1:如何访问 URL 中的参数?
  • 对于 detail=True 的动作,pk 自动从 URL 捕获。
  • 自定义参数可通过正则表达式在 url_path 中定义(如示例 5)。
Q2:如何控制动作的 URL 路径?
  • 使用 url_path 参数覆盖默认路径(如 url_path='custom-path')。
Q3:如何限制动作的访问频率?
  • 结合 throttle_classes:python复制
代码语言:plain
复制
@action(detail=False, methods=['get'], throttle_classes=[UserRateThrottle])

@action总结

  • @action 的核心价值:扩展视图集,支持非标准业务逻辑(如 /users/{id}/activate/)。
  • 关键参数
  • detail: 区分列表级 vs 对象级操作。
  • methods: 定义支持的 HTTP 方法。
  • url_path: 自定义 URL 路径。
  • 典型场景
    • 批量操作(如批量删除)。
    • 对象状态变更(如激活/冻结用户)。
    • 关联资源的子操作(如订单中的商品筛选)。

验证自定义动作

通过 DRF 的 Web 界面或 curl 测试:

代码语言:bash
复制
# 测试示例1
curl http://localhost:8000/users/admins/

# 测试示例3
curl -X POST http://localhost:8000/users/5/activate/

九. 视图集总结

组件/技术

适用场景

核心优势

ViewSet

完全自定义逻辑的非标准接口

自由度高

GenericViewSet

需要灵活组合 Mixin 的场景(如仅支持部分操作)

可定制化 + DRF 核心功能

ModelViewSet

标准 CRUD 接口(如后台管理)

全自动 + 零编码

ReadOnlyModelViewSet

只读接口(如公开数据查询)

安全 + 简洁

@action

扩展自定义动作(如 /users/active_users/

灵活扩展非标准操作

最佳实践

  1. 优先选择 ModelViewSet:快速实现标准 CRUD。
  2. 使用 @action 扩展功能:添加非标准业务逻辑。
  3. 根据需求选择 RouterDefaultRouter 适合完整 API,SimpleRouter 适合轻量级需求。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一. 什么是视图集?
  • 二. 为什么要使用视图集?
    • 优势:
    • 缺点:
  • 三. 视图集与 Mixin 的关系
  • 四. ViewSet 视图集与路由的基础用法
    • 定义视图集
    • 路由绑定
  • 五. 注册路由介绍
    • 区别
    • 为什么路由器能自动生成列表和详情页的 API?
    • 实现原理(源码简化版)
    • 示例:自定义路由规则
    • 路由总结
  • 六. api-root 的作用
  • 七. 其他视图集的使用场景
    • GenericViewSet使用场景
    • ModelViewSet使用场景
    • ReadOnlyModelViewSet使用场景
  • 八. 视图集中 @action 装饰器的使用
    • 1. 基本用法
      • 核心参数说明:
      • 示例 1:简单 GET 请求(列表级)
      • 示例 2:带 POST 请求的列表级动作
    • 2. 复杂示例(带参数)
      • 示例 3:操作单个对象(detail=True)
      • 示例 4:混合 GET 和 POST 方法
      • 示例 5:带 URL 参数的复杂逻辑
    • 3. 高级技巧
      • 动态权限控制
      • 自定义序列化器
    • 4. 常见问题
      • Q1:如何访问 URL 中的参数?
      • Q2:如何控制动作的 URL 路径?
      • Q3:如何限制动作的访问频率?
    • @action总结
  • 九. 视图集总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档