Django Rest框架-更新外键

aor9mmx1  于 2023-03-20  发布在  Go
关注(0)|答案(4)|浏览(205)

我对使用Django Rest框架时遇到的这个问题感到有点沮丧:
我正在使用一个带有custom serializer视图集。这个序列化器有它的depth set to 1。当我查询这个视图集时,我得到了数据的正确表示,例如:

data = {
  id: 1,
  issue_name: 'This is a problem',
  status: {
    id: 3,
    name: 'todo'
  }
}

问题出现在我需要更新状态时。例如,如果我想为此问题选择另一个状态,例如:

status_new = {
   id: 4,
   name: 'done'
}

我将以下补丁发送回服务器,这是输出:

data = {
  id: 1,
  issue_name: 'This is a problem',
  status: {
    id: 4,
    name: 'done'
  }

}

然而,状态并没有得到更新。事实上,它甚至不是validated_data字典的一部分。我读到过嵌套关系是只读的。有人能告诉我需要用一种简单的方法来做这件事吗?
我真的很感激。
先谢了

2guxujil

2guxujil1#

如文档中所述,您需要在序列化程序中编写自己的create()update()方法,以支持可写嵌套数据。
您还需要显式添加status字段,而不是使用depth参数,否则我相信它不会自动添加到validated_data
编辑:也许我在细节上有点短:你要做的是重写ModelIssueSerializer中的update。2这基本上会在序列化器级别上拦截PATCH/PUT请求。3然后获取新的状态并将其分配给示例,如下所示:

class StatusSerializer(serializers.ModelSerializer):
    class Meta:
        model = Status

class ModelIssueSerializer(serializers.ModelSerializer):
    status = StatusSerializer()
    # ...
    def update(self, instance, validated_data):
        status = validated_data.pop('status')
        instance.status_id = status.id
        # ... plus any other fields you may want to update
        return instance

我在注解中提到您可能需要添加StatusSerializer字段的原因是为了将状态输入validated_data,如果我没记错的话,如果您只使用depth,那么嵌套对象可能不会在update()/create()方法中序列化(尽管我可能搞错了)。无论如何,添加StatusSerializer字段只是使用depth=1的显式形式

w6lpcovy

w6lpcovy2#

对于这种情况,我通常使用自定义字段。

class StatusField(serializers.Field):

    def to_representation(self, value):
        return StatusSerializer(value).data

    def to_internal_value(self, data):
        try:
            return Status.objects.filter(id=data['id']).first()
        except (AttributeError, KeyError):
            pass

然后在主串行器中:

class IssueSerializer(serializers.ModelSerializer):
    status = StatusField()

    class Meta:
        model = MyIssueModel
        fields = (
            'issue_name',
            'status',
        )
ctrmrzij

ctrmrzij3#

我会假设你的模型模仿你的序列化器的数据。同样,我会假设你与状态有一个一对多的关系,但是你不需要通过问题序列化器来创建它们,你有一个不同的端点。在这种情况下,你可能会用一个SlugRelatedField来逃避。

from rest_framework import serializers

class StatusSerializer(serializers.ModelSerializer):
    class Meta:
        model = MyStatusModel
        fields = (
            'id',
            'status',
        )

class IssueSerializer(serializers.ModelSerializer):
    status = serializers.SlugRelatedField(slug_field='status', queryset=MyStatusModel.objects.all())

    class Meta:
        model = MyIssueModel
        fields = (
            'issue_name',
            'status',
        )

另一个有效的解决方案是在这里保留外键值,并通过ui-select或select 2组件在前端处理显示名称-RESTfull方法:您正在处理引用状态对象的问题对象。在Angular前端应用程序中,您将从后端查询特定路线上的所有状态,然后您将根据问题表单的外键值显示正确的描述性名称。
让我知道这对你有什么好处。

qpgpyjmq

qpgpyjmq4#

Django有点烦人,它忽略关系字段,如果depth = 1
处理这个问题的一个显式方法是定义2个序列化程序,并定义在视图集中使用哪个序列化程序
视图:

class MyViewSet(viewsets.ModelViewSet):
    queryset = models.MyModel.objects.order_by("pk")
    serializer_class = serializers.ExpandedSerializer

    def get_serializer_class(self):
        if self.action == ["list", "retrieve"]:
            return serializers.MyExpandedSerializer
        if self.action in ["create", "update", "partial_update"]:
            return serializers.MyCreateSerializer
        return super(LenderOfferingViewSet, self).get_serializer_class()

    def create(self, request, *args, **kwargs):
        """Overload the create method with a different response serializer."""
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        response_serializer = serializers.MyExpandedSerializer(serializer.instance)
        return Response(response_serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def update(self, request, *args, **kwargs):
        """Overload method for PUT/PATCH to correctly set values and set response serializer."""
        partial = kwargs.pop('partial', False)
        instance = self.get_object()
        serializer = self.get_serializer(instance, data=request.data, partial=partial)
        serializer.is_valid(raise_exception=True)
        self.perform_update(serializer)

        if getattr(instance, '_prefetched_objects_cache', None):
            # If 'prefetch_related' has been applied to a queryset, we need to
            # forcibly invalidate the prefetch cache on the instance.
            instance._prefetched_objects_cache = {}

        response_serializer = serializers.MyExpandedSerializer(serializer.instance)
        return Response(response_serializer.data)

然后是序列化器:

class MyCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.MyObject
        fields = ["issue_name", "status"]
        depth = 0  # no expansion else create/update fail on missing fields (serializer ignores related fields)

class MyExpandedSerializer(serializers.ModelSerializer):
    """This serializer expands related fields explicitly with SerializerMethodFields."""
    class Meta:
        model = models.My
        fields = ["id", "issue_name", "status"]
        depth = 1

然后当你

POST /my_endpoint/
{
  issue_name: 'This is a problem',
  status: 3
}

您将获得响应(在例如/my_endpoint/1上)

{
  id: 1,
  issue_name: 'This is a problem',
  status: {
    id: 3,
    name: 'todo'
  }
}

相关问题