Django REST Framework自定义权限方法多次重复调用问题排查
问题描述
开发简易博客应用时编写了自定义权限类,用于限制仅帖子所有者可更新或删除对应帖子。添加print语句校验权限运行状态时,发现每次请求访问对应视图,该权限方法会被调用6次,相关实现代码如下:
class PostUpdateDeletePermission(IsAuthenticated): def has_object_permission(self, request, view, obj): print("called") if request.user.is_superuser: return True if (request.method == 'PUT' or request.method == 'DELETE') and request.user != obj.user: return False return True class PostView(ModelViewSet): queryset = Post.objects.all() serializer_class = PostSerializer permission_classes = (PostUpdateDeletePermission,) def create(self, request): serializer = self.get_serializer(data=request.data) serializer.is_valid(raise_exception=True) obj = serializer.save(user=request.user) if request.data.get('files_length'): PostFile.objects.bulk_create([PostFile(img = request.data.get(f"file-{x}"), post=obj) for x in range(request.data.get('files_length'))]) return Response({'posts' : serializer.data}, status=201)
刷新DRF可浏览API页面时,控制台累计打印6次called日志,需要定位重复调用原因并给出解决方案。
重复调用原因
- 最主要的触发源是DRF自带的可浏览API渲染器(BrowsableAPIRenderer)。它在生成页面时,除了校验当前主请求的权限,还会逐个校验该接口支持的所有HTTP动作(PUT、PATCH、DELETE、POST、OPTIONS等)的权限,用来判断是否要在页面上渲染对应操作按钮、提交表单。每一次动作校验只要涉及对象级权限判断,就会触发一次
has_object_permission方法。 - 其次
ModelViewSet的list动作默认会对返回结果中的每一个帖子对象,单独执行一次对象级权限校验。如果当前列表页返回了多条Post数据,对应会触发多次方法调用,和可浏览API渲染触发的校验累加后,就会出现你看到的6次打印。 - 这个重复调用是可浏览API场景下的正常行为,不会出现在普通JSON接口请求中,也不代表你的权限逻辑本身有错误。
解决方案
- 生产环境不需要特殊处理,只需要在DRF配置中关闭可浏览API,仅保留JSON渲染器即可,多余的权限校验调用会自动消失,配置参考:
REST_FRAMEWORK = { 'DEFAULT_RENDERER_CLASSES': [ 'rest_framework.renderers.JSONRenderer', ] }
- 开发阶段如果觉得多余的打印影响调试,可以调整日志输出规则:在print语句前增加请求方法过滤,只打印PUT、DELETE这类你需要调试的写操作的校验记录,过滤掉GET、OPTIONS等可浏览API渲染触发的无关调用。
- 如果列表接口本身已经通过过滤queryset保证了返回数据的权限(比如只返回当前用户有权限访问的帖子),可以重写list动作逻辑,跳过逐对象的权限校验,减少不必要的性能损耗,但注意不要影响retrieve、update、destroy这类单对象操作的权限校验逻辑。
- 不要为了消除重复调用修改权限类的核心判断规则,你当前写的所有者校验逻辑本身是正确的,重复调用只是渲染层的额外触发,不会导致权限绕过问题。
内容的提问来源于stack exchange,提问作者mohamed naser
相关产品推荐
相关产品推荐

