Django REST framework object level permissions

前端 未结 5 1946
我寻月下人不归
我寻月下人不归 2021-02-01 03:46

I am using Django REST Framework to access a resource \'user\'.

As user information is personal, I do not want a GET request to list every user on the system, UNLESS the

5条回答
  •  时光说笑
    2021-02-01 04:00

    I have a similar need. Lets call my app x. Here's what I came up with.

    First, put this in x/viewsets.py:

    # viewsets.py
    from rest_framework import mixins, viewsets
    
    class DetailViewSet(
      mixins.CreateModelMixin,
      mixins.RetrieveModelMixin,
      mixins.UpdateModelMixin,
      mixins.DestroyModelMixin,
      viewsets.GenericViewSet):
        pass
    
    class ReadOnlyDetailViewSet(
      mixins.RetrieveModelMixin,
      viewsets.GenericViewSet):
        pass
    
    class ListViewSet(
      mixins.ListModelMixin,
      viewsets.GenericViewSet):
        pass
    

    Then in x/permissions.py:

    # permissions.py
    from rest_framework import permissions
    
    class UserIsOwnerOrAdmin(permissions.BasePermission):
        def has_permission(self, request, view):
            return request.user and request.user.is_authenticated()
    
        def check_object_permission(self, user, obj):
            return (user and user.is_authenticated() and
              (user.is_staff or obj == user))
    
        def has_object_permission(self, request, view, obj):
            return self.check_object_permission(request.user, obj)
    

    Then in x/views.py:

    # views.py
    from x.viewsets import DetailViewSet, ListViewSet
    from rest_framework import permissions
    
    class UserDetailViewSet(DetailViewSet):
        queryset = User.objects.all()
        serializer_class = UserDetailSerializer
        permission_classes = (UserIsOwnerOrAdmin,)
    
    class UserViewSet(ListViewSet):
        queryset = User.objects.all()
        serializer_class = UserSerializer
        permission_classes (permissions.IsAdminUser,)
    

    By the way, notice that you can use a different serializer for those two viewsets, which means you can show different attributes in the list view than in the retrieve view! For example:

    # serializers.py
    class UserSerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            model = User
            fields = ('username', 'url',)
    
    class UserDetailSerializer(serializers.HyperlinkedModelSerializer):
        class Meta:
            model = User
            fields = ('url', 'username', 'groups', 'profile', 'password',)
            write_only_fields = ('password',)
    

    Then in x/urls.py:

    # urls.py
    from x import views
    from rest_framework import routers
    
    router = routers.DefaultRouter()
    router.register(r'users', views.UserViewSet)
    router.register(r'users', views.UserDetailViewSet)
    
    ...
    

    I was mildly surprised that router accepted the same pattern twice, but it does appear to work.

    Caveat lector: I've confirmed this all works via the API browser, but I haven't tried updating via the API yet.

提交回复
热议问题