DRF视图集自定义方法@action与request对象详解
DRF视图集自定义方法@action与request对象详解
一、@action装饰器基础配置解析
1.1 装饰器参数说明
@action(detail=False, methods=['post'], url_path='batch')
def bulk_upload(self, request):
"""
批量上传自定义端点
- detail=False: 作用于资源集合,URL路径为/resource/batch/
- methods=['post']: 仅接受POST请求
- url_path='batch': 自定义URL路径段,默认使用方法名
"""
- detail参数:False表示操作整个资源集合(如/users/batch/),True表示操作单个实例(如/users/{id}/activate/)[^2][^3]
- methods参数:显式声明允许的HTTP方法,未指定则默认支持GET,企业级开发必须显式指定以避免安全风险[^7]
- url_path参数:自定义URL路径,支持中划线命名(如url_path='batch-upload'),提升API可读性[^4]
1.2 路由与访问方式
DRF路由器自动生成路由规则:
# urls.py
from rest_framework.routers import DefaultRouter
router = DefaultRouter()
router.register(r'resources', ResourceViewSet)
# 自动注册路由: POST /resources/batch/ → bulk_upload方法
- 无需手动配置URL,降低维护成本
- 支持通过url_name参数自定义路由名称,便于反向解析[^7]
- 功能:自动解析JSON、表单、文件等格式的请求体数据,替代Django的request.POST和request.FILES[^5][^18]
- 企业级用法:
二、request对象核心属性与企业级应用
2.1 请求数据获取
2.1.1 request.data(请求体数据)
def bulk_upload(self, request):
# 安全获取批量数据(自动解析Content-Type)
upload_data = request.data.get('items', [])
# 文件处理(支持multipart/form-data格式)
files = request.FILES.getlist('files') # 获取多文件列表
# 数据验证(企业级必须步骤)
serializer = BulkUploadSerializer(data=upload_data)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
2.1.2 request.query_params(URL查询参数)
- 功能:获取URL中的查询字符串参数,等同于Django的request.GET,命名更符合REST规范[^8][^15]
- 企业级用法:
def bulk_upload(self, request):
# 分页参数获取(防SQL注入)
page = request.query_params.get('page', 1)
# 过滤条件获取
filter_type = request.query_params.get('filter', 'all')
# 批量操作模式指定
operation_mode = request.query_params.get('mode', 'insert')
2.2 认证与授权信息
2.2.1 request.user(认证用户)
- 功能:获取经过认证的用户实例,未认证时为AnonymousUser[^8][^18]
- 企业级用法:
def bulk_upload(self, request):
# 审计日志记录
logger.info(f"批量上传操作 - 用户: {request.user.username}, IP: {self.get_client_ip(request)}")
# 权限校验
if not request.user.has_perm('app.bulk_upload'):
return Response(
{"error": "无批量上传权限"},
status=status.HTTP_403_FORBIDDEN
)
2.2.2 request.auth(认证凭证)
- 功能:获取认证凭证对象(如JWT令牌),用于高级权限控制[^8][^18]
- 企业级用法:
def bulk_upload(self, request):
# 令牌有效期检查
if request.auth and request.auth.exp < timezone.now():
return Response(
{"error": "令牌已过期"},
status=status.HTTP_401_UNAUTHORIZED
)
2.3 请求元数据与安全信息
2.3.1 request.META(请求头与环境变量)
- 核心元数据:
o HTTP_AUTHORIZATION: 认证头信息(如Bearer令牌)
o HTTP_X_FORWARDED_FOR: 客户端真实IP(支持代理场景)
o CONTENT_TYPE: 请求内容类型(如application/json)
o HTTP_USER_AGENT: 客户端设备信息[^8][^18]
- 企业级用法:
def get_client_ip(self, request):
"""安全获取客户端IP(支持代理服务器)"""
xff = request.META.get('HTTP_X_FORWARDED_FOR')
return xff.split(',')[0].strip() if xff else request.META.get('REMOTE_ADDR')
def bulk_upload(self, request):
# 防DDoS检查(限制请求体大小)
content_length = int(request.META.get('CONTENT_LENGTH', 0))
if content_length > 10 * 1024 * 1024: # 10MB限制
return Response(
{"error": "请求体超过最大限制"},
status=status.HTTP_413_REQUEST_ENTITY_TOO_LARGE
)
# GDPR合规性检查(强制JSON格式)
if request.content_type != 'application/json':
return Response(
{"error": "仅支持application/json格式"},
status=status.HTTP_415_UNSUPPORTED_MEDIA_TYPE
)
三、企业级批量上传实现案例
3.1 完整业务代码
import logging
from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from django.utils import timezone
import uuid
logger = logging.getLogger(__name__)
class ResourceViewSet(viewsets.ModelViewSet):
queryset = Resource.objects.all()
serializer_class = ResourceSerializer
permission_classes = [IsAuthenticated] # 继承视图集的权限控制
@action(detail=False, methods=['post'], url_path='batch')
def bulk_upload(self, request):
"""
企业级批量上传实现
- 支持JSON/表单数据输入
- 包含完整的认证、授权、审计流程
- 实现数据验证与错误处理
"""
# 1. 请求审计(企业级合规要求)
request_id = str(uuid.uuid4())
client_ip = self.get_client_ip(request)
logger.info(
f"[批量上传] request_id={request_id}, user={request.user.username}, "
f"ip={client_ip}, content_type={request.content_type}"
)
# 2. 安全检查
if not self._validate_request(request):
return Response(
{"error": "请求验证失败", "request_id": request_id},
status=status.HTTP_400_BAD_REQUEST
)
# 3. 数据解析与验证
serializer = BulkResourceSerializer(data=request.data)
if not serializer.is_valid():
logger.warning(
f"[批量上传] 数据验证失败 request_id={request_id}, errors={serializer.errors}"
)
return Response(
{"error": "数据格式错误", "details": serializer.errors, "request_id": request_id},
status=status.HTTP_400_BAD_REQUEST
)
# 4. 批量业务处理
try:
result = self._process_batch(serializer.validated_data)
logger.info(
f"[批量上传] 处理成功 request_id={request_id}, "
f"created={result['created']}, updated={result['updated']}"
)
return Response({
"status": "success",
"data": result,
"request_id": request_id
}, status=status.HTTP_201_CREATED)
except Exception as e:
logger.error(
f"[批量上传] 处理失败 request_id={request_id}, error={str(e)}",
exc_info=True
)
return Response(
{"error": "服务器内部错误", "request_id": request_id},
status=status.HTTP_500_INTERNAL_SERVER_ERROR
)
def _validate_request(self, request):
"""请求安全验证"""
# 内容类型检查
if request.content_type not in ['application/json', 'application/x-www-form-urlencoded']:
return False
# 请求体大小检查
content_length = int(request.META.get('CONTENT_LENGTH', 0))
if content_length > 10 * 1024 * 1024: # 10MB
return False
return True
def _process_batch(self, validated_data):
"""批量数据处理逻辑"""
resources = validated_data['resources']
created_count = 0
updated_count = 0
# 使用bulk_create优化性能
create_list = []
for item in resources:
resource = Resource(**item, created_by=request.user)
create_list.append(resource)
if create_list:
Resource.objects.bulk_create(create_list)
created_count = len(create_list)
return {"created": created_count, "updated": updated_count}
3.2 关键技术点解析
1.** 性能优化:使用bulk_create批量创建数据,比循环创建效率提升10-100倍[^8]
2.安全防护 **:
- 限制请求体大小防止DDoS攻击
- 验证内容类型确保数据格式安全
- 记录详细审计日志支持问题追溯
3.** 错误处理 **:
- 使用UUID生成唯一请求ID便于故障排查
- 区分客户端错误与服务器错误
- 详细记录错误堆栈但返回友好提示
4.** 权限控制 **:
- 继承视图集的permission_classes
- 额外检查对象级权限
- 基于用户角色限制操作范围
四、request对象高级特性
4.1 数据解析机制
DRF自动根据Content-Type选择解析器:
- application/json→JSONParser
- application/x-www-form-urlencoded→FormParser
- multipart/form-data→MultiPartParser[^10][^19]
企业级配置示例:
# 局部配置解析器
class ResourceViewSet(viewsets.ModelViewSet):
parser_classes = [JSONParser, MultiPartParser] # 仅支持JSON和文件上传
4.2 原生Django请求访问
DRF的Request对象通过代理模式封装Django原生HttpRequest:
# 访问Django原生属性
raw_post_data = request._request.body # 原始请求体
session_data = request._request.session # Session数据
cookies = request._request.COOKIES # Cookie数据
# 通过__getattr__自动代理
method = request.method # 等同于request._request.method
path = request.path # 等同于request._request.path
** 注意 **:优先使用DRF提供的属性(如data、query_params),仅在必要时访问原生请求[^1][^14]
4.3 内容协商与响应格式
request对象支持内容协商,根据Accept头自动选择响应格式:
def bulk_upload(self, request):
# 获取客户端期望的响应格式
accepted_format = request.accepted_renderer.format
if accepted_format == 'csv':
return self._render_csv_response(data)
return Response(data)
五、最佳实践与常见问题
5.1 命名规范
- URL路径:使用名词复数形式,如/resources/batch/
- 方法命名:使用动词开头的蛇形命名,如bulk_upload
- 权限控制:显式指定permission_classes,如[IsAdminUser][^4][^7]
5.2 常见错误处理
1.** 数据验证失败 **:
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
2.** 权限不足 **:
if not request.user.is_staff:
return Response(
{"error": "管理员专属功能"},
status=status.HTTP_403_FORBIDDEN
)
3.** 请求方法不允许 **:
@action(detail=False, methods=['post']) # 仅允许POST
def bulk_upload(self, request):
pass # 非POST请求自动返回405 Method Not Allowed
5.3 性能优化建议
1.** 批量操作:使用bulk_create/bulk_update减少数据库交互
2.分页处理:对大型结果集使用request.query_params实现分页
3.异步处理 **:对耗时操作使用Celery异步执行:
from celery import shared_task
@shared_task
def process_large_batch(data, user_id):
# 耗时处理逻辑
def bulk_upload(self, request):
# 立即返回响应
process_large_batch.delay(request.data, request.user.id)
return Response({"status": "任务已提交"}, status=status.HTTP_202_ACCEPTED)
通过以上内容,可全面掌握DRF视图集自定义方法中@action装饰器的配置与request对象的企业级应用,实现安全、高效、可维护的批量操作接口。
 
                    
                 
                
            
         
 浙公网安备 33010602011771号
浙公网安备 33010602011771号