【问题标题】:DRF, Permissions and Tests: Receiving a 403 running tests, 200 OK in browserDRF,权限和测试:在浏览器中接收 403 运行测试,200 OK
【发布时间】:2019-11-07 09:56:23
【问题描述】:

随着我添加新视图和更多功能,我将继续测试我的 Django Rest Framework 应用程序。我必须承认,在这个阶段,我发现测试比实际编码和构建我的应用程序更难。我觉得测试 DRF 可用的资源比谈论使用 DRF 构建 REST 框架的资源少得多。不过,C'est la vie,我继续战斗。

我目前面临的问题是在测试我的一个 DRF 视图集时收到 403 错误。我可以确认视图及其权限在使用浏览器或常规 python 脚本访问端点时工作正常。

让我们从我的 ViewSet 中使用的模型开始

class QuizTracking(models.Model):
    case_attempt = models.ForeignKey(CaseAttempt, on_delete=models.CASCADE)
    user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
    answer = models.ForeignKey(Answer, on_delete=models.CASCADE)
    timestamp = models.DateTimeField(auto_now_add=True)

这里需要注意的是,用户有一个 FK。这在确定权限时使用。

这是我的测试功能。为简洁起见,我没有包含整个类的代码。

    def test_question_retrieve(self):
        """
            Check that quiz tracking/ID returns a 200 OK and the content is correct
        """
        jim = User(username='jimmy', password='monkey123', email='jimmy@jim.com')
        jim.save()
        quiz_tracking = QuizTracking(answer=self.answer, case_attempt=self.case_attempt, user=jim)
        quiz_tracking.save()
        request = self.factory.get(f'/api/v1/progress/quiz-tracking/{quiz_tracking.id}')

        # How do I refernce my custom permission using Permission.objects.get() ?
        # permission = Permission.objects.get()
        # jim.user_permissions.add(permission)

        self.test_user.refresh_from_db()
        force_authenticate(request, user=jim)
        response = self.quiz_detail_view(request, pk=quiz_tracking.id)
        print(response.data)
        print(jim.id)
        print(quiz_tracking.user.id)

        self.assertContains(response, 'answer')
        self.assertEqual(response.status_code, status.HTTP_200_OK)

在上面的代码中,我定义了一个用户jim 和一个quiz_tracking 拥有的jim 对象。

我构建我的请求,force_authenticate 请求并执行我的请求并将响应存储在 response

这里需要注意的有趣的事情是: - jim.id 和 quiz_tracking.user.id 是相同的值 - 我收到 403 响应,带有

{'detail': ErrorDetail(string='You do not have permission to perform this action.', code='permission_denied')}

你可能已经注意到我已经注释掉了permission = Permission.objects.get() 我的理解是我需要通过这个我的权限类,在我的例子中是IsUser。但是,我的数据库中没有这方面的记录,因此 Permission.objects.get('IsUSer') 调用失败。

所以我的问题如下: - 如何验证我的请求以便收到 200 OK? - 我是否需要在我的测试中为我的用户添加权限,如果需要,需要哪些权限以及使用什么语法?

下面是我的视图,下面是我定义IsUser的自定义权限文件

class QuickTrackingViewSet(viewsets.ModelViewSet):
    serializer_class = QuizTrackingSerializser

    def get_queryset(self):
        return QuizTracking.objects.all().filter(user=self.request.user)

    def get_permissions(self):
        if self.action == 'list':
            self.permission_classes = [IsUser, ]
        elif self.action == 'retrieve':
            self.permission_classes = [IsUser, ]

        return super(self.__class__, self).get_permissions()

注意如果我注释掉def get_permissions(),那么我的测试就可以通过了。

我的自定义permission.py

from rest_framework.permissions import BasePermission


class IsSuperUser(BasePermission):

    def has_permission(self, request, view):
        return request.user and request.user.is_superuser


class IsUser(BasePermission):

    def has_object_permission(self, request, view, obj):
        if request.user:
            if request.user.is_superuser:
                return True
            else:
                return obj == request.user
        else:
            return False

干杯,

C

【问题讨论】:

    标签: django python-3.x django-rest-framework


    【解决方案1】:

    看来问题是你的权限IsUser

    您将QuizTracking 实例与用户实例进行比较。

    改变这一行

    return obj == request.user
    

    return obj.user.id == request.user.id
    

    离题建议

    您可以使用以下更易于阅读的方式编写IsUser

    class IsUser(BasePermission):
    
        def has_permission(self, request, view):
            return request.user and request.user.is_authenticated
    
        def has_object_permission(self, request, view, obj):
            return request.user.is_superuser or obj.user.id == request.user.id
    

    请注意,has_object_permission 仅在视图级别 has_permission 返回 True 时调用。

    【讨论】:

    • @anowlinorbit 不用担心。添加了一条“codereview”评论:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-07-14
    • 2014-11-08
    • 2018-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多