需求
开发环境:PYTHON3 + MYSQL + FLASK-SQLALCHEMY
需求:将mysql数据库查询出来的结果直接转为excel并从浏览器直接下载(不保存在服务器)
1、安装模块包:xlswriter
这个包的介绍可以自行百度,我这里后续有时间再补充更新,我们暂时直接切入主题。
首先,安装包:
pip install xlswriter
2、视图类
# order.py
from flask import request, make_response
from flask_restplus import Resource
from itpms.api.v1 import business
......
@api.route('/order/excel/<int:order_id>')
@api.param('order_id', 'order id')
class OrderExcelOperation(Resource):
"""
订单EXCEL相关API
"""
@api.doc('Export Order Purchaselist')
@api.response(return_code.Successful, 'export success')
def get(self, order_id):
"""导出订单的采购清单"""
respdata = business.export_purchaselist(order_id)
resp = make_response(respdata[0])
resp.headers["Content-Disposition"] = "attachment; filename={}.xlsx".format(respdata[1])
resp.headers['Content-Type'] = 'application/x-xlsx'
return resp
3、处理函数
好,上面视图类中引用的就是下面这里的处理函数
数据库模型我就不贴了(使用flask-sqlalchemy生成db对象,然后这里引入使用即可),这个处理函数中的查库以及结果循环你们按你自己的来即可
# business.py
import xlsxwriter
from io import BytesIO
from itpms import db
......
def export_purchaselist(order_id):
"""
导出订单的采购清单
:param order_id:
:return:
"""
order = Orders.query.get(order_id)
if not order:
raise exceptions.MyHttpNotFound('order not found')
purchaselist = db.session.query(PurchaseList.cicode, PurchaseList.sn, DeviceCategory.name, DeviceCategory.specs,
DeviceCategory.price, PurchaseList.kvm_ip, DeviceCategory.supplier).\
join(DeviceCategory, DeviceCategory.id == PurchaseList.device_category_id).\
filter(PurchaseList.order_id == order_id).all()
if not purchaselist:
raise exceptions.MyHttpNotFound('this order has not purchase list')
# sio = StringIO()
sio = BytesIO()
workbook = xlsxwriter.Workbook(sio, {'in_memory': True}) # xlwt.Workbook(encoding='ascii') # 写到IO中
worksheet = workbook.add_worksheet(name=order.order_num)
# worksheet.merge_range(0, 0, 0, 5, 'aaa') # 合并单元格
# worksheet.write(11, 2, '=SUM(1:10)') # 增加公式
# worksheet.set_default_row(35) # 设置默认行高
style1 = workbook.add_format({'font_size': '11', 'align': 'center', 'valign': 'vcenter', 'bold': True}) # 设置风格 'bg_color': '#34A0FF',
style2 = workbook.add_format({'font_size': '11', 'align': 'center', 'valign': 'vcenter', 'bold': False}) # 'font_color': '#217346'
worksheet.set_column('A:H', None, style2)
worksheet.set_column(0, 7, 20) # 设置列宽
title = ['序号', 'CICODE', '序列号', '型号', '配置', '价格', 'KVM IP', '供应商']
worksheet.write_row('A1', title, style1)
for i in range(len(purchaselist)):
data = [
i + 1,
purchaselist[i][0],
purchaselist[i][1],
purchaselist[i][2],
purchaselist[i][3],
purchaselist[i][4],
purchaselist[i][5],
purchaselist[i][6],
]
worksheet.write_row('A'+ str(i+2), data)
workbook.close()
sio.seek(0) # 将byte流再从头读取,之前已经写到最后一个byte了
resp = sio.getvalue() # 通过getvalue函数读取IO流
sio.close() # 关闭IO流
# 这里我是要同时返回IO字节流及订单编号,所以用了个list,不需要的话直接返回return resp即可
orderdata = [resp, order.order_num]
return orderdata
注意事项:踩过的坑
1、StringIO与BytesIO
import StringIO
这是python2.7的用法,python3.0后这个模块归入IO模块了
from io import StringIO
但是我们这里还是用不了,会报错:
TypeError: string argument expected, got 'bytes'
所以,我们这里用另外一个模块:BytesIO,就可以了哈。
2、用flask-restful的方式实现
网上的一些案例都是一些单独的函数处理,对于flask-restful的方式需要稍微留意以下几点,否则很容易会收到如下报错:
TypeError: Object of type 'Response' is not JSON serializable
就如同上面的案例所示:
(1)我们不能为该视图方法get()指定它的marshal模型
(2)在视图get()方法中返回值不能另外带http code,直接返回response对象即可
resp = make_response(respdata[0])
resp.headers["Content-Disposition"] = "attachment; filename={}.xlsx".format(respdata[1])
resp.headers['Content-Type'] = 'application/x-xlsx'
return resp # 这里不可以return resp, 200
参考文章:
(1).python3+xlsxwriter+flask 导出excel 作者: 科比龙篮
(2).flask中使用xlsxwriter导出excel文件 作者:潇湘旧友