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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 18:05:04