使用具有外键的模型的序列化程序在Django Rest Framework中的父表上执行CRUD [英] Use serializer of model having foreign key to do CRUD on parent table in Django Rest Framework

查看:100
本文介绍了使用具有外键的模型的序列化程序在Django Rest Framework中的父表上执行CRUD的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的API中,我有两个模型QuestionOption,如下所示

In my API, I have two models Question and Option as shown below

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


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

在创建问题时,可以同时创建选项会更好.并且已经存在的问题不应创建,但是如果选项与先前的选项不同,则可以更改选项.
我正在使用ModelSerializerModelViewSet.我为QuestionOption使用了不同的URL和视图.

While creating a question it would be nicer the options can be created at the same time. And already existed question should not be created but the options can be changed if the options are different from previous.
I am using ModelSerializer and ModelViewSet. I use different urls and views for Question and Option.

serializers.py

serializers.py

class QuestionSerializer(serializers.ModelSerializer):
    class Meta:
        model = Question
        fields = '__all__'


class OptionReadSerializer(serializers.ModelSerializer):
    question = QuestionSerializer(read_only=True)

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


class OptionWriteSerializer(serializer.ModelSerializer):
    class Meta:
        model = Option
        fields = ('question', 'option', 'is_correct')

views.py

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


class OptionViewSet(ModelViewSet):
    queryset = Option.objects.all()

    def get_serializer_class(self):
        if self.request.method == 'POST':
            return OptionWriteSerializer
        return OptionReadSerializer

urls.py

from django.urls import include
from rest_framework.routers import DefaultRouter

router = DefaultRouter()
router.register('api/question', QuestionViewset, base_name='question')
router.register('api/option', OptionViewSet, base_name='option')

urlpatterns = [
    path('', include(router.urls))
]

通过这种方式,我总是必须先创建问题,然后才能单独添加该问题的选项.我认为这可能不是一种实用的方法.
最好同时添加问题和选项,并且与所有CRUD操作类似,这样更好.

In this way, I always have to create questions first and then I can individually add the option for that question. I think this may not be a practical approach.
It would be nicer that question and option can be added at the same time and similar to all CRUD operations.

预期结果和JSON格式的发布数据如下所示:

The expected result and posting data in JSON format are as shown below:

{
    "body": "Which country won the FIFA world cup 2018",
    "options": [
        {
            "option": "England",
            "is_correct": 0
        },
        {
            "option": "Germany",
            "is_correct": 0
        },
        {
            "option": "France",
            "is_correct": 1
        }
    ]
}

推荐答案

models中,我在Option模型的外键字段中添加了related_name='options'

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

models.py

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()

QuestionWriteSerializer中,我重写了update()create()方法.为了创建和更新逻辑,已从QuestionWriteSerialzer处理.

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

serializers.py

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

QuestionViewSet几乎相同,我删除了OptionViewSet并控制了QuestionViewSet

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)

这篇关于使用具有外键的模型的序列化程序在Django Rest Framework中的父表上执行CRUD的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆