
本文详解为何自定义 AuthorOrReadOnly 权限类在 ViewSet 中未生效,并提供使用 ModelViewSet 与 perform_create 的标准解决方案,确保 PUT/PATCH/DELETE 等操作严格校验对象所有权。
本文详解为何自定义 `authororreadonly` 权限类在 `viewset` 中未生效,并提供使用 `modelviewset` 与 `perform_create` 的标准解决方案,确保 put/patch/delete 等操作严格校验对象所有权。
Django REST Framework 的对象级权限(如 has_object_permission)仅在视图方法明确调用对象实例时才会触发。问题中的 PostViewSet 继承自 viewsets.ViewSet(而非 ModelViewSet),其 update、partial_update 和 destroy 方法虽手动获取了 post = get_object_or_404(...),但未显式调用权限检查逻辑——DRF 不会自动为 ViewSet 的自定义方法执行 has_object_permission,除非你主动调用 self.check_object_permissions(request, obj)。
而 ModelViewSet 则不同:它已内置对 retrieve、update、partial_update 和 destroy 的完整权限链支持,在获取对象后自动调用 check_object_permissions,从而触发你的 AuthorOrReadOnly.has_object_permission 方法。因此,根本解法是升级为 ModelViewSet 并遵循其约定。
以下是推荐的重构方案:
from rest_framework import viewsets
from rest_framework.permissions import BasePermission, SAFE_METHODS
class AuthorOrReadOnly(BasePermission):
def has_object_permission(self, request, view, obj):
# 所有安全方法(GET、HEAD、OPTIONS)放行
if request.method in SAFE_METHODS:
return True
# 非安全方法要求用户必须是作者
return obj.author == request.user
class PostViewSet(viewsets.ModelViewSet):
permission_classes = (AuthorOrReadOnly,)
queryset = Post.objects.all()
serializer_class = PostSerializer
def perform_create(self, serializer):
# 正确设置 author 字段(避免在 serializer.validated_data 中直接赋值)
serializer.save(author=self.request.user)⚠️ 注意事项:
- 不要在 serializer.validated_data 中手动赋值(如原答案中 serializer.validated_data['author'] = ...),这会绕过序列化器字段验证和 save() 的钩子逻辑;应直接传参给 serializer.save(author=...)。
- ModelViewSet 自动处理 list、create、retrieve、update、partial_update、destroy 六大动作,无需重写方法——除非需定制逻辑(如 perform_create)。
- 若必须使用 ViewSet(如需高度定制),则每个涉及对象的操作都需显式添加权限检查:
def update(self, request, pk=None): post = get_object_or_404(self.queryset, pk=pk) self.check_object_permissions(request, post) # ← 关键! serializer = self.serializer_class(post, data=request.data) # ...后续逻辑
总结:权限失效的根本原因在于 ViewSet 的手动实现未激活 DRF 的对象权限机制。采用 ModelViewSet 是最简洁、安全且符合 DRF 设计哲学的实践方式,既能保证权限校验自动生效,又能大幅减少样板代码。


















