将 `partial=True` 传递到 DRF 中的嵌套序列化器

huangapple go评论77阅读模式
英文:

Passing partial=True down to nested serializer in DRF

问题

以下是您要求的内容的翻译:

我有两个像这样组织的序列化程序

class OuterSerializer():
  inner_obj = InnerSerializer(many=True, required=False)
  其他字段 ......

class InnerSerializer():
  field_1 = CharField()
  field_2 = CharField()

现在我的用例是部分更新外部序列化程序的模型我是如何做的

def partial_update(self, request, *args, **kwargs):
    serializer = OuterSerializer(data=request.data, context={'request': self.request}, partial=True)
    serializer.is_valid(raise_exception=True)
    data = serializer.data
    outerobj = self.service_layer.update(kwargs['pk'], data, request.user)
    response_serializer = OpportunitySerializer(instance=outerobj, context={'request': self.request})
    return Response(response_serializer.data, HTTPStatus.OK)

问题是这个部分标志不会传递到InnerSerializer例如如果我的请求正文如下所示我希望它能工作

{"inner_obj":
  {
    "field_1" : "abc"
  }
}

目前对于这个请求我得到一个400错误说这个字段是必需的

我尝试过
1. 通过在OuterSerializer的init方法中修改它来设置partial变量

def __init__(self, *args, **kwargs):
    super().__init__(*args, **kwargs)
    # 我们将“当前序列化程序”的上下文传递给“嵌套序列化程序”
    self.fields['inner_obj'].context.update(self.context)
    self.fields['inner_obj'].partial = kwargs.get('partial')
但是这不会传递到内部序列化程序
英文:

I have two serializers organised like this:

class OuterSerializer():
  inner_obj = InnerSerializer(many=True, required=False)
  other fields ......
class InnerSerializer():
  field_1 = CharField()
  field_2 = CharField()

Now my use case is to partial update the outer serializer's model. How I'm doing that is:

   def partial_update(self, request, *args, **kwargs):
        serializer = OuterSerializer(data=request.data, context={'request': self.request}, partial=True)
        serializer.is_valid(raise_exception=True)
        data = serializer.data
        outerobj = self.service_layer.update(kwargs['pk'], data, request.user)
        response_serializer = OpportunitySerializer(instance=outerobj, context={'request': self.request})
        return Response(response_serializer.data, HTTPStatus.OK) 

The issue is this partial flag does not get passed down to the InnerSerializer.
For example if my request body looks like below, I want it to work:

{"inner_obj":
  {
    "field_1" : "abc"
  }
}

Currently I get a 400 error for this saying the field is required.

What I've tried :

  1. Setting the partial variable within the OuterSerializer in the init method by modifying it as such
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # We pass the "current serializer" context to the "nested one"
        self.fields['inner_obj'].context.update(self.context)
        self.fields['inner_obj'].partial = kwargs.get('partial')  

However this doesn't travel down.

答案1

得分: 1

尝试修改InnerSerializer,使其能够接受partial参数并将其传递给其父类,如下所示:

class InnerSerializer(serializers.Serializer):
    field_1 = CharField()
    field_2 = CharField()

    def __init__(self, *args, **kwargs):
        self.partial = kwargs.pop('partial', False)
        super().__init__(*args, **kwargs)

class OuterSerializer(serializers.Serializer):
    inner_obj = InnerSerializer(many=True, required=False)
    其他字段 ......

    def __init__(self, *args, **kwargs):
        partial = kwargs.get('partial')
        super().__init__(*args, **kwargs)
        self.fields['inner_obj'].child.partial = partial

另一个可能的解决方案。

您还可以在InnerSerializer中重写to_internal_value()方法,以使其接受部分更新,如下所示:

class InnerSerializer(serializers.Serializer):
    field_1 = CharField()
    field_2 = CharField()

    def to_internal_value(self, data):
        if self.partial:
            return {field: data.get(field, getattr(self.instance, field)) for field in data}
        return super().to_internal_value(data)

class OuterSerializer(serializers.Serializer):
    inner_obj = InnerSerializer(many=True, required=False)
    其他字段 ......

编辑:

对于错误:

> KeyError: "Got KeyError when attempting to get a value for field field_2on serializerInnerSerializer`.

您遇到的错误消息表明序列化器正在尝试从数据中访问field_2的值,但它不存在。

目前,为了解决错误,您应该重写InnerSerializer中的to_representation()方法,只包括存在的字段,如下所示:

class InnerSerializer(serializers.Serializer):
    field_1 = CharField()
    field_2 = CharField()

    def to_representation(self, instance):
        data = super().to_representation(instance)
        return {field: value for field, value in data.items() if value is not None}
英文:

Try to modify the InnerSerializer so that it could accept the partial argument and pass it to its parent, like following:

class InnerSerializer(serializers.Serializer):
    field_1 = CharField()
    field_2 = CharField()

    def __init__(self, *args, **kwargs):
        self.partial = kwargs.pop('partial', False)
        super().__init__(*args, **kwargs)

class OuterSerializer(serializers.Serializer):
    inner_obj = InnerSerializer(many=True, required=False)
    other fields ......

    def __init__(self, *args, **kwargs):
        partial = kwargs.get('partial')
        super().__init__(*args, **kwargs)
        self.fields['inner_obj'].child.partial = partial

Another possible solution.

You can also override the to_internal_value() method in the InnerSerializer to make it accept partial updates so:

class InnerSerializer(serializers.Serializer):
    field_1 = CharField()
    field_2 = CharField()

    def to_internal_value(self, data):
        if self.partial:
            return {field: data.get(field, getattr(self.instance, field)) for field in data}
        return super().to_internal_value(data)

class OuterSerializer(serializers.Serializer):
    inner_obj = InnerSerializer(many=True, required=False)
    other fields ......

Edit:

For the error:

> KeyError: "Got KeyError when attempting to get a value for field field_2on serializerInnerSerializer`.

The error message you're encountering suggests that the serializer is trying to access the value for field_2 from the data, but it's not present.

Currently to solve the error, you should override the to_representation() method in the InnerSerializer to only include the fields that are present so:

class InnerSerializer(serializers.Serializer):
    field_1 = CharField()
    field_2 = CharField()

    def to_representation(self, instance):
        data = super().to_representation(instance)
        return {field: value for field, value in data.items() if value is not None}

huangapple
  • 本文由 发表于 2023年2月9日 03:01:34
  • 转载请务必保留本文链接:https://go.coder-hub.com/75390544.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定