DRF ModelViewSet中根据用户权限动态选择序列化器实现方案
Django ModelViewSet 按用户权限动态切换序列化器实现方案
问题核心
get_serializer_class方法执行时,DRF还未完成待操作对象的提取,因此无法直接拿到实例传入haveProfileOwnerRights做校验。针对不同接口场景可以通过路由参数取对象ID,结合动作类型判断即可。
具体实现
1. 修正权限类的漏判逻辑
你当前的权限类没有放行只读权限用户的GET类请求,先补上对应判断:
class IsOwnerOrHaveProfileViewEditOrViewRight(permissions.BasePermission): def has_object_permission(self, request, view, obj): if request.user.is_anonymous: return False if haveProfileOwnerRights(request, obj): return True if haveProfileViewEditRights(request): return True # 只读权限仅允许安全请求(GET/HEAD/OPTIONS) if haveProfileViewRights(request) and request.method in permissions.SAFE_METHODS: return True return False
2. 重写get_serializer_class方法实现动态选择
针对详情类接口(retrieve/update/partial_update/destroy),直接从路由kwargs中提取pk参数查询当前操作的对象实例,再做所有者判断;列表/创建场景不需要单对象判断,直接按角色权限返回对应序列化器即可:
from django.http import Http404 class UserProfileViewSet(viewsets.ModelViewSet): permission_classes = [ permissions.IsAuthenticated, IsOwnerOrHaveProfileViewEditOrViewRight ] queryset = UserProfile.objects.all() def get_serializer_class(self): # 最高优先级:拥有完全管控权限的用户,返回全量编辑序列化器 if haveProfileViewEditRights(self.request): return UserProfileViewEditSerializer # 单对象操作场景:从路由取pk查询实例,判断是否为资料所有者 target_pk = self.kwargs.get('pk') if target_pk is not None: try: target_profile = self.get_queryset().get(pk=target_pk) if haveProfileOwnerRights(self.request, target_profile): return UserProfileOwnerSerializer except UserProfile.DoesNotExist: raise Http404 # 创建场景:默认用户仅能编辑自身资料,返回所有者序列化器 if self.action == 'create': return UserProfileOwnerSerializer # 剩余场景为只读权限,返回只读序列化器 return UserProfileViewOnlySerializer
注意事项
- 上述代码中查询
target_profile不会产生重复数据库查询,后续DRF执行get_object()时会复用查询结果,无额外性能损耗 - 权限判断顺序不要随意调整:完全管控权限 > 所有者权限 > 只读权限,避免高权限用户拿到低权限序列化器
- 如果列表接口需要做数据过滤(比如普通用户仅能看到自己的资料),可以额外重写
get_queryset方法加过滤逻辑
内容的提问来源于stack exchange,提问作者Alessandro Mendolia
相关产品推荐
相关产品推荐

