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

Pytest提交含嵌套图片对象的multipart表单时数组传参异常

问题原因

multipart/form-data 协议本身只支持扁平键值对传输,没有定义原生的嵌套对象、数组序列化规则。直接将带文件对象的嵌套列表传入测试客户端时,客户端无法自动识别嵌套结构,同字段名的内容会被后续传入的值覆盖,最终接口端只能拿到数组的最后一个元素。

Django REST Framework 内置的MultiPartParser支持通过带索引的路径式键名解析嵌套结构,只要将嵌套的数组、对象展平为对应格式的扁平键值对,就能被正确解析为预期的数组结构。

解决方案

方法1:手动展平参数(适合结构简单的场景)

按照字段名[索引][子字段名]的规则拼接键名,把嵌套结构拆成扁平键值对即可,文件对象直接作为对应键的值传入,示例代码如下:

from io import BytesIO
from PIL import Image
from rest_framework import status

# 生成测试图片文件,这部分逻辑和原写法一致
file_horizontal = BytesIO()
image_horizontal = Image.new('RGBA', size=(244, 100), color=(155, 0, 0))
image_horizontal.save(file_horizontal, 'png')
file_horizontal.name = 'teste_horizontal.png'
file_horizontal.seek(0)

file_vertical = BytesIO()
image_vertical = Image.new('RGBA', size=(138, 100), color=(155, 0, 0))
image_vertical.save(file_vertical, 'png')
file_vertical.name = 'teste_vertical.png'
file_vertical.seek(0)

# 展平为multipart支持的键值对格式,注意补全原代码缺失的逗号
self.data = {
    'part_id': 123,
    # 数组第0个元素
    'logo[0][logo_type]': 'HORIZONTAL',
    'logo[0][document_type][0]': 1,
    'logo[0][document_type][1]': 2,
    'logo[0][image]': file_horizontal,
    # 数组第1个元素
    'logo[1][logo_type]': 'VERTICAL',
    'logo[1][document_type][0]': 3,
    'logo[1][document_type][1]': 4,
    'logo[1][image]': file_vertical,
}

def test_diff_create(self):
    response = self.client.post(
        self.url_create,
        self.data,
        format='multipart',
        HTTP_ACCEPT='application/json; version=1.0'
    )
    self.assertEqual(response.status_code, status.HTTP_201_CREATED)

方法2:递归展平工具函数(适合嵌套复杂、元素多的场景)

如果数组元素多、嵌套层级深,手动拼键名容易出错,可以写一个简单的递归函数自动展平嵌套结构,不用修改原来的嵌套数据写法:

def flatten_form_data(data, prefix=''):
    """递归将嵌套字典/列表转换为multipart兼容的扁平键值对"""
    result = {}
    for key, value in data.items():
        current_key = f"{prefix}[{key}]" if prefix else str(key)
        if isinstance(value, dict):
            result.update(flatten_form_data(value, current_key))
        elif isinstance(value, list):
            for idx, item in enumerate(value):
                result.update(flatten_form_data({idx: item}, current_key))
        else:
            result[current_key] = value
    return result

使用时直接传入原来定义的嵌套结构,调用函数展平后再传给请求即可:

raw_data = {
    'part_id': 123,
    'logo': [
        {
            'logo_type': 'HORIZONTAL',
            'document_type': [1, 2],
            'image': file_horizontal
        },
        {
            'logo_type': 'VERTICAL',
            'document_type': [3, 4],
            'image': file_vertical
        },
    ]
}
self.data = flatten_form_data(raw_data)
注意事项
  • 原代码中'part_id': 123行末尾缺失逗号,会触发语法错误,修改时注意补全
  • 确保接口视图使用的是DRF默认的MultiPartParser(或支持该键名规则的解析器),否则无法正确解析展平后的参数

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 23:18:47