1

I've been using the Django rest framework to create API. In this API there are two models Question and Option. Question is Foreign Key in Option. I have been trying to get all the fields in a single form and add data to the corresponding models.
For a question, options can be as many as provided from a form. I tried to create options for the already existed question (ie. first question will be added in the database and then the question is selected and the options for that question will be added) and I don't think it will be the practical approach so I had an idea that the options need to be added in the database at the time of question creation.

models.py

class Question(models.Model):
    body = RichTextField()


class Option(models.Model):
    question = models.ForeignKey(Question, on_delete=CASCADE)
    number = models.IntegerField()
    option = RichTextField()
    is_correct = models.SmallIntegerField()

serializers.py

class QuestionSerializers(serializers.ModelSerializer):
    class Meta:
        model = Questions
        fields = ('id', 'body', 'explanation')


class QuestionReadSerializer(serializers.ModelSerializer):
    question = QuestionSerializers()

    class Meta:
        model = Options
        fields = ('question', 'number', 'option', 'is_correct')


class QuestionWriteSerializers(serializers.ModelSerializer):
    question = QuestionSerializers()

    class Meta:
        model = Options
        fields = ('question', 'number', 'option', 'is_correct')

    @transaction.atomic
    def create(self, validated_data):
        question_data = validated_data.pop('question')
        question = Questions.objects.update_or_create(**question_data)
        option = Options.objects.update_or_create(question=question[0], **validated_data)

        return option[0]

views.py

class QuestionViewset(viewsets.ModelViewSet):
    queryset = Options.objects.all()
    def get_serializer_class(self):
        if self.request.method == 'POST':
            return QuestionWriteSerializers
        return QuestionReadSerializers

I am using Option model for creating new options for a specific question and if the question is new then new question will be created. But it is not the best way. Any help will be appriciated.

dipesh
  • 763
  • 2
  • 9
  • 27

1 Answers1

2

You can override the create of the Question viewset to work on options provided in the payload if you want. Also, IMO you don't really need two set of Serializers for Question. The final code with suggestion in place should look like:


models.py

class Question(models.Model):
    body = RichTextField()


class Option(models.Model):
    question = models.ForeignKey(Question, related_name='options', on_delete=CASCADE)
    number = models.IntegerField()
    option = RichTextField()
    is_correct = models.SmallIntegerField()

serializers.py

class QuestionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Questions
        fields = ('id', 'body', 'explanation')


class OptionSerializer(serializers.ModelSerializer):
    question = QuestionSerializer()

    class Meta:
        model = Options
        fields = ('question', 'number', 'option', 'is_correct')

views.py

class QuestionViewset(viewsets.ModelViewSet):
    serializer_class = QuestionSerializer

    def create(self, request, *args, **kwargs):
        options = request.data.pop("options", [])
        _question = super(QuestionViewset, self).create(request, *args, **kwargs)
        for option in options:
            option["question"] = _question.id
            serializer = OptionSerializer(data=option)

            # I would recommend to delete the added question + options 
            # in case is_valid returns False, depends on your req.
            assert serializer.is_valid()
            serializer.save()

        return _question
akazuko
  • 1,394
  • 11
  • 19
  • ```options = request.data.pop("options", [])```from where does the data will be available in field name ```options```, please have a look – dipesh Aug 03 '19 at 16:45
  • 1
    That would be in the data you can send to POST API pertaining to `question` resource – akazuko Aug 03 '19 at 18:35
  • okay, I got your point and the problem here is that in ```_question``` in line: ```option["question"] = _question.id``` error is: ```AttributeError: 'Response' object has no attribute 'id'``` – dipesh Aug 04 '19 at 09:48
  • You should be able to use `_question.data["id"]` as _question is of type Response from DRF – akazuko Aug 04 '19 at 18:37
  • Will you have a look in a more explained version of this question [question asked in stackoverflow](https://stackoverflow.com/questions/57353803/use-serializer-of-model-having-foreign-key-to-do-crud-on-parent-table-in-django) – dipesh Aug 05 '19 at 08:30