Django Ninja文件上传接口单元测试报错:BufferedReader无法JSON序列化
问题:Django Ninja 文件上传接口单元测试序列化错误
模型定义
class Invoices(TKSModel): invoice = models.FileField(upload_to="invoices/", null=False, blank=False, validators=[document_file_validator]) institution = models.CharField(max_length=64, null=False, blank=False) invoice_date = models.DateField(null=False, blank=False) description = models.TextField(null=True, blank=True)
接口实现
@router.post('/add', response={201: InvoiceShema}, tags=["Invoice"]) def add_invoice(request, invoice: InvoiceAddShema): instance = Invoices.objects.create( created_by=request.auth, **invoice.dict(), ) return 201, instance
请求Schema定义
class InvoiceAddShema(Schema): invoice_file: UploadedFile institution: str invoice_date: date description: Optional[str] = None
测试错误
运行单元测试时抛出:
TypeError: Object of type BufferedReader is not JSON serializable
解决方法
1. 修正字段名映射错误
模型中文件字段名为invoice,但请求Schema中命名为invoice_file,直接展开invoice.dict()会导致模型接收到不存在的字段名,同时文件对象无法正确赋值到目标字段。修改接口代码:
@router.post('/add', response={201: InvoiceShema}, tags=["Invoice"]) def add_invoice(request, invoice: InvoiceAddShema): # 手动映射字段名 instance = Invoices.objects.create( created_by=request.auth, invoice=invoice.invoice_file, institution=invoice.institution, invoice_date=invoice.invoice_date, description=invoice.description ) return 201, instance
或者用字典处理简化:
@router.post('/add', response={201: InvoiceShema}, tags=["Invoice"]) def add_invoice(request, invoice: InvoiceAddShema): data = invoice.dict() # 将Schema中的invoice_file映射到模型的invoice字段 data['invoice'] = data.pop('invoice_file') instance = Invoices.objects.create(created_by=request.auth, **data) return 201, instance
2. 单元测试中正确构造上传文件
测试时不能直接使用open()返回的BufferedReader,需要用Django提供的SimpleUploadedFile构造符合要求的上传文件对象:
from django.core.files.uploadedfile import SimpleUploadedFile from django.test import TestCase class InvoiceAPITest(TestCase): def test_add_invoice(self): # 构造测试文件 test_file = SimpleUploadedFile( "test_invoice.pdf", b"dummy_file_content", content_type="application/pdf" ) # 发送multipart格式的请求 response = self.client.post( "/api/invoices/add", data={ "invoice_file": test_file, "institution": "测试机构", "invoice_date": "2024-05-20", "description": "测试发票" }, format="multipart" ) self.assertEqual(response.status_code, 201)
3. 确保响应Schema正确序列化文件字段
响应使用的InvoiceShema需要将模型的FileField序列化为字符串(比如文件URL或相对路径),而不是直接返回文件对象:
class InvoiceShema(Schema): id: int invoice: str # 序列化为文件的访问URL或存储路径 institution: str invoice_date: date description: Optional[str] = None created_by: int # 根据实际用户模型调整序列化方式
Django Ninja会自动将FileField转换为对应的URL字符串,只要Schema中声明该字段为str类型即可。
内容的提问来源于stack exchange,提问作者niaei
相关产品推荐
相关产品推荐

