自定义权限类必须继承BasePermission,不可用函数或无指定方法的类;需重写has_permission控制视图级访问,has_object_permission控制实例级操作,二者为“且”关系;permission_classes须为元组或列表。

自定义权限类必须继承 BasePermission
DRF 的权限系统不接受任意可调用对象,只认继承自 BasePermission 的类。直接写一个函数或返回布尔值的类方法会静默失效——API 始终放行(因为 DRF 默认只检查 has_permission 和 has_object_permission 两个方法是否存在且返回 True)。
实操建议:
- 始终从
from rest_framework.permissions import BasePermission导入基类 - 重写
has_permission(self, request, view)控制视图级访问(如 GET /api/users/) - 重写
has_object_permission(self, request, view, obj)控制实例级操作(如 DELETE /api/users/123/),注意该方法仅在对象已被取出后调用(即get_object()已执行) - 返回
False时 DRF 自动返回403 Forbidden,无需手动 raise
区分 has_permission 和 has_object_permission 的触发时机
常见错误是把对象级逻辑全塞进 has_permission,结果在列表接口(如 ListAPIView)里反复查数据库,或在未传 pk 的请求中尝试访问 view.get_object() 导致 404。
使用场景示例:
立即学习“Python免费学习笔记(深入)”;
-
has_permission:判断用户是否登录、是否为 staff、是否拥有某全局角色(如"can_manage_orders") -
has_object_permission:判断当前用户是否为该订单的创建者、是否属于同一部门、是否被授权编辑该资源 - 注意:即使
has_permission返回True,DRF 仍会在单个对象操作中调用has_object_permission—— 两者是“且”关系,不是“或”
在视图中正确配置 permission_classes
权限类必须以元组或列表形式赋值给视图的 permission_classes 属性,写成单个类(如 permission_classes = MyPermission)会导致 TypeError: 'type' object is not iterable。
参数差异与兼容性影响:
- 类名末尾加
Permission是约定,非强制,但避免用IsAdmin这类易与内置IsAdminUser混淆的名称 - 多个权限类按顺序执行,任一返回
False即拒绝(短路逻辑),适合组合「登录 + 角色 + 对象归属」校验 - 若需动态决定权限类(如根据 query param 切换规则),应重写视图的
get_permissions()方法并返回实例列表,而非类列表
示例:
class OrderDetailView(RetrieveUpdateDestroyAPIView):
permission_classes = (IsAuthenticated, IsOwnerOrReadOnly, CanEditOrderStatus)
调试权限失败时优先检查 request.user 和 request.auth
权限类里最常踩的坑是假设 request.user 一定存在或已认证。实际中,未登录用户的 request.user 是 AnonymousUser 实例,request.auth 为 None(TokenAuthentication 下)或空字符串(SessionAuthentication 下)。直接调用 user.is_staff 没问题,但若依赖 user.profile 等外键,需提前处理 RelatedObjectDoesNotExist。
实操建议:
- 在
has_permission开头加if not request.user or not request.user.is_authenticated:显式拦截 - 避免在权限类中做耗时操作(如查 Redis 或发 HTTP 请求),它会在每次请求都执行
- 用
logging.debug(f"Permission check: {request.user} -> {result}")打点,比断点更可靠(因权限检查可能在中间件之后、视图之前)
复杂点在于:权限逻辑一旦嵌套多层(比如先查角色,再查角色对应的资源范围,再比对当前对象 ID),就容易漏掉缓存或批量查询优化。别指望靠一个 has_object_permission 方法扛住所有业务规则。


















