我正在使用Django REST Framework来访问资源'user'。
由于用户信息是个人信息,我不希望GET请求列出系统中的每个用户,除非他们是管理员。
如果用户指定了他们的ID,并且他们已登录,我希望他们能够查看他们的详细信息并在需要时修改它们(PUT POST DELETE)。
总而言之,对于不是管理员的人,请禁用GET方法,并在查看其信息时允许登录用户使用GET POST DELETE PUT。
所以我创建了自定义权限类:
class UserPermissions(permissions.BasePermission):
"""
Owners of the object or admins can do anything.
Everyone else can do nothing.
"""
def has_permission(self, request, view):
# if admin: True otherwise False
def has_object_permission(self, request, view, obj):
# if request.user is the same user that is contained within the obj then allow
这不起作用。经过一些调试后,我发现它首先检查has_permission,然后检查has_object_permission。因此,如果我们没有超过第一个障碍GET / user /,那么它甚至不会考虑下一个GET / user / id。
总而言之,有没有人知道如何让它发挥作用?
谢谢:)
修改
我正在使用ModelViewSets,它确实存在我所描述的这个问题。
但是如果你将List功能与Detail分开,那么你可以给它们单独的权限类:
class UserList(generics.ListCreateAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes=(UserPermissionsAll,)
class UserDetail(generics.RetrieveUpdateDestroyAPIView):
queryset = User.objects.all()
serializer_class = UserSerializer
permission_classes=(UserPermissionsObj,)
class UserPermissionsAll(permissions.BasePermission):
"""
Owners of the object or admins can do anything.
Everyone else can do nothing.
"""
def has_permission(self, request, view):
if request.user.is_staff:
return True
else:
return False
class UserPermissionsObj(permissions.BasePermission):
"""
Owners of the object or admins can do anything.
Everyone else can do nothing.
"""
def has_object_permission(self, request, view, obj):
if request.user.is_staff:
return True
return obj == request.user
答案 0 :(得分:20)
我过去使用自定义权限执行此操作,并覆盖has_object_permission
,如下所示:
from rest_framework import permissions
class MyUserPermissions(permissions.BasePermission):
"""
Handles permissions for users. The basic rules are
- owner may GET, PUT, POST, DELETE
- nobody else can access
"""
def has_object_permission(self, request, view, obj):
# check if user is owner
return request.user == obj
您可以执行一些更详细的操作,例如拒绝特定请求类型(例如,允许对所有用户进行GET请求):
class MyUserPermissions(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# Allow get requests for all
if request.method == 'GET':
return True
return request.user == obj
然后在您的视图中,您告诉它使用权限类:
from my_custom_permissions import MyUserPermissions
class UserView(generics.ListCreateAPIView):
...
permission_classes = (MyUserPermissions, )
...
答案 1 :(得分:11)
我有类似的需求。让我们调用我的应用x
。这就是我想出来的。
首先,将其放在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
然后在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)
然后在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,)
顺便提一下,请注意您可以为这两个视图集使用 不同的序列化程序 ,这意味着您可以在list
视图中显示不同的属性在retrieve
视图中!例如:
# 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',)
然后在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)
...
我有点惊讶router
两次接受相同的模式,但它似乎确实有用。
警告lector:我已经通过API浏览器确认这一切都有效,但我还没有通过API尝试 更新 。
答案 2 :(得分:9)
对于绊脚石,对象级别权限限制下的documentation说:
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.
因此,详情视图可以使用,但对于列表,您需要filter针对当前用户。
答案 3 :(得分:7)
对于@ will-hart的答案还有一件事。
在DRF3文档中,
注意:只有在已经通过了视图级别的has_permission检查时才会调用实例级别的has_object_permission方法
因此,应指定 has_permission
以使用has_object_permission
。
from rest_framework import permissions
class MyUserPermissions(permissions.BasePermission):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
return request.user == obj
但是,当用户尝试获取用户列表时,上述代码将授予任何人权限。在这种情况下,最好根据action
提供权限,而不是HTTP method
。
from rest_framework import permissions
def has_permission(self, request, view):
if request.user.is_superuser:
return True
elif view.action == 'retrieve':
return True
else:
return False
def has_object_permission(self, request, view, obj):
if request.user.is_superuser:
return True
elif view.action == 'retrieve':
return obj == request.user or request.user.is_staff
答案 4 :(得分:1)
这是对重写has_object_permission()方法的澄清。使用复杂权限时,返回False将无法正常工作。 有关更多详细信息,请参阅此问题 https://github.com/encode/django-rest-framework/issues/7117