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

如何将内存中生成的Excel文件保存到Django的models.FileField

问题:将内存生成的Excel保存到Django FileField时报错 'XlsxWriter' object has no attribute 'read'

操作流程:从Django模型提取数据存入pandas DataFrame → 转换为Excel文件 → 保存到models.FileField时触发错误。

报错代码段:

file = File(excel_file)
doc.document.save(file_name, file, save=True)

错误信息:'XlsxWriter' object has no attribute 'read'


错误原因

当前代码中save_xlsx函数返回的是pd.ExcelWriter实例(底层为XlsxWriter对象),但Django的File类要求传入支持read()方法的类文件对象(如BytesIO、本地文件流),XlsxWriter对象不满足该要求。同时原代码还将Excel写入静态目录,属于多余的本地IO操作。


解决方案:用io.BytesIO在内存中生成并传递Excel

1. 修改utils.py中的函数

取消本地文件写入,直接在内存中生成Excel二进制数据:

import io  # 新增导入

def df_to_excel(writer, df, sheet_name):
    if isinstance(df, pd.DataFrame):
        if len(df.index) > 0:
            df.to_excel(writer, sheet_name=str(sheet_name), index=False)

def save_xlsx(df_active=None, df_earnings=None):
    # 用BytesIO在内存存储Excel数据,替代本地路径
    output = io.BytesIO()
    writer = pd.ExcelWriter(output, engine='xlsxwriter')
    
    df_to_excel(writer, df_active, sheet_name=_('active'))
    df_to_excel(writer, df_earnings, sheet_name=_('active_history'))
    
    # 保存后重置文件指针到开头,否则读取时会从末尾开始(空内容)
    writer.close()
    output.seek(0)
    
    return output

def extract_data(request, context, plan, year, data_type, data_from_list=False):
    date_statement = datetime.date(year, 12, 31)

    members = Member.objects.filter(plan=plan)
    active = members.filter(
        Q(date_of_termination__gt=date_statement) |
        Q(date_of_termination__isnull=True)).filter(
        date_joined_plan__lte=date_statement)
    df_active, df_earnings = extract_data_active(
        request,
        plan,
        year,
        active,
        date_statement,
        date_previous,  # 注意:需确保该变量已提前定义
        data_type,
    )

    # 调用修改后的save_xlsx,无需传入本地路径
    excel_file = save_xlsx(
        df_active=df_active,
        df_earnings=df_earnings,
    )

    return excel_file

2. 修改views.py中的保存逻辑

现在excel_file是符合要求的BytesIO对象,直接传入Django的File类即可:

# ... 其他代码不变
excel_file = extract_data(
    request,
    context,
    plan,
    year,
    data_type,
    data_from_list
)
today = datetime.date.today()
file_name = "data_" + str(year) + ".xlsx"
doc = DocuPlan(
    user=request.user,
    plan=plan,
    doc_type=DocuPlanType.DATA,
    date_order=plan.year_end(year),
    date_published=today,
    is_public=False,
    date_filed=today,
    file_name=file_name,
)
# 直接用BytesIO对象创建Django File实例
file = File(excel_file, name=file_name)
doc.document.save(file_name, file, save=True)

关键注意点

  • 使用io.BytesIO避免了本地文件IO开销,无需后续清理临时文件
  • 必须执行output.seek(0)重置文件指针,否则Django读取时会得到空内容
  • BytesIO实现了read()方法,完全符合Django File类的要求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 19:10:40