从 Django 休息框架中的多对多字段获取无类型值

问题描述

我无法在序列化程序列表的多对多字段中检索答案选择 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,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 帖子正文中:

    {
        "label": "Label q","explanation": "","answer": ["option 7","option 8"],"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 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,**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