从入门到"精通"Django REST Framework-(六)
一. 什么是视图集?
视图集是 DRF 提供的用于统一管理多个相关视图逻辑的类。它将常见的 CRUD 操作(如 list, create, retrieve, update, destroy)封装在一个类中,并支持通过路由器(Router)自动生成 RESTful 风格的 URL。
核心特点:
- 一个类处理多个动作(如
GET /users/和GET /users/{id}/)。 - 与
Mixin类结合,快速实现标准化操作。 - 支持自定义扩展(如
@action装饰器)。
二. 为什么要使用视图集?
优势:
- 代码复用:无需为每个动作(列表、详情、创建等)单独写视图类。
- 路由自动化:通过
Router自动生成 URL(如/users/和/users/{id}/)。 - 标准化接口:天然支持 RESTful 设计,适合快速开发 CRUD API。
- 灵活扩展:通过
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 视图集与路由的基础用法
定义视图集
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 自动生成路由:
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 的路由器(如 DefaultRouter 和 SimpleRouter)通过以下机制自动生成 URL:
- 视图集的标准化方法
视图集(如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}/(删除对象)
- 路由器的预定义映射规则
路由器内部预定义了 HTTP 方法 与 视图集方法 的映射关系,例如:
# 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 方法与视图集方法的映射。
- 自动检测视图集支持的方法
当调用router.register()注册视图集时,路由器会检查视图集是否实现了特定方法:- 如果视图集包含
list方法 → 生成列表路由(GET /users/)。 - 如果视图集包含
create方法 → 允许POST /users/。 - 如果视图集包含
retrieve方法 → 生成详情路由(GET /users/{pk}/)。 - 其他方法(
update,destroy)同理。
- 如果视图集包含
实现原理(源码简化版)
以 SimpleRouter 为例,其核心逻辑如下:
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. 定义视图集
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. 自定义路由器规则
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更轻量,适合简单场景。
- 扩展性:
可以通过继承路由器并修改routes规则,实现自定义 URL 设计。
六. api-root 的作用
- 功能:
DefaultRouter自动生成的根路径(/)会列出所有注册的视图集端点。 - 示例:访问
/返回:
{
"users": "http://localhost:8000/users/",
"groups": "http://localhost:8000/groups/"
}
- 自定义:通过视图集的
basename控制链接名称。
七. 其他视图集的使用场景
GenericViewSet使用场景
- 适用场景:需要灵活组合 Mixin 的场景(如仅支持部分操作)。
- 示例:
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)。
- 示例:
class UserViewSet(ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
自动支持 GET, POST, PUT, PATCH, DELETE。
ReadOnlyModelViewSet使用场景
- 适用场景:只读接口(如公开的数据查询 API)。
- 示例:
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 请求(列表级)
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 请求的列表级动作
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)
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 方法
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 参数的复杂逻辑
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. 高级技巧
动态权限控制
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覆盖视图集的默认权限。
自定义序列化器
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复制
@action(detail=False, methods=['get'], throttle_classes=[UserRateThrottle])
@action总结
@action** 的核心价值**:扩展视图集,支持非标准业务逻辑(如/users/{id}/activate/)。- 关键参数:
detail: 区分列表级 vs 对象级操作。methods: 定义支持的 HTTP 方法。url_path: 自定义 URL 路径。
- 典型场景:
- 批量操作(如批量删除)。
- 对象状态变更(如激活/冻结用户)。
- 关联资源的子操作(如订单中的商品筛选)。
验证自定义动作:
通过 DRF 的 Web 界面或 curl 测试:
# 测试示例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/) |
灵活扩展非标准操作 |
最佳实践:
- **优先选择 **
ModelViewSet:快速实现标准 CRUD。 - 使用
@action扩展功能:添加非标准业务逻辑。 - 根据需求选择 Router:
DefaultRouter适合完整 API,SimpleRouter适合轻量级需求。
每天进步一点点

浙公网安备 33010602011771号