Django Rest Framework 权限和所有权

5

我有两个简单的模型

class User(AbstractUser): 
    pass


class Vacation(Model):
    id    = models.AutoField(primary_key=True)
    owner = models.ForeignKey(User, on_delete=models.CASCADE)

我不确定在Django Rest Framework中进行用户权限的可扩展方式。具体而言:
  • 用户只能查看自己的假期
  • /vacation端点上,用户将看到一个筛选后的列表
  • /vacation/$id端点上,如果不是所有者,用户将收到403
  • 只有作为对象所有者(通过外键)的用户才能创建/更新假期
在未来保持兼容性的最佳方法是什么?比如,如果将来发生以下情况:
  • 我添加了另一个用户类型,该用户可以查看所有假期,但只能创建/更新/删除自己的假期
  • 我添加了另一个模型,其中用户可以阅读,但无法编写
谢谢!
3个回答

7
根据文档:
REST框架中的权限总是定义为权限类列表。在运行视图的主体之前,将检查列表中的每个权限。如果任何权限检查失败,将引发exceptions.PermissionDenied或exceptions.NotAuthenticated异常,并且视图的主体将不会运行。
REST框架权限还支持对象级别的权限控制。对象级别权限用于确定用户是否应该被允许对特定对象进行操作,这通常是一个模型实例。
对于您当前的需求,您可以定义自己的Permission类:
class IsVacationOwner(permissions.BasePermission):
    # for view permission
    def has_permission(self, request, view):
        return request.user and request.user.is_authenticated

    # for object level permissions
    def has_object_permission(self, request, view, vacation_obj):
        return vacation_obj.owner.id == request.user.id

并将此权限添加到您的视图中。例如,在视图集上:

class VacationViewSet(viewsets.ModelViewSet):
    permission_classes = (IsVacationOwner,)

注意,由于您将使用过滤器列表响应'/vacations',请确保使用request.user进行过滤。因为对象级别权限不适用于列表。
为了性能原因,当返回对象列表时,通用视图不会自动将对象级别权限应用于查询集中的每个实例。
对于您未来的需求,您可以始终通过get_permissions方法有条件地设置权限。
class VacationViewSet(viewsets.ModelViewSet):
    def get_permissions(self):
        if self.action == 'list':
            # vacations can be seen by anyone
            # remember to remove the filter for list though
            permission_classes = [IsAuthenticated] 

            # or maybe that special type of user you mentioned
            # write a `IsSpecialUser` permission class first btw
            permission_classes = [IsSpecialUser] 
        else:
            permission_classes = [IsVacationOwner]

        return [permission() for permission in permission_classes]

DRF拥有优秀的文档。我希望这些可以帮助你入门,并根据你未来的需求帮助你处理不同的应用场景。


1

我建议您使用drf-viewsets链接。我们将使用vacation viewset来完成这项工作。

我们的urls.py

from your_app.views import VacationViewSet
router.register('api/vacations/', VacationViewSet)

我们的 serializers.py
from rest_framework import serializers
from your_app.models import Vacation

class VacationSerializer(serializers.ModelSerializer):

    class Meta:
        model = Vacation
        fields = ('id', 'owner',)
        read_only_fields = ('id',)

我们的views.py

在这里,我们将重写viewset的检索和列表方法。还有其他可能的方法可以做到这一点,但我最喜欢这种方法,因为我可以看到代码中正在发生什么。Django模型视图集继承了drf-mixins的检索和列表方法link

from rest_framework import viewsets, permissions, exceptions, status
from your_app.models import Vacation, User
from your_app.serializers import VacationSerializer 


class VacationViewSet(viewsets.ModelViewSet):
    queryset = Vacation.objects.all()
    permission_classes = [IsAuthenticated]
    serializer = VacationSerializer

    # we are going to overwrite list and retrive
    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())
        # now we are going to filter on user 
        queryset = queryset.filter(owner=self.request.user) 
        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        return Response(serializer.data)

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        # not permitted check
        if instance.owner is not self.request.user:
             raise exceptions.PermissionDenied()
        serializer = self.get_serializer(instance)
        return Response(serializer.data)

-1

Django rest framework 提供了内置的设置来实现这个功能

只需导入所需的权限并将其添加到您的类变量permission_classes中即可

my_name.api.views中实现

from rest_framework.permissions import ( AllowAny, IsAuthenticated, IsAdminUser, IsAuthenticatedOrReadOnly,)

class Vacation(ListAPIView):
    serializer_class = VacationListSerializer
    permission_classes = [IsAuthenticated]

您可以将多个权限类添加为列表

此外,如果这不是有帮助的,您始终可以按照以下方式过滤模型对象

Mymodel.objects.filter(owner = self.request.user)

谢谢您的回答,但那并没有回答我的问题。我该如何确保用户只能对自己的假期进行CRUD操作? - Andrei Cioara
谢谢您的补充。对于过滤部分,如果将来我有两种类型的用户:一种需要过滤,另一种不需要呢? - Andrei Cioara
将权限类添加到特定类中。在您的情况下,是 IsOwner 类。 - Maram Sreerama Reddy
在这种情况下,您需要检查请求用户是否是可以访问该视图的用户之一。 - Maram Sreerama Reddy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接