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
相关产品推荐
相关产品推荐

