2

I am working on a Quiz Application using DjangoRestFramework and ReactJS. My App is comprised of two apps, api and frontend. In my api app, I have multiple API views for many different things. One of my API Views is called JoinQuiz. When I call it on my frontend, I get this error in the console:

Forbidden: /api/join-quiz
[16/Dec/2020] "POST /api/join-quiz HTTP/1.1" 403 58

I don't think my problem is due to a CSRF error because my other API views are working perfectly fine. I may be wrong on this point.

[16/Dec/2020] "GET /api/get-question?id=3 HTTP/1.1" 200 10009 
[17/Dec/2020 01:15:47] "POST /api/create-quiz HTTP/1.1" 201 11959

I suspect that my request.session may be the issue because when I go directly to /api/join-quiz and make a POST request with my code, nothing goes wrong and I have a successful post.

Files

Views.py

class QuizView(generics.ListAPIView):
    queryset = Quiz.objects.all()
    serializer_class = QuizSerializer

class GetQuiz(APIView):
    """ Searches for a quiz given its code and returns the Quiz with is_host info"""
    serializer_class = QuizSerializer
    lookup_url_kwarg = 'code'

    def get(self, request, format=None): # This is an HTTP GET request
        code = request.GET.get(self.lookup_url_kwarg)
        if code != None: # Check if code is not equal to None
            quiz = Quiz.objects.filter(code=code)
            if len(quiz) > 0: # If there is a quiz...
                data = QuizSerializer(quiz[0]).data
                data['is_host'] = self.request.session.session_key == quiz[0].host
                

                return Response(data, status=status.HTTP_200_OK)

            return Response({'Quiz not found': 'Invalid Code'}, status=status.HTTP_404_NOT_FOUND)
        return Response({'Bad Request': 'Code Parameter not found in request'}, status=status.HTTP_400_BAD_REQUEST)
        

        
class CreateQuizView(APIView):
    """Creates A new Quiz given nested question and answer data"""
    serializer_class = QuizSerializer

    def post(self, request, format=None):
        """ Create the User's Account first"""
        if not self.request.session.exists(self.request.session.session_key):
            self.request.session.create()
        
        data = request.data
        data['host'] = self.request.session.session_key
        serializer = self.serializer_class(data=data)

        if serializer.is_valid():
            
            quiz = serializer.create(validated_data=data)
            self.request.session['quiz_code'] = quiz.code

            return Response(
                self.serializer_class(quiz).data, 
                status=status.HTTP_201_CREATED
                )


        return Response({'Bad Request': 'Invalid Data'}, status=status.HTTP_400_BAD_REQUEST)

        
class JoinQuiz(APIView):
    """Join a quiz based on the quiz code"""
    lookup_url_kwarg = 'code'

    def post(self, request, format=None):
        if not self.request.session.exists(self.request.session.session_key):
            self.request.session.create()

        print(self.request.session.session_key)
        code = request.data.get(self.lookup_url_kwarg)

        if code != None:
            quiz_result = Quiz.objects.filter(code=code)
            if len(quiz_result) > 0:

                self.request.session['quiz_code'] = code
                return Response({'message': 'Quiz Joined!'}, status=status.HTTP_200_OK)
            
            return Response({'Quiz Not Found': 'Invalid Quiz Code'}, status=status.HTTP_404_NOT_FOUND)
        
        return Response({'Bad Request': 'Invalid Post Data'}, status=status.HTTP_400_BAD_REQUEST)
        

serializers.py




class QuizSerializer(serializers.ModelSerializer):
    
    questions = serializers.PrimaryKeyRelatedField(many=True, read_only=True)

    class Meta:
        model = Quiz
        fields = ['id', 'code', 'questions', 'name']

    def create(self, validated_data):
        questions_data = validated_data.pop('questions')

        quiz = Quiz.objects.create(**validated_data)

        for question_data in questions_data:
            
            answers_data = question_data.pop('answers')
            question = Question.objects.create(quiz=quiz, **question_data)

            for answer_data in answers_data: 
                Answer.objects.create(question=question, **answer_data)

        return quiz

Frontend Request

  const quizButtonPressed = () => {
      const requestOptions = {
          method: "POST",
          headers: {"Content-Type": "application/json"},
          body: JSON.stringify({
              code: quizCode
          })
      };
      fetch('/api/join-quiz', requestOptions)
      .then((response) => {
          if (response.ok) {
              props.history.push(`/play/${quizCode}`);
          } else {
              setError("Quiz not found")
          }        
      })
      .catch((error) => console.log(error));
  }

EDIT

I followed the solution from @Blusky and it worked!

Following the Django docs link from @Blusky solved the problem: https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax

1 Answer 1

1

Even though you checked it, it might be a CSRF problem. You should check the body of the 403 Error, it might contains further information.

Only when authenticated, POST request on Django requires a CSRF token (it might be why your first POST is working)

If it's the case, you can check this snippet: https://docs.djangoproject.com/en/3.1/ref/csrf/#ajax

Sign up to request clarification or add additional context in comments.

1 Comment

It worked! Thank you so much, I had just assumed that CSRF was not related to my problem because it was working for my other request. The Django docs link were so helpful in implenting the CSRF token.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.