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

DRF中如何使用has_object_permission校验POST请求存库前权限

核心问题原因

你自定义的WarehouseIsMemberOfCompany权限类默认无法在POST请求写入数据库前触发校验,这是DRF的默认权限执行逻辑决定的:

  • has_permission 方法会在所有请求进入视图业务逻辑前执行,此时还没有对应操作的数据库实例
  • has_object_permission 方法仅在视图调用get_object()获取到已存在的数据库实例后才会执行,只会作用于检索、更新、删除这类操作已有资源的请求,POST创建新资源时数据还未入库,没有现成的obj实例,因此不会触发该方法的逻辑。
实现方案

方案1:扩展权限类,在has_permission阶段完成POST场景前置校验

POST创建场景下,你需要校验的关联仓库ID是从请求参数中传入的,不需要等数据入库生成实例,因此可以直接在has_permission中区分请求类型,提前完成校验,修改后的权限类代码如下:

from rest_framework import permissions
from .models import Warehouse

class WarehouseIsMemberOfCompany(permissions.BasePermission):
    def has_permission(self, request, view):
        # 未登录用户直接拒绝
        if not request.user.is_authenticated:
            return False
        
        # 非POST请求(操作已有资源)直接放行,后续走对象级权限校验
        if request.method != "POST":
            return True
        
        # POST创建场景:从请求参数中取仓库ID做前置校验
        warehouse_id = request.data.get("warehouse")
        if not warehouse_id:
            return False
        
        try:
            warehouse = Warehouse.objects.get(id=warehouse_id)
        except Warehouse.DoesNotExist:
            return False
        
        # 校验当前用户所属公司与仓库所属公司一致
        return request.user.company.id == warehouse.company.id

    def has_object_permission(self, request, view, obj):
        # 操作已有资源时,校验实例关联仓库的公司归属
        return request.user.company.id == obj.warehouse.company.id

在视图中正常配置该权限类即可生效:

from rest_framework import viewsets
from .permissions import WarehouseIsMemberOfCompany
from .models import StockRecord
from .serializers import StockRecordSerializer

class StockRecordViewSet(viewsets.ModelViewSet):
    queryset = StockRecord.objects.all()
    serializer_class = StockRecordSerializer
    permission_classes = [WarehouseIsMemberOfCompany]

方案2:在序列化器层做校验(更推荐)

如果希望权限校验和参数校验逻辑聚合,返回的错误格式和普通参数错误统一,可以把归属校验逻辑放到序列化器的字段校验方法中,执行时机同样在数据写入数据库之前:

from rest_framework import serializers
from .models import StockRecord, Warehouse

class StockRecordSerializer(serializers.ModelSerializer):
    class Meta:
        model = StockRecord
        fields = "__all__"

    def validate_warehouse(self, value):
        request = self.context["request"]
        if request.user.company.id != value.company.id:
            raise serializers.ValidationError("无权操作该归属仓库下的数据")
        return value

注:使用DRF默认的视图集/泛型视图时,会自动将request传入序列化器上下文,无需额外配置。

注意事项
  • 上述两种方案的校验逻辑都在serializer.save()执行前运行,不会出现校验不通过但数据已经写入数据库的问题。
  • 如果你的POST接口是自定义操作而非创建资源,只要从请求参数中提前取出需要校验的关联对象,就可以用相同逻辑在has_permission阶段完成拦截。

内容的提问来源于stack exchange,提问作者sk1p

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.02 06:48:29