You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

DRF中在Serializer的to_representation内使用ListSerializer的正确处理方式

处理DRF中单个请求创建多个对象的序列化问题

问题场景

需要接收嵌套JSON数组数据,批量创建多个对象,通过重写Serializer的create和to_representation方法实现,但运行时出现序列化错误。

原始代码

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)

错误原因

DRF的普通Serializer默认对应单个实例,其data属性会用ReturnDict(键值对结构)包装序列化结果。但你的create方法返回了实例列表,to_representation也返回列表结构,导致ReturnDict初始化时无法解析列表数据,从而报错。

你之前重写data属性返回ReturnList的临时方案,会破坏DRF可浏览API的逻辑(API期望单个对象的输入表单),因此不推荐。


正确处理方案

方案一:拆分输入处理与输出序列化(推荐)

将输入验证、实例创建的逻辑,与结果序列化的逻辑拆分,既符合DRF设计规范,又兼容可浏览API:

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 LinkCreateSerializer(serializers.Serializer):
    title = serializers.CharField(max_length=100)
    description = serializers.CharField()
    urls = URLSerializer(many=True)

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


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

# 处理输入并创建实例
create_serializer = LinkCreateSerializer(data=data)
create_serializer.is_valid(raise_exception=True)
links = create_serializer.save()

# 序列化输出结果
response_serializer = LinkResponseSerializer(links, many=True)
print(json.dumps(response_serializer.data, indent=4))

方案二:在通用视图中适配

如果使用DRF通用视图(如CreateAPIView),可以在视图层完成输入输出的序列化适配,保证可浏览API正常工作:

from rest_framework.views import APIView
from rest_framework.response import Response

# 复用方案一中的LinkCreateSerializer和LinkResponseSerializer

class LinkCreateView(APIView):
    def post(self, request):
        # 验证输入并创建实例
        create_serializer = LinkCreateSerializer(data=request.data)
        create_serializer.is_valid(raise_exception=True)
        links = create_serializer.save()
        
        # 序列化输出列表结果
        response_serializer = LinkResponseSerializer(links, many=True)
        return Response(response_serializer.data)

方案三:自定义ListSerializer(适配批量场景)

如果需要直接用单个Serializer处理,可通过自定义ListSerializer实现输入输出的统一:

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 LinkListSerializer(serializers.ListSerializer):
    def create(self, validated_data):
        # 从单个输入数据中解析urls,生成多个实例
        data = validated_data[0]
        links = []
        urls = data.pop("urls", [])
        for obj in urls:
            links.append(Link(url=obj["url"], **data))
        return links


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

    class Meta:
        list_serializer_class = LinkListSerializer

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


# 使用示例:初始化时指定many=True
data = {
    "title": "Example",
    "description": "Exampe Description",
    "urls": [
        {"url": "https://google.com"},
        {"url": "https://yahoo.com"},
    ]
}

serializer = LinkSerializer(data=[data], many=True)
serializer.is_valid(raise_exception=True)
serializer.save()

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

内容的提问来源于stack exchange,提问作者hardhypochondria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 11:54:57