在Serializer类的“to_representation”中使用ListSerializer

问题描述 投票:0回答:1

我想根据嵌套 json 数组中收到的数据创建多个对象。为此,我创建了一个

serializers.Serializer
并重写
to_representation
方法以使用
ListSerializer
来序列化新创建的对象。

简化的代码如下所示(为了便于通过安装 DRF 运行 python 来轻松重现)

import json

from rest_framework import serializers


class Link:

    def __init__(self, title: str, description: str, url: str):
        self.title = title
        self.description = description
        self.url = url


class LinkResponseSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    description = serializers.CharField(required=False)
    url = serializers.CharField()


class URLSerializer(serializers.Serializer):

    url = serializers.CharField()


class LinkSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    description = serializers.CharField()
    urls = URLSerializer(many=True)

    def create(self, validated_data):
        links = []
        for obj in validated_data.pop("urls", []):
            links.append(Link(url=obj["url"], **validated_data))
        return links

    def to_representation(self, instance):
        return LinkResponseSerializer(instance, many=True).data


data = {
    "title": "Example",
    "description": "Exampe Description",
    "urls": [
        {"url": "https://google.com"},
        {"url": "https://yahoo.com"},
    ]
}

serializer = LinkSerializer(data=data)
serializer.is_valid(raise_exception=True)
serializer.save()

print(json.dumps(serializer.data, indent=4))

运行此代码将导致错误:

Traceback (most recent call last):
  File "/tmp/.../test.py", line 54, in <module>
    print(json.dumps(serializer.data, indent=4))
                     ^^^^^^^^^^^^^^^
  File "/tmp/.../venv/lib64/python3.12/site-packages/rest_framework/serializers.py", line 556, in data
    return ReturnDict(ret, serializer=self)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/tmp/.../venv/lib64/python3.12/site-packages/rest_framework/utils/serializer_helpers.py", line 19, in __init__
    super().__init__(*args, **kwargs)
ValueError: too many values to unpack (expected 2)

我知道这个问题来自于

.data
serializers.Serializer
属性,它尝试使用
ReturnDict
作为返回值,并使用列表作为参数进行初始化。

我可以通过覆盖

.data
属性来解决这个问题,如下所示:

from rest_framework.utils.serializer_helpers import ReturnList

class LinkSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    description = serializers.CharField()
    urls = URLSerializer(many=True)

    def create(self, validated_data):
        links = []
        for obj in validated_data.pop("urls", []):
            links.append(Link(url=obj["url"], **validated_data))
        return links

    def to_representation(self, instance):
        return LinkResponseSerializer(instance, many=True).data

    @property
    def data(self):
        self._data = self.to_representation(self.instance)
        return ReturnList(self._data, serializer=self)

这将产生所需的输出:

[
    {
        "title": "Example",
        "description": "Exampe Description",
        "url": "https://google.com"
    },
    {
        "title": "Example",
        "description": "Exampe Description",
        "url": "https://yahoo.com"
    }
]

但是如果我们使用通用视图,DRF Browserable API 将不再起作用,因为它期望 ReturnDict 作为

.data
serializers.Serializer
属性的返回类型!

处理问题的正确方法是什么?用例对我来说并不奇怪,感觉必须有一种更简单的方法。

python django-rest-framework
1个回答
0
投票

覆盖数据属性不是一个好习惯。

LinkSerializer 的

to_representation
应返回链接数据的字典,而不是代码中的列表,这就是可浏览 API 不起作用的原因。留下

如果要创建自定义列表响应,则需要修改

LinkResponseSerializer
to_representation 处理列表`

喜欢

class LinkResponseSerializer(serializers.ListSerializer):
    title = serializers.CharField(max_length=100)
    description = serializers.CharField(required=False)
    url = serializers.CharField()

    def to_representation(self, instance):
        links = instance.all() # This should be a list and you can return it.
        return links


class LinkSerializer(serializers.ListSerializer):
    title = serializers.CharField(max_length=100)
    description = serializers.CharField()
    urls = URLSerializer(many=True)

    def create(self, validated_data):
        links = []
        for obj in validated_data.pop("urls", []):
            links.append(Link(url=obj["url"], **validated_data))
        return links

    def to_representation(self, instance):
        return LinkResponseSerializer(instance, many=True)

    class Meta:
        list_serializer_class = LinkResponseSerializer

这未经测试,因此您应该预料到代码中会出现一些错误。

有关此内容的更多详细信息可以在这里找到 -> https://www.django-rest-framework.org/api-guide/serializers/#customizing-listserializer-behavior

© www.soinside.com 2019 - 2024. All rights reserved.