Django rest m2m嵌套序列化器创建/更新嵌套对象

时间:2018-04-26 17:28:28

标签: django django-rest-framework

我有一个模型场景

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

任务对象已经创建,从我的前端我创建了一个场景,只需添加一些现有的任务

串行:

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

    def create(self, validated_data):
        tasks = validated_data.pop('tasks')
        scenario = Scenario.objects.create(**validated_data)
        for task in tasks:
            Task.objects.update(scenario=scenario, **task)
        return scenario 

如果没有create()方法,则会抛出错误:

AssertionError: The `.create()` method does not support writable nested fields by default.
Write an explicit `.create()` method for serializer `scenarios.serializers.ScenarioSerializer`, or set `read_only=True` on nested serializer fields.

当我添加此方法时,错误消失了,但它扔了:

FieldError: Cannot update model field <ManyToManyRel: scenarios.scenario> (only non-relations and foreign keys permitted)

我做错了什么?我不想创建新任务,只需创建一个新方案并将现有任务附加到它。

更新:任务模型:

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, )
    OFTEN_CHOICES = (
    ('DS', 'Daily several times'),  
    ('DO', 'Daily once'),
    ('WO', 'Weekly once'),
    ('MO', 'Monthly once'),
    ('YO', 'Yearly once'),
    )
    how_often = models.CharField(blank=True, null = True, choices=OFTEN_CHOICES, max_length=2, )
    IMPORTANCE_CHOICES = (
    ('EI', 'Extremely important'),  
    ('RI', 'Rather important'),
    ('LI', 'Less important'),
    )
    how_important_task = models.CharField(blank=True, null = True, choices=IMPORTANCE_CHOICES, max_length=2, )
    role = models.CharField(max_length=20, blank=True, null = True, )
    why_perform_task = models.CharField(max_length=150, blank=True, null = True, )
    why_important_task = models.CharField(max_length=150, blank=True, null = True, )
    sequence_of_actions = models.CharField(max_length=500, blank=True, null = True, )
    tools_used = models.CharField(max_length=150, blank=True, null = True, )
    special_training_required = models.NullBooleanField(blank=True, null = True, default=False, )
    what_training_required = models.CharField(max_length=150, blank=True, null = True, )
    what_can_go_wrong = models.CharField(max_length=150, blank=True, null = True, )
    effects_of_task = models.CharField(max_length=150, blank=True, null = True, )
    special_vocabulary_used = models.CharField(max_length=150, blank=True, null = True, )
    people_involved = models.CharField(max_length=150, blank=True, null = True, )
    any_improvements = models.CharField(max_length=150, blank=True, null = True, )
    how_important_improvement = models.CharField(blank=True, null = True, choices=IMPORTANCE_CHOICES, max_length=2, )
    benefits_of_improvement = models.CharField(max_length=150, blank=True, null = True, )

样本请求数据:

{"tasks":[{"id":7,"title":"Seven","how_often":"","how_important_task":"","role":"","why_perform_task":"","why_important_task":null,"sequence_of_actions":"","tools_used":"","special_training_required":null,"what_training_required":"","what_can_go_wrong":"","effects_of_task":"","special_vocabulary_used":"","people_involved":"","any_improvements":"","how_important_improvement":"","benefits_of_improvement":"","stakeholder":2,"project":1}]}

2 个答案:

答案 0 :(得分:2)

您完成了第一部分,现在需要在docs添加方法update

例如

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

    def create(self, validated_data):
        tasks = validated_data.pop('tasks', [])
        instance = Scenario.objects.create(**validated_data)
        for task_data in tasks:
            task = Task.objects.get(pk=task_data.get('id'))
            instance.tasks.add(task)
        return instance 

   def update(self, instance, validated_data):
        tasks = validated_data.pop('tasks', [])
        instance = super().update(instance, alidated_data)
        for task_data in tasks:
            task = Task.objects.get(pk=task_data.get('id'))
            instance.tasks.add(task)
        return instance 

答案 1 :(得分:0)

按照这篇文章 https://lynxbee.com/solved-the-create-method-does-not-support-writable-nested-fields-by-default/ 中的步骤解决了这个问题。

pip install drf_writable_nested
from drf_writable_nested import WritableNestedModelSerializer

class YourSerializer(WritableNestedModelSerializer, serializers.ModelSerializer):
   pass