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

Django Rest Framework自定义权限类参数传递异常求助

DRF自定义权限类参数错乱问题排查

问题现象

  • 自定义权限类的has_permission/has_object_permission方法参数完全错位:
    • self变成了request实例,而非权限类自身的实例
    • request参数变成了view实例
    • 必须给view参数设默认值None,否则会报参数缺失错误
  • 原本作为默认权限运行一个月的IsEmailVerifiedPermission一切正常,但通过编程方式添加UserCreated、CanGloballyEdit这两个新权限时,触发如下报错:
AttributeError: 'ExampleViewSet' object has no attribute 'user'

相关代码

settings.py

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'api.authentication.TokenAuthentication',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
        'api.permissions.IsEmailVerifiedPermission',
    ),
    . . .
}

permissions.py

from rest_framework.permissions import BasePermission

class IsEmailVerifiedPermission(BasePermission):
    def has_permission(self, request, view=None):
        return request.user.email_verified

    def has_object_permission(self, request, view=None, obj=None):
        return request.user.email_verified


class UserCreated(BasePermission):
    def has_permission(self, request, view=None):
        # 调试时发现参数错位:
        # self是request实例
        # request是view实例
        # 必须设view=None才能避免报错

    def has_object_permission(self, request, view=None, obj=None):
        # 存在同样的参数错位问题


class CanGloballyEdit(BasePermission):
    def has_permission(self, request, view=None):
        # 存在同样的参数错位问题

    def has_object_permission(self, request, view=None, obj=None):
        # 存在同样的参数错位问题

views/example.py

from rest_framework import viewsets
from .serializers import ExampleSerializer
from api.permissions import UserCreated, CanGloballyEdit

class ExampleViewSet(viewsets.GenericViewSet):

    serializer_class = ExampleSerializer
    
    def get_permissions(self):
        default_permissions = super(viewsets.GenericViewSet, self).get_permissions()
        if self.action == 'list':
            return [CanGloballyEdit] + default_permissions
        if self.action == 'retrieve':
            return [UserCreated] + default_permissions
        if self.action in ('update', 'partial_update'):
            return [UserCreated | CanGloballyEdit] + default_permissions
        return default_permissions

问题原因

你在get_permissions方法里返回的是权限类本身,而不是权限类的实例。DRF处理权限时会把这些类当成可调用对象直接执行,相当于直接调用CanGloballyEdit.has_permission(request, view)——此时Python会把第一个参数request绑定给方法的self参数,后续参数依次错位,最终导致你看到的异常。

解决方法

修改get_permissions方法,返回权限类的实例(给类名加())而非类本身:

def get_permissions(self):
    default_permissions = super(viewsets.GenericViewSet, self).get_permissions()
    if self.action == 'list':
        return [CanGloballyEdit()] + default_permissions
    if self.action == 'retrieve':
        return [UserCreated()] + default_permissions
    if self.action in ('update', 'partial_update'):
        return [UserCreated() | CanGloballyEdit()] + default_permissions
    return default_permissions

另外,权限类的方法可以恢复DRF的标准签名,无需给view或obj设默认值None,标准写法如下:

class UserCreated(BasePermission):
    def has_permission(self, request, view):
        # 正常使用self、request、view参数
        pass

    def has_object_permission(self, request, view, obj):
        # 正常使用self、request、view、obj参数
        pass

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 13:33:22