我正在实现一个API,其中我有嵌套的结构。
假设这是一个动物园,我可以调用GET /api/cage/
来获取笼子的列表GET /api/cage/1/
来获取笼子ID 1,但我可以使用GET /api/cage/1/animals/
来获取那个笼子里的动物列表。
我遇到的问题是权限问题。只有当我能看到笼子本身的时候,我才能看到笼子里的动物。如果has_object_permission()
在相关权限类中返回True
,我应该能够看到框架本身。
出于某种原因,当我获取/api/cage/1/
时,会调用has_object_permission()
,但当我调用GET /api/cage/1/animals/
时,has_permission()
会被调用。使用has_permission()
,我无法访问该对象以检查权限。我是不是错过了什么?我该怎么做?
我的笼子视图集看起来或多或少像这个
class CageViewSet(ModelViewSet):
queryset = Cage.objects.all()
serializer_class = CageSerializer
permission_classes = [GeneralZooPermissions, ]
authentication_classes = [ZooTicketCheck, ]
def get_queryset(self):
... code to only list cages you have permission to see ...
@detail_route(methods=['GET'])
def animals(self, request, pk=None):
return Request(AnimalSerializer(Animal.objects.filter(cage_id=pk), many=True).data)
我的GeneralZooPermissions
类(目前)是这样的
class GeneralZooPermissions(BasePermission):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
return request.user.has_perm('view_cage', obj)
这似乎是DRF中的一个错误。详细的路由不会调用正确的权限检查。我试着向DRF开发人员报告这个问题,但我的报告似乎已经消失了。不知道下一步该做什么。想法?
我与DRF发布的问题又回来了,我得到了回应。似乎只检查has_permission()
而不检查has_object_permission()
是预期行为。这对我没有帮助。在这一点上,必须做这样的事情:
class CustomPermission(BasePermission):
def has_permission(self, request, view):
"""we need to do all permission checking here, since has_object_permission() is not guaranteed to be called"""
if 'pk' in view.kwargs and view.kwargs['pk']:
obj = view.get_queryset()[0]
# check object permissions here
else:
# check model permissions here
def has_object_permission(self, request, view, obj):
""" nothing to do here, we already checked everything """
return True
好吧,所以在阅读了一堆DRF的代码并在DRF GitHub页面上发布了一个问题之后。
似乎只有当视图调用get_object()
来检索要操作的对象时,才会调用has_object_permission()
这是有意义的,因为无论如何都需要检索对象来检查权限,如果他们透明地这样做,就会添加额外的数据库查询。
回复我报告的人说,他们需要更新文件以反映这一点。所以,这个想法是,如果你想写一个自定义的详细路线,并让它正确检查权限,你需要做
class MyViewSet(ModelViewSet):
queryset = MyModel.objects.all()
....
permission_classes = (MyCustomPermissions, )
@detail_route(methods=['GET', ])
def custom(self, request, pk=None):
my_obj = self.get_object() # do this and your permissions shall be checked
return Response('whatever')
在我的情况下,我没有正确处理请求,所以我的URL是api/account/users
,我的错误是我将前端的URL设置为api/account/
,这是不正确的!
如果您想在执行另一个不调用get_object()
的方法(例如POST
方法)时定义权限,可以重写has_permission方法。也许这个答案会有所帮助(https://stackoverflow.com/a/52783914/12737833)
您可以做的另一件事是在POST
方法中使用check_object_permissions
,这样您就可以调用has_object_permission
方法:
@action(detail=True, methods=["POST"])
def cool_post(self, request, pk=None, *args, **kwargs):
your_obj = self.get_object()
self.check_object_permissions(request, your_obj)