DRF自定义权限优化咨询:重复逻辑处理及关联对象权限检查
1. 优化重复的SAFE_METHODS检查逻辑
你的ImagePermission类里重复检查request.method in SAFE_METHODS,核心是has_permission和has_object_permission都需要处理安全请求的放行,但又不能随意移除某一处检查。最符合DRF最佳实践的优化方式是抽取公共辅助方法,既复用逻辑又保留必要的权限校验:
from rest_framework.permissions import BasePermission, SAFE_METHODS class ImagePermission(BasePermission): # 抽取安全方法判断为公共方法 def _is_safe_request(self, request): return request.method in SAFE_METHODS def has_permission(self, request, view): # 安全请求直接放行 if self._is_safe_request(request): return True # 非安全请求要求用户已认证 return request.user.is_authenticated def has_object_permission(self, request, view, obj): # 安全请求直接放行 if self._is_safe_request(request): return True # 管理员不可编辑内容(排除PUT/PATCH),其他非安全操作允许 if request.user.is_staff: return request.method not in ('PUT', 'PATCH') # 普通认证用户需是关联Publication的作者 return obj.publication.author == request.user
这种方式把重复的判断逻辑抽成私有方法,既避免代码冗余,又保证两处权限检查的逻辑一致性,不会出现移除任意一处就异常的问题。
2. 关联对象权限检查的标准做法
针对Image关联Publication的权限校验,DRF有几种通用方案:
方案一:直接在权限类中检查关联对象
像上面的代码那样,在has_object_permission里通过obj.publication获取关联的Publication,直接校验用户对该Publication的权限(比如是否为作者)。这种方式简单直接,适合关联关系不复杂的场景。
方案二:复用已有权限类
如果已经定义了PublicationPermission,可以在ImagePermission中复用它的逻辑,避免重复编写权限判断:
class ImagePermission(BasePermission): def __init__(self): self.pub_perm = PublicationPermission() def _is_safe_request(self, request): return request.method in SAFE_METHODS def has_permission(self, request, view): if self._is_safe_request(request): return True return request.user.is_authenticated def has_object_permission(self, request, view, obj): if self._is_safe_request(request): return True # 复用PublicationPermission的对象权限逻辑 return self.pub_perm.has_object_permission(request, view, obj.publication)
这样如果Publication的权限逻辑更新,Image的权限会自动同步,降低维护成本。
方案三:结合Django内置模型权限
如果项目依赖Django的模型权限系统(比如view_publication、change_publication),可以自定义DjangoObjectPermissions的权限映射,让Image的权限校验关联到Publication:
from rest_framework.permissions import DjangoObjectPermissions class ImageObjectPermissions(DjangoObjectPermissions): # 映射Image的请求方法到Publication的模型权限 perms_map = { 'GET': ['%(app_label)s.view_publication'], 'POST': ['%(app_label)s.add_publication'], 'PUT': ['%(app_label)s.change_publication'], 'PATCH': ['%(app_label)s.change_publication'], 'DELETE': ['%(app_label)s.delete_publication'], 'OPTIONS': [], 'HEAD': [], } def has_object_permission(self, request, view, obj): # 把校验对象替换为关联的Publication return super().has_object_permission(request, view, obj.publication)
这种方式扩展性强,适合需要统一管理模型权限的场景。
方案四:视图层提前过滤数据
在视图的get_queryset中先过滤出用户有权访问的Image(比如关联到用户作为作者的Publication),再配合权限类做最终校验:
from rest_framework.viewsets import ModelViewSet from .models import Image from .permissions import ImagePermission class ImageViewSet(ModelViewSet): queryset = Image.objects.all() permission_classes = [ImagePermission] def get_queryset(self): queryset = super().get_queryset() # 非管理员用户仅能看到自己作者的Publication关联的Image if not self.request.user.is_staff: queryset = queryset.filter(publication__author=self.request.user) return queryset
这种方式能减少不必要的数据库查询,提升性能,同时权限类只需要做最后的兜底校验。
内容的提问来源于stack exchange,提问作者aleksandr

