博客原创,作者:BruceBee,转载请标明出处,谢谢!
最近在用django写一个项目,项目当中有一处功能,需要在前端进行数据的选择,然后生成对应的excel表格并进行下载到本地。
将此功能进行拆解:
一、前端进行内容选择,生成excel表格
二、后端生成的excle表格供前端进行下载
python中提供的xlwt模块即可以实现生成excel表格,前后端的信息交互采用ajax,文件下载采用web前端访问url形式实现。
一、后端生成excel
现在我的项目目录下新建一个download目录,用于存储download的py文件和生成的excel文件,其中FileHandle.py为处理excel的主函数。
[root@localhost download]#tree
.
├── core
│?? ├── FileHandle.py
│?? ├── FileHandle.pyc
│?? ├──__init__.py
│?? └──__init__.pyc
├── file
│?? ├── csv
│?? └── excel
├──__init__.py
└──__init__.pyc
View Code
FileHandle.py:
1 #-*- coding:utf-8 -*-
2 importos,django,sys3 BASE_DIR =os.path.dirname(os.path.dirname(os.path.abspath(__file__)))4 sys.path.append(BASE_DIR)5
6 from Log.models importActionLog,SSHLog7 from OM.models importServerGroup,ServerList8 from Matrix.models importBaseInfo,ConfigInfo,Platform,BusinessUnit,DomainInfo,DnsInfo,ZabbixAlertInfo,Asset9
10 importdatetime11 importxlwt12
13
14 defBulidNewExcel(download_url,dbname):15 db_dict={16 'BaseInfo':BaseInfo,17 'ConfigInfo':ConfigInfo,18 'Platform':Platform,19 'BusinessUnit':BusinessUnit,20 'DomainInfo':DomainInfo,21 'DnsInfo':DnsInfo,22 'ZabbixAlertInfo':ZabbixAlertInfo,23 'Asset':Asset,24 'ActionLog':ActionLog,25 'SSHLog':SSHLog,26 'ServerGroup':ServerGroup,27 'ServerList':ServerList,28 }29 style0 = xlwt.easyxf('font: name Times New Roman, color-index red, bold on')30 style1 = xlwt.easyxf(num_format_str='D-MMM-YY')31 #获取字段名(列表)
32 field_name_list =[]33 field_verbose_name_list =[]34
35 #for i in models.SSHLog._meta.get_fields():
36 for i indb_dict[dbname]._meta.get_fields():37 field_name_list.append(i.name)38 if db_dict[dbname] ==BaseInfo or db_dict[dbname] ==Platform or db_dict[dbname] ==BusinessUnit or db_dict[dbname] ==DomainInfo:39 field_verbose_name_list.append(i.name)40 else:41 field_verbose_name_list.append(i._verbose_name)42
43 #Dns表中字段替换
44 field_name_list = ['Domain_name_id' if x == 'Domain_name' else x for x infield_name_list]45 #config表中字段替换
46 field_name_list = ['baseid_id' if x == 'baseid' else x for x infield_name_list]47
48 #plat、buss表字段替换
49 if 'baseinfo' in field_name_list:field_name_list.remove('baseinfo')50 if 'baseinfo' in field_verbose_name_list:field_verbose_name_list.remove('baseinfo')51 #domain表字段替换
52 if 'dnsinfo' in field_name_list:field_name_list.remove('dnsinfo')53 if 'dnsinfo' in field_verbose_name_list:field_verbose_name_list.remove('dnsinfo')54
55 #base表字段替换
56 if 'configinfo' in field_name_list:field_name_list.remove('configinfo')57 if 'business_unit' in field_name_list:field_name_list.remove('business_unit')58 if 'configinfo' in field_verbose_name_list:field_verbose_name_list.remove('configinfo')59 if 'business_unit' in field_verbose_name_list:field_verbose_name_list.remove('business_unit')60
61 field_name_list = ['isp_id' if x == 'isp' else x for x infield_name_list]62
63
64 style0 = xlwt.easyxf('font: name Times New Roman, color-index red, bold on')65 style1 = xlwt.easyxf(num_format_str='D-MMM-YY')66
67 wb =xlwt.Workbook()68 ws = wb.add_sheet('Sheet',cell_overwrite_ok=True)69 for i inrange(len(field_verbose_name_list)):70 ws.write(0,i,field_verbose_name_list[i],style0)71
72 mylist=[]73
74 log_obj =db_dict[dbname].objects.all()75
76
77 num =078 for i inlog_obj.values():79 mylist.append([])80 for j inrange(len(field_name_list)):81 mylist[num].append(i[field_name_list[j]])82 num+=1
83
84 for i inrange(0,log_obj.count()):85 for j inrange(len(field_verbose_name_list)):86 ws.write(i+1,j,mylist[i][j])87 timestr=datetime.datetime.now().strftime("%Y%m%d%H%M%S")88 wb.save(download_url+'New-'+timestr+'.xls')89 return timestr
View Code
说明如下:
2-4行:BASE_DIR为获取问文件路径,并将其添加到django的包路径中,供django的app调用;
6-8行:项目本地的models里面的名称,,由于我有三个app,为避免混淆我分别导入。可根据自己的情况修改;
14行:传入2个参数,分别为我定义好的文件存储路径和需要对应的数据库名称;
15-28行:定义字符串与实际数据库名为k-v关系的字典;
32-33行:定义两个列表,目的是将从models中获取的models的字段名和字段中文名提取出来(name与verbose_name),这里需要注意的是:如果你的models关系里没有定义verbose_name,那么提取出来的verbose_name将为空,多对多关系的字段没有verbose_name属性,直接取的话会报错;
36-61行:本项目实际情况对两个列表进行的处置动作,目的是得到最终的表格头部的内容,可忽略;
64-70行:实例化一个表格对象,使表格支持重复覆盖(即写动作),将列表内容写入表格的第一行,即得到表头。
74行:提取对应modles的内容,得到一个QuerySet对象,遍历这个对象,每个key就是一个记录,以字典形式呈现;
78-82行:嵌套循环,目的是生成一个列表,由每一条字段值组成的小列表,这些小列表为元素,组成一个大列表;
84-86行:嵌套循环这个大列表,将列表中的值写入到表格对象中;
87-89行:生成时间,将excel表格命名为New-‘时间格式’.xls,保存,返回该时间格式字符串;
至此,excel文件生成完成,但是潜在一个问题:就是当需要生成的数据量足够大的时候,这个转换列表也就足够大,其占用内存必然会很大。
view.py
1 from download.core importFileHandle2 defBulidData(request):3 dbname = request.POST.get('dbname')4 #return HttpResponse(dbname)
5 ret = FileHandle.BulidNewExcel('/var/www/html/dtop/download/file/excel/',dbname)6 returnHttpResponse(ret)7
8
9 defdownload(request,offset):10 #ret = FileHandle.BulidNewExcel('/var/www/html/dtop/download/file/excel/')
11 from django.http importStreamingHttpResponse12 def file_iterator(file_name,chunk_size=512):13 with open(file_name) as f:14 whileTrue:15 c =f.read(chunk_size)16 ifc:17 yieldc18 else:19 break
20
21 the_file_name ='New-'+offset+'.xls'
22 response = StreamingHttpResponse(file_iterator('/var/www/html/dtop/download/file/excel/New-'+offset+'.xls'))23 response['Content-Type'] = 'application/octet-stream'
24 response['Content-Disposition'] = 'attachment;filename="{0}"'.format(the_file_name)25
26 return response
View Code
说明如下:
1行:从前面的download目录中导入表格生成函数
2-7行:BulidData函数调用生成表格,得到该表格名称的时间字符串
9-26行:download函数根据用户传进来的offset值,拼接成文件名称字符串,并到指定目录取回该文件,以http流方式返回给前端,即实际的下载功能。
url.py:
urlpatterns =[
url(r'^BulidData/', Matrix.views.BulidData),
url(r'^download/(\w+)*/$', Matrix.views.download),
]
View Code
说明如下:
在url里添加以上两个函数的路由信息;注意download函数采用动态url的方式获取用户的参数,这里参数实际上是一串日期字符串,即前面的Filehadle函数返回的timestr
二:前端交互并下载
html界面:
View Code
说明如下:为了简化,我只写了一个button,注意这里的name属性值最终是要传递给后端,与FileHadel函数里的字典进行匹配,需要取不同数据库的excel,改这里就可以了。
js:
1 functiondownData(){2 var inputChecks=$("input:checkbox[name='dataFrom_check']:checked");3 if(inputChecks.length==0){4 layer.alert('请选中导出项!');5 return;6 }7 var dbname =$("#downData").attr("name")8
9 $.ajax({10 type:'POST',11 url:'/BulidData/',12 dataType:'text',13 data:{'dbname':dbname},14 success:function(text){15 var url ='/download/'+text;16 window.location.href=url;17 },error:function(){18 alert('导出失败');19 }20 });21
22 }
View Code
说明如下:
定义了一个downData函数,获取指定DOM元素的的name属性,通过ajax传递给BulidData函数,生成excel,得到该excel文件时间字符串
16行:window.location.href=url,ajax访问该url,即实际的下载功能。
至此,前端点击“导出”按钮,即可实现后端生成excel并下载只本地,功能实现。但是这个功能还有一些地方不完善,除了前面提到的转换形成的列表是个潜在的因素以外(后来测试了一下,生成一个8000条的excel表格,内存2G的虚拟机mysql瞬时的占用内存才不到10%,CPU使用率不到5%,从前端点击到下载到本地,感觉耗时不到半秒钟)。自定义内容的excel表格没有实现,后续自己慢慢完善该功能。