Joshua
Joshua

Reputation: 197

DRF: Serializing one to many

Trying to print all the answers to a question when serializing the question but having difficulty telling django how to lookup the parent.

Target is to return something like this:

[
  {
    uuid: 1,
    question: 'Question 1',
    answers: [
      {'uuid': 1, 'answer': "A"},
      {'uuid': 2, 'answer': "B"},
      {'uuid': 3, 'answer': "C"},
    ],
  },
]

models.py

class Question(models.Model):
    uuid = ShortUUIDField(unique=True, blank=False, editable=False, default=shortuuid.uuid())
    question = models.CharField(max_length=140)
    creator = models.ForeignKey(CustomUser, on_delete=models.CASCADE, blank=True, null=True)
    def __str__(self):
        return '{}'.format(self.question)
    class Meta:
        indexes = [
            models.Index(fields=['uuid']),
        ]

class Answer(models.Model):
    uuid = ShortUUIDField(unique=True, blank=False, editable=False, default=shortuuid.uuid())
    question = models.ForeignKey(Question, on_delete=models.CASCADE)
    answer = models.CharField(max_length=70)
    creator = models.ForeignKey(CustomUser, on_delete=models.CASCADE, blank=True, null=True)
    def __str__(self):
        return '{}'.format(self.answer)
    class Meta:
        indexes = [
            models.Index(fields=['uuid']),
        ]

serializers.py

class AnswerSerializer(serializers.ModelSerializer):
    creator = ProfileSerializer(read_only = True)
    class Meta:
        model = models.Answer
        fields = ('uuid','answer', 'creator','question')
class QuestionSerializer(serializers.ModelSerializer):
    creator = ProfileSerializer(read_only = True)
    question_answer = AnswerSerializer(many=True,source='id')
    class Meta:
        model = models.Question
        fields = ('uuid','question', 'creator', 'question_answer')

views.py

class QuestionViewSet(viewsets.ModelViewSet):
    queryset = models.Question.objects.all()
    serializer_class = serializers.QuestionSerializer

Error thrown

Traceback (most recent call last):
  File "\Backend\env\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
    response = get_response(request)
  File "\Backend\env\lib\site-packages\django\core\handlers\base.py", line 115, in _get_response
    response = self.process_exception_by_middleware(e, request)
  File "\Backend\env\lib\site-packages\django\core\handlers\base.py", line 113, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "\Backend\env\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "\Backend\env\lib\site-packages\rest_framework\viewsets.py", line 116, in view
    return self.dispatch(request, *args, **kwargs)
  File "\Backend\env\lib\site-packages\rest_framework\views.py", line 495, in dispatch
    response = self.handle_exception(exc)
  File "\Backend\env\lib\site-packages\rest_framework\views.py", line 455, in handle_exception
    self.raise_uncaught_exception(exc)
  File "\Backend\env\lib\site-packages\rest_framework\views.py", line 492, in dispatch
    response = handler(request, *args, **kwargs)
  File "\Backend\env\lib\site-packages\rest_framework\mixins.py", line 48, in list
    return Response(serializer.data)
  File "\Backend\env\lib\site-packages\rest_framework\serializers.py", line 768, in data
    ret = super(ListSerializer, self).data
  File "\Backend\env\lib\site-packages\rest_framework\serializers.py", line 262, in data
    self._data = self.to_representation(self.instance)
  File "\Backend\env\lib\site-packages\rest_framework\serializers.py", line 686, in to_representation
    self.child.to_representation(item) for item in iterable
  File "\Backend\env\lib\site-packages\rest_framework\serializers.py", line 686, in <listcomp>
    self.child.to_representation(item) for item in iterable
  File "\Backend\env\lib\site-packages\rest_framework\serializers.py", line 530, in to_representation
    ret[field.field_name] = field.to_representation(attribute)
  File "\Backend\env\lib\site-packages\rest_framework\serializers.py", line 686, in to_representation
    self.child.to_representation(item) for item in iterable
TypeError: 'int' object is not iterable

Upvotes: 0

Views: 153

Answers (1)

A. J. Parr
A. J. Parr

Reputation: 8026

I believe the error is being cause by your use of source='id' on QuestionSerializer.question_answer. The use of source='id' implies that the serializer would be using the id field on your Question to retrieve the answers.

What I would recommend you do to solve this is:

  1. Add a related_name argument to the Answer.question field, e.g. question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='answers')

  2. Update the serializer to use the name of the reverse relation Question.answers on the field:

    class QuestionSerializer(serializers.ModelSerializer):
        creator = ProfileSerializer(read_only = True)
        answers = AnswerSerializer(many=True, read_only=True)
        class Meta:
            model = models.Question
            fields = ('uuid','question', 'creator', 'answers')

Upvotes: 2

Related Questions