DRF: Сериализация поля с разными сериализаторами и many=True

У меня есть следующие модели в моем приложении django rest framework:

class Question(models.Model):
    class Type(models.TextChoices):
        TEXT = 'text', 'text question'
        RADIO = 'radio', 'choosing one option quesiton'
        CHOICE = 'check', 'choosing multiple options question'
    
    type = models.CharField(max_length=5, choices=Type.choices)
    title = models.CharField()
    description = models.TextField(blank=True, default='')
    
    def __str__(self) -> str:
        return f'{self.type}: {self.title}'

class TextQuestion(Question):
    answer = models.CharField()

class RadioQuestion(Question):
    variants = fields.ArrayField(models.CharField())
    answer = models.CharField()

class ChoiceQuestion(Question):
    variants = fields.ArrayField(models.CharField())
    answers = fields.ArrayField(models.CharField())

class Test(models.Model):
    name = models.CharField()
    creator = models.ForeignKey(User, on_delete=models.CASCADE)
    questions = models.ManyToManyField(Question)```

Я хочу написать сериализатор для сериализации тестов, но я не знаю, как сериализовать поле вопросов. Потому что у меня есть разные сериализаторы для каждого типа вопросов: Что мне делать? Может быть, лучше как-то реорганизовать мои модели?

Я уже написал сериализаторы для всех типов вопросов:

class TextQuestionSerializer(serializers.ModelSerializer):
    class Meta:
        model = TextQuestion
        fields = '__all__'

class RadioQuestionSerializer(serializers.ModelSerializer):
    class Meta:
        model = RadioQuestion
        fields = '__all__'

class ChoiceQuestionSerializer(serializers.ModelSerializer):
    class Meta:
        model = ChoiceQuestion
        fields = '__all__'

Я думал использовать serializer.SerializerMethodField(), но это не работает с many=True

У меня была такая проблема, можно использовать serializer.SerializerMethodField() и написать сериализатор для теста с isinstance(). Например:

class TestSerializer(serializers.ModelSerializer):
questions = serializers.SerializerMethodField()

class Meta:
    model = Test
    fields = ['id', 'name', 'creator', 'questions']

def get_questions(self, obj):
    questions = obj.questions.all()
    serialized_questions = []
    for question in questions:
        if isinstance(question, TextQuestion):
            serialized_question = TextQuestionSerializer(question).data
            #continiue other tasks
        serialized_questions.append(serialized_question)
    return serialized_questions

Вы можете проверить это, и если это не сработает, дайте мне знать:)

Большое спасибо parsarezaee, я наконец-то решил свою проблему. Чтобы заставить этот код работать, мне пришлось написать def to_representation(self, instance: Test):. Вот как выглядит окончательное решение:

class TestSerializer(serializers.ModelSerializer):
    questions = serializers.ListField(child=QuestionSerializer())

    class Meta:
        model = Test
        fields = '__all__'

    def create(self, validated_data):
        questions_data = validated_data.pop('questions')
        test = Test.objects.create(**validated_data)
        for question_data in questions_data:
            question_type = question_data['type']
            if question_type == 'text':
                created_question = TextQuestion.objects.create(**question_data)
            elif question_type == 'radio':
                created_question = RadioQuestion.objects.create(**question_data)
            created_question.save()
            test.questions.add(created_question)
        return test

    def update(self, instance, validated_data):
        instance.name = validated_data.get('name', instance.name)
        instance.description = validated_data.get('description', instance.description)
        instance.save()

        questions_data = validated_data.get('questions', [])
        for question_data in questions_data:
            question_type = question_data.pop('type')
            question_id = question_data.pop('id', None)
            if question_id:
                if question_type == 'text':
                    question = TextQuestion.objects.get(id=question_id)
                elif question_type == 'radio':
                    question = RadioQuestion.objects.get(id=question_id)
                for key, value in question_data.items():
                    setattr(question, key, value)
                question.save()
        return instance

    def to_representation(self, instance: Test):
        ret = OrderedDict()
        ret['id'] = instance.id
        ret['name'] = instance.name
        ret['creator'] = instance.creator.id
        ret['questions'] = []
        for question in instance.questions.all():
            qrepr = OrderedDict()
            qrepr['id'] = question.id
            qrepr['title'] = question.title
            qrepr['description'] = question.description
            qrepr['type'] = question.type
            if question.type == 'text':
                qrepr["answer"] = question.textquestion.answer
            elif question.type == 'radio':
                qrepr["answer"] = question.radioquestion.answer
                qrepr["variants"] = question.radioquestion.variants
            ret['questions'].append(qrepr)
        return ret
Вернуться на верх