django + vue导出功能实现下

395 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第24天,点击查看活动详情

0 环境

  • 编辑器:idea
  • 系统版本:win10
  • python版本:3.9.6

1 前期准备

后端库需要pip安装好xlsxwriter,还有一点导入时用到xlrd的时候,要注意xlrd的版本,不能超过2.xx版本,超过了出现提示不支持xlsx这类,请卸载,安装旧版本,比如我用的是1.2.0版本。

2 后端(django)

主要的重担在于后端,由于刚用django开发,时间紧,drf的精髓还么有时间去学习应用,先用fbv垫垫肚子吧。其实并不是直接发给前端的,而是先服务器保存成excel,前端直接读取那个相对于的url即可。

1 设置excel写入路径

1、settings设置写入路径

import os
from pathlib import Path


BASE_DIR = Path(__file__).resolve().parent.parent

# 路由地址
MEDIA_URL = "/upload/"

# 写入路径
MEDIA_ROOT = BASE_DIR / 'upload'

2、urls挂载 在urls中输入如下代码:

from . import settings
from django.conf.urls.static import static

urlpatterns = [
    这里建一个导出功能的路由
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

2 导出功能的代码实现

由于前端是url,不需要加相应访问方式限制。我们的需求是将标题和数据写入到一个sheet中去,首先设置文件名,并且文件名被格式化为文件名后面追加当前时间,这样的好处不会使文件名重名。标题,设置写入到服务器的位置。xlsxwriter创建一个excel,添加sheet,第一行写入相应的标题,从第二行开始写入数据,我们先要从数据库中获取数据(values里加入对应的标题名的字段),在循环中若是需要转换,具体转换,不需要的话,直接将数据传给临时列表,然后写入行,行加1,循环往复,直接最终写入结束,关闭写入。这里的写入到excel用到了yield,性能上会好很多。

还有一点:后端要配置好前端发送的url相对应的路由,不要设置get,post。

import datetime
import os
import xlsxwriter
from 某个app名 import settings

def download(request):
    file_name = "download"

    title = [u'a', u'b', u'c', u'd' , u'e', u'f',u'g', u'f']
    
    filename = '{}_{}.xlsx'.format(file_name,datetime.datetime.now().strftime("%Y%m%d%H%M%S")) #指定excel文件名
    # PROJECT_HOME = os.path.dirname(os.path.abspath(__file__))
    DOWNLOAD_DIR = os.path.join(settings.MEDIA_ROOT, "excel") #指定存放文件的目录
    file_path = "{}/{}".format(DOWNLOAD_DIR,filename)
    workbook = xlsxwriter.Workbook(file_path)  # 这里我用的xlsxwriter导出文件
    table = workbook.add_worksheet()
    table.write_row('A1', title)
    tmp_line = 2
    list_obj = 对应model.objects.all().values("id","xx","xx","xx","xx","xx","xx")
    # print("list_obj ==>", list_obj)
    for l in list_obj:
        # tmp = [] #todo 指定每一行的数据
        tmp = []
        try:
            在这里对需要转换的数据进行改造,存入到tmp中,
            比如需要读取其他model,可以用filter + values_list得到一个元组,当然还有其他更好的方式
        except Exception:
            pass

        table.write_row("A{}".format(tmp_line), tmp) #自行去查一下write_row方法
        tmp_line += 1
    workbook.close()

    response = StreamingHttpResponse(file_iterator(file_path))
    response['Content-Type'] = 'application/octet-stream'
    # 适应 中文.xlsx
    response['Content-Disposition'] = "attachment; filename*=utf-8''{}".format(escape_uri_path(file_name+".xlsx"))
    # response['Content-Disposition'] = "attachment;filename={0}".format(filename)
    return response

def file_iterator(file_name):
    with open(file_name, 'rb') as f:  # 切记open打开文件的方式
        while True:
            c = f.read()
            if c:
                yield c
            else:
                break

5 前后端联调

当我们点击前端的导出功能,相应的后端会在这个upload/excel目录生成一个带有时间的xlsx。

image.png

6 总结

前端点击了导出按钮,触发了a标签,发送了一个URL给后端,后端设置了相应的路由,我们在相应的路由对应了相应的方法,在这个方法,写出excel用的是xlsxwriter,因为它可以满足大数据量。重回方法里,一个表格是不是要有文件名,文件薄又分为头和数据(字段的中文标题和字段的数据库数据),需要提供数据库相关字段的数组(标题名数组,数据数组),因为要先保存到服务器上,需要先设置保存的路径,创建excel,将标题名数组写入到excel中,若是数据数组需要转换,先循环转化,在行写入到excel,直接循环结束,关闭写操作,保存到服务器上指定的路径。设置响应Content-Type和Content-Disposition,然后返回给前端。