如何将内存中生成的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()方法,完全符合DjangoFile类的要求
内容的提问来源于stack exchange,提问作者guy007
相关产品推荐
相关产品推荐

