Django Rest Framework M2M序列化程序保存嵌套对象 [英] Django rest framework M2M serializer save nested objects

查看:185
本文介绍了Django Rest Framework M2M序列化程序保存嵌套对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个模型:任务和场景。预先创建任务,然后通过包含少量现有任务来创建方案。重要的是,在场景中,必须按照特定的顺序而不是根据其ID安排任务。

I have two models: Task and Scenario. Tasks are created beforehand and Scenarios are created afterwards by including few of the existing tasks. Important thing is in a scenario, tasks must be arranged in a specific sequence and not according to their ids.

class Task(models.Model):
    stakeholder = models.ForeignKey(User, related_name='tasks', blank=True, )
    project = models.ForeignKey(Project, related_name='project_tasks' )
    title = models.CharField(max_length=50, blank=True, null = True, )
    ...

class Scenario(models.Model):
    stakeholder = models.ForeignKey(User, related_name='scenarios', blank=True,)
    tasks = models.ManyToManyField(Task, blank=True)

序列化器:

class TaskSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField()
    class Meta:
        model = Task
        fields = '__all__'

class ScenarioSerializer(serializers.ModelSerializer):
    tasks = TaskSerializer(many=True, required=False)
    class Meta:
        model = Scenario
        fields = '__all__'

    def get_or_create_task(self, data):
        qs  = Task.objects.filter(pk=data.get('id'))
        if qs.exists():
            return qs.first()
        task = Task.objects.create(**data)
        return task

    def add_tasks(self, instance, tasks):
        for task_data in tasks:
            task = self.get_or_create_task(task_data)
            instance.tasks.add(task)

    def create(self, validated_data):
        tasks = validated_data.pop('tasks')
        instance = Scenario.objects.create(**validated_data)
        self.add_tasks(instance, tasks)
        return instance

    def update(self, instance, validated_data):
        tasks = validated_data.pop('tasks', [])
        instance = super().update(instance, validated_data)
        self.add_tasks(instance, tasks)
        return instance

我有几个要求:

在检索场景时,我想检索对应的任务对象,而不仅仅是它们的ID,这就是为什么行 id = serializers.IntegerField()存在于TaskSerializer中。

While retrieving a scenario/s I wanted to retrieve corresponding task objects and not just their ids, that's why the line id = serializers.IntegerField() exists in TaskSerializer.

使用此代码成功创建了一个新任务,但任务更新失败,例如 / api / tasks / 1 ,因为它也希望请求正文中有id。如果没有 id = serializers.IntegerField(),则任务创建和更新都将成功。

With this code a new Task is successfully created, but Task update fails at e.g./api/tasks/1 as it also expects id in the request body. Without id = serializers.IntegerField() both task creation and update is successful.

此外,没有<$ c TaskSerializer中的$ c> id = serializers.IntegerField()方案创建成功,并且包含的​​任务以正确的顺序返回,例如task3,task1,Task5。但是,无论方案创建时包括什么任务,都将在数据库中再次创建这些任务(当然具有连续的ID)。

Also, without id = serializers.IntegerField() in TaskSerializer Scenario creation is successful and included tasks are returned in the correct order, say task3, task1, Task5. However, whichever tasks are included while scenario creation, those are created again in the database (of course with successive ids).

取消注释 id = serializers.IntegerField()可以成功创建方案,并且不会自动创建其他任务(很好),但该方案返回的任务的ID数即为任务,即如果使用Task3,task1,task5创建了方案,则在获取方案时会返回task1,task3,task5。

Uncommenting id = serializers.IntegerField() leads to successful scenario creation and no extra tasks are automatically created (which is good), but the scenario returns tasks in the oder of their ids i.e. if a scenario was created with Task3, task1, task5 then when you GET a scenario you get back task1, task3, task5.

为什么会发生?

附加说明:我不打算在创建场景时创建任务,任务始终会事先创建,然后在场景中附加一些现有任务便会创建场景。

Extra Note: I do not intend to create a task while scenario creation, tasks will always be created beforehand, and scenario will be created afterwards by attaching a few existing tasks to it

推荐答案

您可以尝试设置任务只读,并从上下文获取数据,并保存创建的订单,您可以使用 extra select

you can try to set tasks as read_only and get data from context, and to save the created order you may use extra select

class ScenarioSerializer(serializers.ModelSerializer):
    tasks = serializers.SerializerMethodField()

    class Meta:
        model = Scenario
        fields = '__all__'

    def get_tasks(self, obj):
        qs = obj.tasks.extra(
            select={'creation_seq': 'scenario_scenario_tasks.id'}
            ).order_by("creation_seq")
        return TaskSerializer(qs, many=True).data

    def get_or_create_task(self, data):
        qs = Task.objects.filter(pk=data.get('id'))
        if qs.exists():
            return qs.first()
        serializer = TaskSerializer(data=data)
        serializer.is_valid(raise_exception=True)
        task = serializer.save()
        return task

    def add_tasks(self, instance, data):
        tasks = data.get('tasks', [])
        for task_data in tasks:
            task = self.get_or_create_task(task_data)
            instance.tasks.add(task)

    def create(self, validated_data):
        instance = super().create(validated_data)
        # for python 2
        # instance = super(ScenarioSerializer, self).create(validated_data)
        data = self.context.get('request').data
        #      HERE ^^^^^^^^^^
        self.add_tasks(instance, data)
        return instance

    def update(self, instance, validated_data):
        instance = super().update(instance, validated_data)
        # for python 2
        # instance = super(ScenarioSerializer, self).update(instance, validated_data)
        data = self.context.get('request').data
        #      HERE ^^^^^^^^^^
        self.add_tasks(instance, data)
        return instance

和此处 drfdoc-demo 您可以查看带有基于类的drfdoc视图的完整示例

and here drfdoc-demo you can look on full example with class based views for drfdoc

这篇关于Django Rest Framework M2M序列化程序保存嵌套对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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