简体   繁体   English

Django Rest框架的权限和所有权

[英]Django Rest Framework Permissions and Ownership

I have two simple models 我有两个简单的模型

class User(AbstractUser): 
    pass


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

I am not really sure what is the scalable way of doing user permissions for Django Rest Framework. 我不太确定对Django Rest Framework执行用户权限的可伸缩方式是什么。 In particular: 尤其是:

  • Users should only be able to see their own vacations 用户只能看到自己的假期
  • On the /vacation endpoint, user would see a filtered list /vacation端点上,用户将看到一个过滤列表
  • On the /vacation/$id endpoint, user would get a 403 if not owner /vacation/$id端点上,如果不是所有者,则用户将获得403
  • Users should only be able to Create/Update vacations as long as they are the owners of that object (through Foreign Key) 只要用户是该对象的所有者(通过外键),他们就只能创建/更新休假

What is the best way to achieve this in a future-proof fashion. 以面向未来的方式实现此目标的最佳方法是什么。 Say if further down the line: 说是否更深入:

  • I add a different user type, which can view all vacations, but can only create/update/delete their own 我添加了其他用户类型,该用户类型可以查看所有假期,但只能创建/更新/删除自己的假期
  • I add another model, where users can read, but cannot write 我添加了另一个模型,用户可以在其中读取但不能写入

Thank you! 谢谢!

From the docs: 从文档:

Permissions in REST framework are always defined as a list of permission classes. REST框架中的权限始终定义为权限类列表。 Before running the main body of the view each permission in the list is checked. 在运行视图主体之前,将检查列表中的每个权限。 If any permission check fails an exceptions.PermissionDenied or exceptions.NotAuthenticated exception will be raised, and the main body of the view will not run. 如果任何权限检查失败,则将引发exceptions.PermissionDenied或exceptions.NotAuthenticated异常,并且视图主体将不运行。

REST framework permissions also support object-level permissioning. REST框架权限还支持对象级权限。 Object level permissions are used to determine if a user should be allowed to act on a particular object, which will typically be a model instance. 对象级别权限用于确定是否应允许用户对特定对象执行操作,该对象通常是模型实例。

For your current need you can define your own Permission class: 根据您当前的需要,您可以定义自己的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

And add this permission to your view. 并将此权限添加到您的视图。 For example on a viewset: 例如,在视图集上:

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

One thing is important to notice here, since you will respond with a filtered list for '/vacations' , make sure you filter them using the request.user . 这里要注意一件事,因为您将使用'/vacations'的过滤列表进行响应,因此请确保使用request.user对其进行过滤。 Because object level permission will not be applicable for lists. 因为对象级别权限将不适用于列表。

For performance reasons the generic views will not automatically apply object level permissions to each instance in a queryset when returning a list of objects. 出于性能原因,返回对象列表时,通用视图不会自动将对象级别权限应用于查询集中的每个实例。

For your future requirement, you can always set the permissions conditionally with the help of get_permissions method. 为了将来的需求,您始终可以在get_permissions方法的帮助下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 has great documentation . DRF具有出色的文档 I hope this helps you to get started and helps you to approach different use cases according to your future needs. 我希望这可以帮助您入门,并可以帮助您根据将来的需求使用不同的用例。

I would suggest you to use drf-viewsets link . 我建议您使用drf-viewsets link We are going to use vacation viewset to do this work. 我们将使用vacation viewset来完成这项工作。

our urls.py 我们的urls.py

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

our serializers.py 我们的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',)

our views.py 我们的views.py

Here we are going to overwrite viewset's retrive and list method. 在这里,我们将覆盖viewset's和list方法。 There are other possible way to do that but i like this most as i can able to see what is happening in code. 还有其他可能的方法,但是我最喜欢这种方法,因为我能够看到代码中正在发生的事情。 Django model viewset inherited link of drf-mixins retrive and list method. 视图集中继承的Django模型链接drf-mixins retrive和列表的方法。

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)

Django rest framework provides in-build settings for this Django rest框架为此提供了内置设置

Just import the required permission and add it to you class variable permission_classes 只需导入所需的权限并将其添加到您的类变量permission_classes中

in my_name.api.views my_name.api.views中

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

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

You can add multiple permission classes as a list 您可以将多个权限类别添加为列表

Furthur, in case this is not helpful, you can always filter the model objects as 此外,如果这样做没有帮助,您可以始终将模型对象过滤为

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM