Hi All,
I’m continuing my journey of testing my Django Rest Framework application as I add new views and more functionality. I must admit, at this stage, I’m finding testing harder than actually coding and building my app. I feel that there are far fewer resources on testing DRF available than there are resources talking about building a REST framework with DRF. C’est la vie, though, I soldier on.
My issue that I’m currently facing is that I’m receiving a 403 error when testing one of my DRF ViewSets. I can confirm that the view, and its permissions work fine when using the browser or a regular python script to access the endpoint.
Let’s start with the model that is used in my 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)
Of note here is that there is a FK to a user. This is used when determining permissions.
Here is my test function. I have not included code for the entire class for the sake of brevity.
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)
In the above code, I define a user, jim
and a quiz_tracking
object owned by jim
.
I build my request, force_authenticate
the requst and the execute my request and store the response in response
.
The interesting things to note here are:
- jim.id and quiz_tracking.user.id are the same value
- I receive a 403 response with
{'detail': ErrorDetail(string='You do not have permission to perform this action.', code='permission_denied')}
You may have noticed that I have commented out permission = Permission.objects.get()
My understanding is that I need to pass this my Permission Class, which in my case is IsUser
. However, there is no record of this in my DB and hence Permission.objects.get('IsUSer')
call fails.
So my questions are as follows:
- How do I authenticate my request so that I receive a 200 OK?
- Do I need to add a permission to my user in my tests, and if so, which permission and with what syntax?
Below is my view and below that is my custom permission file defining 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()
N.B. If I comment out def get_permissions()
, then my test passes without problem.
My custom 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
Cheers,
C