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