You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 23:15:01