DRF实现可读取request.data的自定义认证 解决诊所数据权限校验问题
你原有代码报错的核心原因是装饰器执行顺序问题:@api_view 装饰器的作用是将Django原生的HttpRequest对象封装为DRF的Request对象,新增request.data等属性,而你把自定义权限装饰器放在了@api_view外层,此时拿到的还是原生request,自然无法获取request.data。如果把装饰器放在内层,你的权限函数返回布尔值不符合DRF视图必须返回Response的要求,所以触发断言错误。
问题1:该场景下如何实现DRF自定义权限控制
DRF官方推荐通过自定义权限类实现权限校验,而非自定义装饰器,权限校验逻辑会在DRF完成请求解析后、视图逻辑执行前自动触发,完全适配你的需求。
你要做的核心区分:
- 认证(Authentication):校验当前登录用户身份,你的场景已经完成了用户登录,不需要自定义认证,只需要自定义权限。
- 权限(Permission):校验当前用户对目标诊所是否有指定操作权限,这正是你需要实现的部分。
问题2:沿用原有校验逻辑、获取request.data的实现方案
步骤1:调整原有校验逻辑
将原有has_permission_level中返回重定向的逻辑改为直接返回布尔值,适配DRF权限类的要求:
def has_permission_level(request, required_permission, clinic=None): user = request.user if not clinic or not user.is_authenticated: return False # 原有校验逻辑不变 group_maps = UserGroupMap.objects.filter(user=user, clinic=clinic) if len(group_maps) < 1: return False for map in group_maps: rolesmapped = GroupRoleMap.objects.filter(group=map.group) for rolemap in rolesmapped: if rolemap.role.name == required_permission: return True return False
步骤2:实现自定义DRF权限类
from rest_framework.permissions import BasePermission from rest_framework.exceptions import PermissionDenied class ClinicRolePermission(BasePermission): # 权限不通过时返回的错误提示 message = "无当前诊所的操作权限" def has_permission(self, request, view): # 从视图属性读取当前接口需要的权限码,不同接口可以自定义不同权限 required_perm = getattr(view, "required_permission", None) if not required_perm: return True # 1. 获取诊所ID clinic_id = None if request.method == "POST": # POST请求从提交数据中取linkedclinic clinic_id = request.data.get("registration_data", {}).get("linkedclinic") else: # GET/PUT等请求从请求头/查询参数取诊所ID(根据你的前端传参逻辑调整) clinic_id = request.META.get("HTTP_X_CLINIC_ID") or request.query_params.get("clinic_id") if not clinic_id: raise PermissionDenied("缺少诊所ID参数") # 2. 校验诊所存在性 try: target_clinic = Clinic.objects.get(clinicid=clinic_id) except Clinic.DoesNotExist: raise PermissionDenied("目标诊所不存在") # 3. 复用原有逻辑校验权限 return has_permission_level(request, required_perm, clinic=target_clinic)
步骤3:给视图添加权限校验
直接用DRF原生的@permission_classes装饰器修饰函数视图即可:
@api_view(['GET', 'POST']) @permission_classes([ClinicRolePermission]) def register_patient_vue(request): # 给当前视图指定需要的权限 register_patient_vue.required_permission = 'EDIT_CLINICAL_RECORD' # 原有视图逻辑完全不需要修改 if request.method == 'POST': data = request.data['registration_data'] serializer = customerSpecialSerializer(data=data) if serializer.is_valid(): serializer.save() return Response(serializer.data, status=status.HTTP_201_CREATED) return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
为什么可以拿到request.data?
DRF的权限类校验逻辑运行在@api_view完成请求封装之后,此时传入的request已经是DRF处理过的Request对象,不管是JSON、表单还是其他格式的提交数据,都可以直接通过request.data读取。
如果一定要沿用原有装饰器方案,只要把自定义装饰器放在@api_view的内层,调整装饰器逻辑:API请求校验失败时返回403 Response而非重定向,权限校验函数只返回布尔值即可,不过更推荐用官方提供的权限类方案,可维护性更强。
内容的提问来源于stack exchange,提问作者Joel G Mathew
相关产品推荐
相关产品推荐

