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

DRF序列化报错:预期字典却得到列表的问题解决

DRF序列化器适配外部API数据报错的解决方案

问题背景

我正在开发一个基于Django+DRF的项目,通过Celery任务从外部API拉取剧集数据并保存到数据库。外部API返回格式如下:

{"shows": ["Boy Meets World", "Everybody Hates Chris", ...]}

Celery任务简化代码:

@shared_task
def fetch_and_save_all_shows():
    api_url = TV_SHOW_URL
    shows_url = f"{api_url}/quotes/shows"
    try:
        response = requests.get(shows_url)
        data = response.json()

        if isinstance(data, dict) and 'shows' in data:
            show_names = data['shows']

            if not isinstance(show_names, list):
                raise ValueError("Expected 'shows' to be a list.")

            # 转换为序列化器预期的格式
            show_data = [{'shows': show} for show in show_names]

            serializer = AllTvShowsSerializer(data=show_data, many=True)
            serializer.is_valid(raise_exception=True)
            serializer.save()
        else:
            raise ValueError("Invalid response data format")

    except Exception as ex:
        return {"error": f"An error occurred: {str(ex)}"}

遇到两个错误:

  • 设置many=True时,报错:Invalid data. Expected a dictionary, but got list.
  • 设置many=False时,报错:Invalid data. Expected a dictionary, but got str.

错误原因与修复方案

核心问题:序列化器定义与传入数据格式不匹配

根据报错信息和代码逻辑,问题出在序列化器预期的数据结构与你构造的show_data格式不匹配,分两种常见场景处理:


场景1:序列化器对应单个剧集模型

如果AllTvShowsSerializer是单个TvShow模型的ModelSerializer,模型中存储剧集名称的字段是name(而非shows):

修复步骤:

  1. 调整数据转换逻辑,使用模型字段名作为字典键:
    # 把'shows'改为模型对应的字段名,比如'name'
    show_data = [{'name': show} for show in show_names]
    
  2. 保持many=True实例化序列化器(因为传入的是字典列表):
    serializer = AllTvShowsSerializer(data=show_data, many=True)
    

示例序列化器定义:

from rest_framework import serializers
from .models import TvShow

class AllTvShowsSerializer(serializers.ModelSerializer):
    class Meta:
        model = TvShow
        fields = ['name']  # 与模型字段一致

场景2:序列化器接收完整外部API响应结构

如果AllTvShowsSerializer是用来接收外部API返回的完整{"shows": [列表]}结构:

修复步骤:

  1. 无需拆分show_names,直接传入外部API返回的原始data:
    # 移除show_data的构造逻辑,直接用data
    serializer = AllTvShowsSerializer(data=data)
    
  2. 不要设置many=True(因为传入的是单个字典)。

示例序列化器定义:

from rest_framework import serializers
from .models import TvShow

class AllTvShowsSerializer(serializers.Serializer):
    # 定义字段匹配外部API的结构:shows是字符串列表
    shows = serializers.ListField(child=serializers.CharField(max_length=255))

    def create(self, validated_data):
        # 批量创建/获取剧集,避免重复
        show_names = validated_data['shows']
        shows = []
        for name in show_names:
            show, created = TvShow.objects.get_or_create(name=name)
            shows.append(show)
        return shows

额外检查点

  • 确认外部API返回的shows列表中所有元素都是字符串,无嵌套列表或其他类型。
  • 确保序列化器的字段名与传入字典的键完全一致(大小写敏感)。
  • 使用ModelSerializer时,验证模型字段类型与传入数据类型匹配(比如字符串对应CharField)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 14:36:15