Django测试中APIClient发送带文件请求,视图request.FILES为空问题排查
问题根因
你遇到的问题本质是DRF 测试客户端 APIClient 的传参规则和 Django 原生测试客户端不兼容,你传递的 files 参数被完全忽略,根本没有进入请求 payload。
- 两个测试客户端的核心差异:
- 从
django.test导入的原生Client,其post方法明确支持独立的files入参,会自动将文件对象和普通表单字段拼接为符合规范的 multipart 请求体。 - 你使用的从
rest_framework.test导入的APIClient,重写了请求构造与编码逻辑,post方法本身不接收独立的files形参。你额外传入的files={"resume": fp}会被当成无意义的额外关键字参数丢弃,不会被编码到请求体中,自然视图里的request.FILES为空。
- 从
- 为什么用 Python requests 发送相同请求是正常的?
requests 库的post方法原生支持独立的files参数,会自动完成 multipart 结构拼接,整个请求到达视图后的处理逻辑和测试客户端发出的请求完全一致——这也直接排除了请求格式不规范、中间件异常、视图解析器配置错误的可能性,问题100%出在测试请求的构造环节。 - 代码中的隐性笔误:
你定义的测试数据路径变量是TEST_DIR,但实际打开文件时使用的是FILE_DIR + file_name,如果不是你写示例时的手误,这里实际运行时会直接抛出NameError,需要注意保持变量名统一,避免出现文件路径读取错误的问题。
正确的实现方式
按照 DRF APIClient 的规则,所有需要携带的普通表单字段、文件对象,必须统一放入 data 参数字典中,配合 format="multipart" 参数,客户端会自动完成正确的 multipart 编码:
TEST_DIR = "myapp/tests/test_data/" file_name = "some_resume.pdf" email_data = { "sender": "somedude@someprovidercom", "recipient": "someotherdude@someotherprovider.com", "subject": "New candidate", "stripped-html": "Check out this new candidate." } api = APIClient() # 保持在文件打开的上下文中发起请求 with open(f"{TEST_DIR}{file_name}", "rb") as fp: # 将文件对象直接嵌入data字典,不需要单独传files参数 email_data["resume"] = fp response = api.post( "/api/v1.0/emails/receive/", data=email_data, format="multipart", ) # 后续测试断言逻辑
如果你的测试不依赖本地磁盘的固定文件,更推荐使用 Django 提供的内存文件对象
SimpleUploadedFile构造测试文件,避免路径依赖、文件句柄泄漏的问题:from rest_framework.test import APIClient from django.core.files.uploadedfile import SimpleUploadedFile # 构造内存中的测试PDF文件 test_resume = SimpleUploadedFile( name="some_resume.pdf", content=b"%PDF-1.4 这是测试PDF的二进制内容", content_type="application/pdf" ) email_data = { "sender": "somedude@someprovidercom", "recipient": "someotherdude@someotherprovider.com", "subject": "New candidate", "stripped-html": "Check out this new candidate.", "resume": test_resume } api = APIClient() response = api.post( "/api/v1.0/emails/receive/", data=email_data, format="multipart" )
小概率排查项
如果按照上述写法修改后依然无法获取 request.FILES,可以检查两个场景(不过根据你描述的用 requests 能正常取到文件的前提,这两个场景基本可以排除):
- 视图的解析器配置是否包含
MultiPartParser:如果全局或视图级别的解析器列表没有覆盖 multipart 格式,DRF 会无法正确解析请求体中的文件字段。 - 自定义中间件是否在请求流转过程中重新构造了 request 对象,导致原 request 中的 FILES 属性被清空。
内容的提问来源于stack exchange,提问作者Martin Faucheux
相关产品推荐
相关产品推荐

