django rest框架has_object_permission提出PermissionDenied异常

时间:2014-01-01 11:32:58

标签: django django-rest-framework

我正在尝试为我的django rest框架视图构建一个权限类,该视图应该允许或禁止用户访问。

class UserPermissions(permissions.DjangoModelPermissions):
    """
    """
    def has_object_permission(self, request, view, obj):
        # if safe methods (get, retrieve) we check permissions from django perms
        if request.method in permissions.SAFE_METHODS:
            return super(UserPermissions, self).has_object_permission(request, view, obj)

        # else if methods (post, put, patch), we match user with obj and if obj is owner we grant him access
        return request.user == obj

上面的代码似乎工作正常,但我正在构建测试用例。我构建了一个案例,我尝试使用用户y凭证有意更新用户x

def test_patching_someones_else_profile(self):
    """ Test patching someone's else profile """
    response = self.c.post("/api/account/api-token-auth/", {'username': 'lion', 'password': 'password'})
    self.assertEqual(response.status_code, 200, "User couldn't log in")
    token = response.data['token']
    user_id = response.data['id']
    header = {'HTTP_AUTHORIZATION': 'Token {}'.format(token)}

    response = self.c.patch(reverse('user-detail', args=['1', ]), {'last_name': 'mooz'}, **header)

现在看到的问题,它突然抛出一个异常PermissionDenied(),它不会返回任何status_code来处理,而是应用程序崩溃并跟随异常

======================================================================
ERROR: test_patching_someones_else_profile (dlap.apps.account.tests.UserTestCase)
Test patching someone's else profile
----------------------------------------------------------------------
Traceback (most recent call last):
  File "/Users/mo/Projects/pythonic/dl-env/dlap/apps/account/tests.py", line 217, in test_patching_someones_else_profile
    response = self.c.patch(reverse('user-detail', args=['1', ]), {'last_name': 'mooz'}, **header)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/test.py", line 84, in patch
    return self.generic('PATCH', path, data, content_type, **extra)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/compat.py", line 487, in generic
    return self.request(**r)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/test.py", line 143, in request
    return super(APIClient, self).request(**kwargs)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/test.py", line 95, in request
    request = super(APIRequestFactory, self).request(**kwargs)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/django/test/client.py", line 444, in request
    six.reraise(*exc_info)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/django/core/handlers/base.py", line 139, in get_response
    response = response.render()
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/django/template/response.py", line 105, in render
    self.content = self.rendered_content
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/response.py", line 59, in rendered_content
    ret = renderer.render(self.data, media_type, context)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/renderers.py", line 577, in render
    context = self.get_context(data, accepted_media_type, renderer_context)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/renderers.py", line 537, in get_context
    raw_data_put_form = self.get_raw_data_form(view, 'PUT', request)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/renderers.py", line 475, in get_raw_data_form
    serializer = view.get_serializer(instance=obj)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/generics.py", line 99, in get_serializer
    serializer_class = self.get_serializer_class()
  File "/Users/mo/Projects/pythonic/dl-env/dlap/apps/account/views.py", line 96, in get_serializer_class
    obj = self.get_object()
  File "/Users/mo/Projects/pythonic/dl-env/dlap/apps/account/views.py", line 107, in get_object
    return super(UserViewSet, self).get_object(queryset=queryset)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/generics.py", line 321, in get_object
    self.check_object_permissions(self.request, obj)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/views.py", line 284, in check_object_permissions
    self.permission_denied(request)
  File "/Users/mo/Projects/pythonic/dl-env/lib/python2.7/site-packages/rest_framework/views.py", line 135, in permission_denied
    raise exceptions.PermissionDenied()
PermissionDenied

之前有人遇到过这个问题吗?有没有办法返回403与权限被拒绝消息没有应用程序崩溃?

更新

如下所述,get_seralizer_class()可能导致问题,这正是我面临此错误的原因。但是,我有什么替代方法来构建类似于下面的动态seralizer类方法?

def get_serializer_class(self):
    # decide on which seralizer to use
    if u'create_guest' in self.action_map.itervalues():
        return UserGuestSerializer

    if u'create_user' in self.action_map.itervalues():
        return PublicUserSerializer
    obj = self.get_object()
    if self.request.user.id == obj.id:
        if u'set_password' in self.action_map.itervalues():
            return PasswordSerializer

        return PrivateUserSerializer
    return PublicUserSerializer

1 个答案:

答案 0 :(得分:1)

您似乎正在使用由于某种原因调用.get_serializer_class()的自定义.get_object()方法。

File "/Users/mo/Projects/pythonic/dl-env/dlap/apps/account/views.py", line 96, in get_serializer_class
obj = self.get_object()

这导致在呈现Browsable API响应时重新运行每对象权限检查。是否有可能重新考虑您的get_serializer_class实施?

编辑:请注意,您可以简单地检查self.kargs ['pk'],而不是实际调用get_object。看起来您不需要执行完整的对象检索以进行所有权检查。