概述
@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, PasswordResetSerializerclass 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 DefaultRouterrouter = 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 permissionsclass 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 接口。