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
相关产品推荐
相关产品推荐

