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

Django REST Framework多或逻辑自定义权限消息不显示问题

问题分析与解决方案

这问题我之前在DRF开发中也踩过坑,核心原因是DRF通过位运算符|组合权限类时,生成的OrPermission组合类不会正确传递子权限类的自定义错误消息。

为什么单独用CanDogsRetrieveObject时自定义消息正常?

当你单独使用这个权限类时,DRF会直接调用它的has_permission和has_object_permission方法。如果权限被拒绝,DRF会读取这个类本身定义的message属性,返回你设置的"Some custom message.",逻辑直接清晰,不会有消息丢失的问题。

为什么组合使用时不显示自定义消息?

当你用IsUserCat | CanDogsRetrieveObject时,DRF会自动创建一个OrPermission实例来包装这两个权限类。这个组合类的权限检查逻辑是:

  • 先检查第一个权限类IsUserCat,如果通过则直接允许访问;
  • 如果IsUserCat拒绝,再检查第二个权限类CanDogsRetrieveObject;
  • 如果两个都拒绝,OrPermission会返回DRF默认的权限拒绝消息,而不是CanDogsRetrieveObject的自定义message——因为组合类本身没有继承或传递子权限类的错误消息属性,相当于把自定义消息给"吞"了。

解决方法

方法1:自定义组合权限类,传递错误消息

你可以自己写一个OrPermission类,让它在权限被拒绝时,主动返回最后一个被拒绝的子权限类的错误消息:

from rest_framework.permissions import OrPermission

class CustomOrPermission(OrPermission):
    def has_permission(self, request, view):
        for permission in self.permissions:
            if permission.has_permission(request, view):
                return True
        # 所有权限都拒绝,绑定最后一个权限的自定义消息
        self.message = self.permissions[-1].message
        return False

    def has_object_permission(self, request, view, obj):
        for permission in self.permissions:
            if permission.has_object_permission(request, view, obj):
                return True
        # 对象权限拒绝时同样绑定最后一个权限的消息
        self.message = self.permissions[-1].message
        return False

然后在视图中这样使用:

permission_classes = (CustomOrPermission(IsUserCat, CanDogsRetrieveObject),)

方法2:合并逻辑到一个自定义权限类

把两个权限的逻辑合并成一个独立的权限类,这样可以完全控制错误消息的返回时机和内容:

from rest_framework.permissions import IsAuthenticated
from django.utils import timezone

class CatOrValidDogPermission(IsAuthenticated):
    def has_permission(self, request, view):
        if not super().has_permission(request, view):
            return False
        # 猫用户直接通过权限检查
        if request.user.species == SPECIES_CAT:
            return True
        # 仅允许狗用户进入后续对象权限检查
        return request.user.species == SPECIES_DOG

    def has_object_permission(self, request, view, obj):
        if not super().has_object_permission(request, view, obj):
            return False
        # 猫用户直接通过对象权限
        if request.user.species == SPECIES_CAT:
            return True
        # 狗用户检查对象是否过期,过期则返回自定义消息
        if obj.expiry_date >= timezone.now().date():
            return True
        self.message = "Some custom message."
        return False

然后视图中使用:

permission_classes = (CatOrValidDogPermission,)

方法3:重写视图的permission_denied方法

在你的RetrieveUpdateDestroyAPIView子类中,重写permission_denied方法,手动遍历权限类获取正确的错误消息:

from rest_framework.views import RetrieveUpdateDestroyAPIView

class YourObjectView(RetrieveUpdateDestroyAPIView):
    permission_classes = (IsUserCat | CanDogsRetrieveObject,)
    # 其他视图配置(queryset、serializer_class等)...

    def permission_denied(self, request, message=None, code=None):
        # 遍历所有权限类,提取最后一个带自定义消息的权限的内容
        for permission in self.get_permissions():
            if hasattr(permission, 'message'):
                message = permission.message
        # 调用父类方法返回正确的错误消息
        super().permission_denied(request, message=message, code=code)

这样就能确保当CanDogsRetrieveObject因为对象过期拒绝访问时,它的自定义消息会被正确返回。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.14 08:50:31