概述
@action
装饰器是 Django REST Framework (DRF) 中 ViewSet 的一个核心功能,用于定义自定义路由方法。它允许开发者在标准的 CRUD 操作(list、create、retrieve、update、destroy)之外,创建符合特定业务需求的接口,并自动生成相应的 URL 路由。
参数详解
1. methods
- HTTP 方法配置
- 类型:
List[str]
- 作用: 指定该动作响应哪些 HTTP 请求方法
- 默认值:
['get']
(仅响应 GET 请求) - 示例:
@action(methods=['post', 'put'], detail=True) def custom_action(self, request, pk=None): # 同时响应 POST 和 PUT 请求 pass
2. detail
- 操作级别配置
- 类型:
bool
- 必填: 是
- 作用: 决定动作是针对单个对象还是整个集合
- 取值:
True
: 针对单个对象,URL 中包含对象IDFalse
: 针对整个集合,URL 中不包含对象ID
- 示例:
# 单个对象操作: /users/{pk}/activate/ @action(detail=True, methods=['post']) def activate(self, request, pk=None): pass # 集合操作: /users/recent/ @action(detail=False, methods=['get']) def recent(self, request): pass
3. url_path
- URL 路径自定义
- 类型:
str
- 作用: 自定义 URL 路径段,覆盖默认的方法名
- 默认值: 使用被装饰的方法名
- 示例:
@action(detail=True, methods=['post'], url_path='change-password') def set_password(self, request, pk=None): pass # 生成 URL: /users/{pk}/change-password/
4. url_name
- 反向解析名称
- 类型:
str
- 作用: 定义反向解析时使用的名称
- 默认值: 方法名(下划线替换为连字符)
- 示例:
@action(detail=True, url_name='user-activation') def activate(self, request, pk=None): pass # 反向解析: reverse('user-viewset-user-activation', kwargs={'pk': 1})
5. **kwargs
- 额外配置参数
- 类型:
dict
- 作用: 覆盖视图级别的配置设置
- 常用选项:
permission_classes
: 权限控制authentication_classes
: 认证方式throttle_classes
: 限流配置renderer_classes
: 响应渲染器parser_classes
: 请求解析器serializer_class
: 序列化器
实战案例
用户管理系统示例
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAdminUser, IsAuthenticated
from django.contrib.auth.models import User
from .serializers import UserSerializer, PasswordResetSerializer
class UserViewSet(viewsets.ModelViewSet):
queryset = User.objects.all()
serializer_class = UserSerializer
# 1. 密码重置 - 单个对象操作
@action(
detail=True,
methods=['post'],
url_path='reset-password',
permission_classes=[IsAuthenticated],
serializer_class=PasswordResetSerializer
)
def reset_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['new_password'])
user.save()
return Response(
{'status': '密码重置成功'},
status=status.HTTP_200_OK
)
# 2. 获取活跃用户 - 集合操作
@action(
detail=False,
methods=['get'],
url_path='active-users',
permission_classes=[IsAdminUser]
)
def get_active_users(self, request):
active_users = User.objects.filter(is_active=True)
page = self.paginate_queryset(active_users)
if page is not None:
serializer = self.get_serializer(page, many=True)
return self.get_paginated_response(serializer.data)
serializer = self.get_serializer(active_users, many=True)
return Response(serializer.data)
# 3. 批量导入用户 - 集合操作,文件上传
@action(
detail=False,
methods=['post'],
url_path='bulk-import',
permission_classes=[IsAdminUser],
parser_classes=[MultiPartParser] # 支持文件上传
)
def bulk_import_users(self, request):
import_file = request.FILES.get('file')
if not import_file:
return Response(
{'error': '请提供导入文件'},
status=status.HTTP_400_BAD_REQUEST
)
# 执行批量导入逻辑
try:
imported_count = self.process_import_file(import_file)
return Response({
'status': f'成功导入 {imported_count} 个用户',
'imported_count': imported_count
})
except Exception as e:
return Response(
{'error': f'导入失败: {str(e)}'},
status=status.HTTP_400_BAD_REQUEST
)
# 4. 用户统计信息 - 集合操作
@action(detail=False, methods=['get'])
def statistics(self, request):
total_users = User.objects.count()
active_users = User.objects.filter(is_active=True).count()
staff_users = User.objects.filter(is_staff=True).count()
return Response({
'total_users': total_users,
'active_users': active_users,
'staff_users': staff_users,
'inactive_users': total_users - active_users
})
# 辅助方法
def process_import_file(self, import_file):
# 实现文件处理逻辑
return 10 # 返回导入的用户数量
自动生成的路由
使用 DefaultRouter
注册 ViewSet:
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'users', UserViewSet, basename='user')
urlpatterns = router.urls
生成的路由包括:
HTTP 方法 | URL 路径 | 调用的方法名 | 方法类型 | 操作级别 | 说明 |
---|---|---|---|---|---|
GET | /users/ |
list |
内置方法 | 集合操作 | 获取用户列表 |
POST | /users/ |
create |
内置方法 | 集合操作 | 创建新用户 |
GET | /users/{pk}/ |
retrieve |
内置方法 | 单个对象 | 获取指定用户详情 |
PUT | /users/{pk}/ |
update |
内置方法 | 单个对象 | 完整更新用户信息 |
PATCH | /users/{pk}/ |
partial_update |
内置方法 | 单个对象 | 部分更新用户信息 |
DELETE | /users/{pk}/ |
destroy |
内置方法 | 单个对象 | 删除指定用户 |
POST | /users/{pk}/reset-password/ |
reset_password |
自定义@action方法 | 单个对象 | 重置用户密码 |
GET | /users/active-users/ |
get_active_users |
自定义@action方法 | 集合操作 | 获取所有活跃用户列表 |
POST | /users/bulk-import/ |
bulk_import_users |
自定义@action方法 | 集合操作 | 批量导入用户数据 |
GET | /users/statistics/ |
statistics |
自定义@action方法 | 集合操作 | 获取用户统计信息 |
详细说明
1. 内置标准方法
ModelViewSet
这些方法是 Django REST Framework 的ModelViewSet
自动提供的:
方法名 | 作用 | 对应 HTTP 方法 |
---|---|---|
list |
查询资源集合 | GET |
create |
创建新资源 | POST |
retrieve |
获取单个资源 | GET |
update |
完整更新资源 | PUT |
partial_update |
部分更新资源 | PATCH |
destroy |
删除资源 | DELETE |
2. 自定义 @action 方法
这些是通过 @action
装饰器添加的自定义业务方法:
方法名 | @action 配置 | 业务功能 |
---|---|---|
reset_password |
@action(detail=True, methods=['post']) |
用户密码重置 |
get_active_users |
@action(detail=False, methods=['get']) |
筛选活跃用户 |
bulk_import_users |
@action(detail=False, methods=['post']) |
批量导入用户 |
statistics |
@action(detail=False, methods=['get']) |
统计数据分析 |
URL 路由生成规则
DRF 的路由器按照以下规则生成 URL:
- 标准方法:
/{model_name}/
和/{model_name}/{pk}/
- 自定义方法:
detail=True
:/{model_name}/{pk}/{action_name}/
detail=False
:/{model_name}/{action_name}/
高级用法
自定义权限和序列化器
from rest_framework import permissions
class IsOwnerOrAdmin(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
return obj == request.user or request.user.is_staff
@action(
detail=True,
methods=['get'],
permission_classes=[IsOwnerOrAdmin],
serializer_class=UserDetailSerializer
)
def detailed_profile(self, request, pk=None):
user = self.get_object()
serializer = self.get_serializer(user)
return Response(serializer.data)
组合多个动作
class ProductViewSet(viewsets.ModelViewSet):
# ... 其他代码 ...
@action(detail=True, methods=['post'])
def add_to_cart(self, request, pk=None):
# 添加到购物车逻辑
pass
@action(detail=True, methods=['post'])
def add_to_wishlist(self, request, pk=None):
# 添加到收藏夹逻辑
pass
@action(detail=True, methods=['get'])
def reviews(self, request, pk=None):
# 获取商品评论
product = self.get_object()
reviews = product.reviews.all()
serializer = ReviewSerializer(reviews, many=True)
return Response(serializer.data)
最佳实践
- 明确操作级别: 始终明确设置
detail
参数,确保 URL 结构正确 - 合理使用 HTTP 方法: 根据操作性质选择正确的 HTTP 方法
- GET: 获取数据
- POST: 创建或执行非幂等操作
- PUT/PATCH: 更新数据
- DELETE: 删除数据
- 适当的权限控制: 为每个动作设置合适的权限类
- 使用专用序列化器: 为不同的动作使用专门的序列化器
- 错误处理: 提供清晰的错误响应
- 文档注释: 为每个自定义动作添加详细的文档注释
常见问题解答
Q: 什么时候应该使用 @action?
A: 当需要实现超出标准 CRUD 操作的业务逻辑时,如状态变更、批量操作、统计查询等。
Q: detail=True 和 detail=False 有什么区别?
A: detail=True
针对单个对象,URL 包含对象ID;detail=False
针对整个集合,URL 不包含对象ID。
Q: 如何为 @action 方法编写测试?
A: 使用 DRF 的 APITestCase,像测试标准端点一样测试自定义动作:
def test_reset_password_action(self):
url = reverse('user-reset-password', kwargs={'pk': self.user.pk})
response = self.client.post(url, {'new_password': 'newpass123'})
self.assertEqual(response.status_code, status.HTTP_200_OK)
总结
@action
装饰器是 DRF ViewSet 的强大扩展工具,它提供了:
- 灵活性: 轻松创建自定义 API 端点
- 一致性: 保持与标准 CRUD 操作一致的代码风格
- 自动化: 自动生成 URL 路由
- 可配置性: 支持细粒度的权限、认证和序列化配置
通过合理使用 @action
装饰器,可以构建出既符合 RESTful 原则又能满足复杂业务需求的 API 接口。