从 Django 休息框架中的 ManytoMany 字段获取 None 类型值

Getting None type value from ManytoMany Field in django rest framework

我无法在序列化程序列表的 ManytoMany 字段中检索答案选择 ID 或检索 相反,我得到了 quiz.Choice.None

我明白为什么会这样,但是如果我从序列化程序中删除 answer = serializers.CharField() 这一行。创建问题时引发错误:

{
    "answer": [
        "Incorrect type. Expected pk value, received str."
    ]
}

我无法从前端传递答案,我希望检索 "answer": [2,3] 而不是 None 并且我也知道如果我从序列化程序中删除 answer = serializers.CharField() 这一行。它解决了这个问题,但它提出了另一个问题,我无法传递一个甚至尚未创建的答案选择 ID。此类问题的最佳解决方案是什么?我还尝试对空数组进行答案验证。但这甚至不起作用。

{
        "id": 5,
        "label": "Question 1",
        "answer": "quiz.Choice.None",
        "explanation": "New Added fhfd",
        "mode": 1,
        "subtopic": 2,
        "choice": [
            {
                "id": 5,
                "option": "option 6 Edited New One",
                "question": 5
            },
            {
                "id": 6,
                "option": "option 5 Hllloo Sakib",
                "question": 5
            }
        ]
    }

我的模特:

    # Question Mode
    MODE = ((0, "Easy"), (1, "Medium"), (2, "Hard"))
    
    
    class Question(models.Model):
        """Question Model"""
    
        label = models.CharField(max_length=1024)
        answer = models.ManyToManyField("Choice", related_name="quesans", blank=True)
        explanation = models.TextField(blank=True)
        mode = models.IntegerField(choices=MODE, default=0)
        subtopic = models.ForeignKey(
            "motherset.Subtopic", on_delete=models.CASCADE, related_name="question"
        )
        created_time = models.DateTimeField(auto_now_add=True, blank=True)
    
        def __str__(self):
            return self.label
    
    
    class Choice(models.Model):
        """Question Option"""
    
        option = models.CharField(max_length=1024)
        question = models.ForeignKey(
            Question, on_delete=models.CASCADE, related_name="choice", blank=True, null=True
        )
    
        def __str__(self):
            return self.option

在 Serializers.py:

class ChoiceSerializer(serializers.ModelSerializer):
    """Serializers for Question Choice"""

    id = serializers.IntegerField()

    class Meta:
        model = Choice
        fields = ("id", "option", "question")


class CreateQuestionSerializer(serializers.ModelSerializer):

    choice = ChoiceSerializer(many=True, partial=True)
    answer = serializers.CharField()

    class Meta:
        model = Question
        fields = ("id", "label", "answer", "explanation", "mode", "subtopic", "choice")
        extra_kwargs = {"answer": {"validators": []}}

    def create(self, validated_data):
        """Create Model With nested Serializer"""

        choices_data = validated_data.pop("choice")
        answerString = validated_data.pop("answer", [])
        answer = literal_eval(answerString)

        print(answer)
        print(type(answer))
        question = Question.objects.create(**validated_data)
        for choice_data in choices_data:
            choice_created = Choice.objects.create(
                option=choice_data["option"], question=question
            )
            for choice_option in answer:
                if choice_option == choice_created.option:
                    question.answer.add(choice_created)
                    question.save()
        return question

    def update(self, instance, validated_data):
        """Update Instance including nested serializer"""
        choices_data = validated_data.pop("choice", None)

        if choices_data is not None:
            for choice_data in choices_data:
                choice = Choice.objects.get(pk=choice_data["id"])
                choice.option = choice_data["option"]
                choice.save()
        return super().update(instance, validated_data)

在 api.py 中:

class CreateQuestionView(viewsets.ModelViewSet):
    serializer_class = CreateQuestionSerializer
    queryset = Question.objects.all()

在 axios post 正文中:

    {
        "label": "Label q",
        "explanation": "",
        "answer": ["option 7","option 8"],
        "mode": 1,
        "subtopic": 2,
        "choice": [
            {
                "id":3324242,
                "option": "option 7"
            },
            {
                "id":3324245,
                "option": "option 8"
            },
            {
                "id":3324248,
                "option": "option 9"
            }
            ]
    }

answer 由其 pk 引用,而不是 option

像下面这样的调用应该有效,您可以在其中提交选择的 PK 而不是选项文本:

    {
        "label": "Label q",
        "explanation": "",
        "answer": [7, 8],
        "mode": 1,
        "subtopic": 2,
        "choice": [
            {
                "id":3324242,
                "option": "option 7"
            },
            {
                "id":3324245,
                "option": "option 8"
            },
            {
                "id":3324248,
                "option": "option 9"
            }
            ]
    }

更新

我将 Modelviewset class 更新为:

class CreateQuestionView(viewsets.ModelViewSet):
    serializer_class = CreateQuestionSerializer
    queryset = Question.objects.all()

    def retrieve(self, request, *args, **kwargs):
        instance = self.get_object()
        serializer = self.get_serializer(instance)
        answer = []
        for ans in instance.answer.all():
            answer.append(ans.id)
        newDict = {}
        newDict.update(serializer.data)
        newDict.update({"answer": answer})

        return Response(newDict)

    def list(self, request, *args, **kwargs):
        queryset = self.filter_queryset(self.get_queryset())

        page = self.paginate_queryset(queryset)
        if page is not None:
            serializer = self.get_serializer(page, many=True)
            return self.get_paginated_response(serializer.data)

        serializer = self.get_serializer(queryset, many=True)
        newList = []
        for question in serializer.data:
            q = Question.objects.get(pk=question["id"])

            answer = []
            for ans in q.answer.all():
                answer.append(ans.id)
            newDict = {}
            newDict.update(question)
            newDict.update({"answer": answer})
            newList.append(newDict)

        return Response(newList)

现在一切正常。感谢我自己。 :v