【问题标题】:Django Rest Framework Permissions and OwnershipDjango Rest 框架权限和所有权
【发布时间】:2019-07-13 07:22:21
【问题描述】:

我有两个简单的模型

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
  • 用户应该只能创建/更新假期,只要他们是该对象的所有者(通过外键)

以面向未来的方式实现这一目标的最佳方式是什么。说如果再往下说:

  • 我添加了一个不同的用户类型,可以查看所有假期,但只能创建/更新/删除自己的
  • 我添加了另一个模型,用户可以读取,但不能写入

谢谢!

【问题讨论】:

    标签: python django django-rest-framework


    【解决方案1】:

    来自文档:

    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 有great documentation。我希望这可以帮助您入门,并帮助您根据未来的需求处理不同的用例。

    【讨论】:

      【解决方案2】:

      我建议你使用drf-viewsetslink。我们将使用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',)
      

      我们的意见.py

      这里我们将覆盖viewset's retrive 和 list 方法。还有其他可能的方法可以做到这一点,但我最喜欢这个,因为我可以看到代码中发生了什么。 Django 模型视图集继承了linkdrf-mixins 检索和列表方法。

      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)
      

      【讨论】:

        【解决方案3】:

        Django rest 框架为此提供了内置设置

        只需导入所需的权限并将其添加到您的类变量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 自己的假期?
        • 感谢您的加入。对于过滤部分,如果将​​来我有两种类型的用户:一种需要过滤,另一种不需要?
        • 将权限类添加到特定类。在你的情况下IsOwner class
        • 在这种情况下,您需要检查请求用户是否在可以访问视图的用户中。
        猜你喜欢
        • 2017-04-13
        • 2016-11-20
        • 2014-08-25
        • 2016-01-25
        • 1970-01-01
        • 2018-11-19
        • 2020-09-22
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多