Django Rest Framework自定义has_object_permission为何未被调用?
has_object_permission在KeywordList视图中无法触发? 这个问题其实是Django REST Framework(DRF)权限系统的执行逻辑导致的,我来给你拆解清楚:
核心原因:两个权限方法的触发场景完全不同
DRF的权限检查有明确的执行顺序和触发条件:
has_permission是全局入口,优先执行
不管请求是针对资源集合还是单个资源,DRF都会先调用has_permission。如果这个方法返回False,直接返回403;如果返回True,才会根据请求类型决定是否继续调用has_object_permission。has_object_permission仅在操作「已存在的具体对象」时触发
这个方法是用来校验对单个资源的操作权限的——比如访问api/v1/keyword/$id获取、修改或删除某个关键词时,DRF需要先从数据库中查询出对应的对象,这时候才会触发has_object_permission。但
KeywordList的POST请求是创建新资源,此时还没有对应的对象存在,DRF不需要查询任何对象,自然不会触发has_object_permission的校验逻辑。默认权限逻辑的隐形坑
如果你只重写了has_object_permission而没重写has_permission,has_permission会使用BasePermission的默认实现——直接返回True。这就导致创建请求直接跳过了权限校验,返回200,你写的has_object_permission代码根本没机会执行。
正确的解决方法
你需要在自定义权限类中同时实现has_permission和has_object_permission,分别处理集合级和对象级的权限:
from rest_framework.permissions import BasePermission class AnonWriteOnly(BasePermission): def has_permission(self, request, view): # 处理集合级请求:允许匿名/非管理员POST创建关键词 if view.action == "create": return request.method == "POST" and (request.user.is_anonymous or not request.user.is_staff) # 其他集合级动作(比如GET列表)仅允许管理员访问 return request.user.is_staff def has_object_permission(self, request, view, obj): # 处理对象级请求:允许匿名/非管理员POST到详情端点 # 注意:需确保你的KeywordDetail视图已配置支持POST动作 return request.method == "POST" and (request.user.is_anonymous or not request.user.is_staff)
这样不管是创建新资源的POST请求,还是针对单个资源的POST请求,都会触发对应的权限校验逻辑。
内容的提问来源于stack exchange,提问作者Marsha

