Django 休息框架更新嵌套序列化程序

时间:2021-06-29 22:09:45

标签: django django-rest-framework

我有两种模型,一种与另一种具有外键关系。我希望能够同时创建或更新多个条目。我不会同时创建和更新。

我一直对人们的例子感到困惑。我希望有人不仅可以向我展示我需要做什么,而且还可以对每一步发生的事情进行一些解释。我特别不明白 validated_data.get() 方法在做什么,或者实例究竟是什么,以及它们正在做什么。

class ExtraFieldsSerializer(serializers.ModelSerializer):
    id = serializers.ReadOnlyField()

    class Meta:
        model = ExtraFields
        fields = [
            'id',
            'job',
            'custom_data',
        ]


class JobWriteSerializer(serializers.ModelSerializer):
    extra_fields = ExtraFieldsSerializer(many=True)

    class Meta:
        model = Job
        fields = [
            "extra_fields",
            "custom_data_fields",
        ]

    # Make JobSerializer able to create custom fields.
    def create(self, validated_data):
        extra_fields = validated_data.pop("extra_fields")
        user = User.objects.get(id=self.context['request'].user.id)
        client = Client.objects.get(user=self.context['request'].user)
        new_job = Job.objects.create(user=user, client=client, **validated_data)
        new_job.save()
        for field in extra_fields:
            new_field = ExtraFields.objects.create(job=new_job, custom_data=field['custom_data'])
            new_field.save()
        return new_job

    # Make JobSerializer able to update custom fields
    def update(self, instance, validated_data):
        pass

1 个答案:

答案 0 :(得分:2)

首先,您可能可以使用 drf-writeable-nested,因为它几乎完全符合您的要求。

但了解正在发生的事情永远不会有什么坏处:

def create(self, validated_data):
        extra_fields = validated_data.pop("extra_fields")
        user = User.objects.get(id=self.context['request'].user.id)
        client = Client.objects.get(user=self.context['request'].user)
        new_job = Job.objects.create(user=user, client=client, **validated_data)
        new_job.save()
        for field in extra_fields:
            new_field = ExtraFields.objects.create(job=new_job, custom_data=field['custom_data'])
            new_field.save()
        return new_job

由于 JobWriteSerializer 是父序列化器,我们将从它开始。 传递给您的 create 函数的 validated_data 参数包含,顾名思义,验证数据。这意味着,您可以假设您在序列化程序中定义的所有约束(必填字段、max_length、min_length 等)都成立,并且您不需要检查它们。

代码看起来不错,似乎您正在从序列化程序中弹出所有 extra_fields 并从中创建 ExtraField 对象。 您特别询问使用 validated_data.get 时发生了什么,但您使用的是 validated_data.pop。不同之处在于,使用 get 时,检索到的数据保留在字典中,而 pop 将其删除。

这对于您还必须创建嵌套对象的情况特别方便,请考虑这一点(省略了一些不相关的内容):

class MyModel(models.Model):
  text = models.CharField(max_length=10, related_name='children')

class MyChildModel(models.Model):
  someVal = models.BooleanField()
  model = models.ForeignKey(MyModel)

class MyChildSerializer(serializers.ModelSerializer):
  someVal = serializers.BooleanField()  
   
class MyModelSerializer(serializers.ModelSerializer):
  text = serializers.TextField(...)
  childen = ChildrenSerializer(many=True)
  
  def create(self, validated_data):
     children = validated_data.pop('children', []) #POP!
     instance = super().create(validated_data)
     
     for c in children:
       MyChildSerializer.objects.create(model=instance, **c)
     return instance

你可以自己测试,如果你在这里使用 get 而不是 pop,你的序列化器会理所当然地抱怨 children 对象中有 validated_data 对象,并且drf 无法创建开箱即用的嵌套关系。当您 pop 它们时,序列化程序不再有这些字段并且可以工作。

请注意,这种方法对您的情况来说效率不高,因为您手动将数据传递给您无法通过的 Job 对象(如 userclient)传递的数据,但来自您的请求。如果您愿意,您可以使用 get_serializer_context 来解决这个问题,但我们只能说这超出了问题范围。

关于您的更新方法,我建议使用类似的方法(未经测试,但您懂的):

def update(self, instance, validated_data):
  extra_fields = validated_data.pop('extra_fields', []) # POP the extra fields
  instance = super().update(instance, validated_data) # !!!!!

  for extra in extra_fields:
    #retrieve your extra fields and update them
    myExtra = ExtraFields.objects.get(id=extra['id])
    ....

传递的参数 instance 实际上是您的 Job 模型的现有实例。这是您要更新的对象。请注意,我再次弹出 extra_fields,正是为了执行我上面描述的操作:我使用 drf 本身来修改/更新对象,因为我只需要为子元素实现 update。< /p>