我希望用户只能访问属于他们的记录,而不能访问任何其他用户的记录,因此我创建了以下view
:
class AddressViewSet(viewsets.ModelViewSet):
authentication_classes = (TokenAuthentication,)
permission_classes = [IsAuthenticated, IsOwner]
queryset = Address.objects.all()
def retrieve(self, request, pk):
address = self.address_service.get_by_id(pk)
serializer = AddressSerializer(address)
return Response(serializer.data, status=status.HTTP_200_OK)
我只希望记录的所有者能够访问该视图中的所有方法,即检索、列表等(稍后我将实现其余方法(,因此我在core
应用程序中创建了以下permissions.py
文件:
class IsOwner(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
print('here in has_object_permission...')
return obj.user == request.user
这不起作用,所以在查看了stackoverflow的答案后,我发现这是Django Rest Framework的所有者权限,它指示必须实现has_permission
方法。但正如你在这个答案中看到的,它试图从view.kwargs
中获得id
,但我的view.kwargs
只包含pk
,而不包含用户。我该怎么解决这个问题?我需要在请求url中隐式传递用户id吗?听起来不对。
以下是我用来验证用户无法访问其他用户记录的测试:
def test_when_a_user_tries_to_access_another_users_address_then_an_error_is_returned(self):
user2 = UserFactory.create()
addresses = AddressFactory.create_batch(3, user=user2)
address_ids = [address.id for address in addresses]
random_address_id = random.choice(address_ids)
url = reverse(self.ADDRESSES_DETAIL_URL, args=(random_address_id,))
res = self.client.get(url, format='json')
print(res.data)
目前只使用测试来检查返回的数据,稍后将实现断言
编辑
所以我在IsOwner
:中添加了has_permission
方法
def has_permission(self, request, view):
return request.user and request.user.is_authenticated
如果我在这里放一个print语句,它就会被打印出来,但似乎没有达到has_object_permission
方法,我在那里添加的打印都没有显示
这个答案对我来说是正确的。
上面写着:
列表视图不调用has_object_permission。这个文件显示:
还要注意,通用视图将只检查检索单个模型实例的视图的对象级别权限。如果你需要列表视图的对象级筛选,则需要进行筛选单独设置查询集。有关详细信息,请参阅筛选文档详细信息。
文档链接
注意:只有在视图级别has_permission检查已经通过时,才会调用实例级别has_object_permission方法。
您还需要编写has_permission,以便使您的自定义权限发挥作用。
这是官方文档并提到了它。它应该在您添加has_permission后工作。
如文档中所述,对self.get_object
方法调用进行权限检查。
def get_object(self):
obj = get_object_or_404(self.get_queryset(), pk=self.kwargs["pk"])
self.check_object_permissions(self.request, obj)
return obj
这基本上是retrieve
方法在ModelViewSet
中所做的全部
def retrieve(self, request, *args, **kwargs):
instance = self.get_object()
serializer = self.get_serializer(instance)
return Response(serializer.data)
无论您在self.address_service.get_by_id(pk)
中做什么,都应该移动到self.get_object
或在retrieve
方法中调用self.check_object_permissions(self.request, obj)
。
在基本场景中,这就是您所需要的。不需要重写retrieve
方法。
class AddressViewSet(viewsets.ModelViewSet):
serializer_class = AddressSerializer
authentication_classes = (TokenAuthentication,)
permission_classes = [IsAuthenticated, IsOwner]
queryset = Address.objects.all()