Use serializer of model having foreign key to do CRUD on parent table in Django Rest Framework

落花浮王杯 提交于 2019-12-06 11:32:28

In models I added related_name='options' in foreign key field of Option model

models.py

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


class Options(models.Model):
    question = models.ForeignKey(Question, on_delete=models.CASCADE, related_name='options')
    option = models.CharField(max_length=100)
    is_correct = models.SmallIntegerField()

In QuestionWriteSerializer I override the update() and create() method. For creating and updating the logic was handled from QuestionWriteSerialzer.

serializers.py

class OptionSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(required=False)

    class Meta:
        model = Option
        fields = ('id', 'question', 'option', 'is_correct')


class QuestionReadSerializer(serializers.ModelSerializer):
    options = OptionSerializer(many=True, read_only=True)

    class Meta:
        model = Question
        fields = ('id', 'body', 'options')


class QuestionWriteSerializers(serializers.ModelSerializer):
    options = OptionSerializer(many=True)

    class Meta:
        model = Question
        fields = ('id', 'body', 'options')

    def create(self, validated_data):
        options_data = validated_data.pop('options')
        question_created = Questions.objects.update_or_create(**validated_data)

        option_query = Options.objects.filter(question=question_created[0])
        if len(option_query) > 1:
            for existeding_option in option_query:
                option_query.delete()

        for option_data in options_data:
            Options.objects.create(question=question_created[0], **option_data)

        return question_created[0]

    def update(self, instance, validated_data):
        options = validated_data.pop('options')
        instance.body = validated_data.get('body', instance.body)
        instance.save()

        keep_options = []
        for option_data in options:
            if 'id' in option_data.keys():
                if Options.objects.filter(id=option_data['id'], question_id=instance.id).exists():
                    o = Options.objects.get(id=option_data['id'])
                    o.option = option_data.get('option', o.option)
                    o.is_correct = option_data.get('is_correct', o.is_correct)
                    o.save()
                    keep_options.append(o.id)
                else:
                    continue
            else:
                o = Options.objects.create(**option_data, question=instance)
                keep_options.append(o.id)

        for option_data in instance.options.all():
            if option_data.id not in keep_options:
                Options.objects.filter(id=option_data.id).delete()

        return instance

The QuestionViewSet is almost the same and I removed the OptionViewSet and controlled all things from QuestionViewSet

views.py

class QuestionViewSet(ModelViewSet):
    queryset = Question.objects.all()

    def get_serializer_class(self) or self.request.method == 'PUT' or self.request.method == 'PATCH':
        if self.request.method == 'POST':
            return QuestionWriteSerializer
        return QuestionReadSerializer

    def create(self, request, *args, **kwargs):
        """
        Overriding create() method to change response format
        """
        serializer = self.get_serializer(data=request.data)
        if serializer.is_valid():
            self.perform_create(serializer)
            headers = self.get_success_headers(serializer.data)
            return Response({
                'message': 'Successfully created question',
                'data': serializer.data,
                'status': 'HTTP_201_CREATED',
            }, status=status.HTTP_201_CREATED, headers=headers)
        else:
            return Response({
                'message': 'Can not create',
                'data': serializer.errors,
                'status': 'HT',
            }, status=status.HTTP_400_BAD_REQUEST)

We can use PrimaryKeyRelatedField.

tldr;

I believe a Question can have multiple Options attached to it. Rather than having an Option hooked to a Question.

Something like this:

class Question(models.Model):
    body = models.TextField()
    options = models.ManyToManyField(Option)

class Options(models.Model):
    text = models.CharField(max_length=100)
    is_correct = models.BooleanField()

Then we can use PrimaryKeyRelatedField something like this:

class QuestionSerializer(serializers.ModelSerializer):
    options = serializers.PrimaryKeyRelatedField(queryset=Options.objects.all(), many=True, read_only=False)
    class Meta:
        model = Question
        fields = '__all__'

Reference : https://www.django-rest-framework.org/api-guide/relations/#primarykeyrelatedfield

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!