Django项目实现指定生成excel并导出本地功能

博客原创,作者: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 import os,django,sys
 3 BASE_DIR =os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
 4 sys.path.append(BASE_DIR)
 5 
 6 from Log.models import ActionLog,SSHLog
 7 from OM.models import ServerGroup,ServerList
 8 from Matrix.models import BaseInfo,ConfigInfo,Platform,BusinessUnit,DomainInfo,DnsInfo,ZabbixAlertInfo,Asset
 9 
10 import datetime
11 import xlwt
12 
13 
14 def BulidNewExcel(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 in db_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 in field_name_list]
45     #config表中字段替换
46     field_name_list = ['baseid_id' if x == 'baseid' else x for x in field_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 in field_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 in range(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 = 0
78     for i in log_obj.values():
79         mylist.append([])
80         for j in range(len(field_name_list)):
81             mylist[num].append(i[field_name_list[j]])
82         num+=1
83 
84     for i in range(0,log_obj.count()):
85         for j in range(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 import FileHandle
 2 def BulidData(request):
 3     dbname = request.POST.get('dbname')
 4     # return HttpResponse(dbname)
 5     ret = FileHandle.BulidNewExcel('/var/www/html/dtop/download/file/excel/',dbname)
 6     return HttpResponse(ret)
 7 
 8 
 9 def download(request,offset):
10     # ret = FileHandle.BulidNewExcel('/var/www/html/dtop/download/file/excel/')
11     from django.http import StreamingHttpResponse
12     def file_iterator(file_name,chunk_size=512):
13         with open(file_name) as f:
14             while True:
15                 c = f.read(chunk_size)
16                 if c:
17                     yield c
18                 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界面:

<input id="downData" class="btn btn-info" type="button" value="导出" name="Asset" onclick="downData();">
View Code

说明如下:为了简化,我只写了一个button,注意这里的name属性值最终是要传递给后端,与FileHadel函数里的字典进行匹配,需要取不同数据库的excel,改这里就可以了。

js:

 1 function downData(){
 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表格没有实现,后续自己慢慢完善该功能。

posted @ 2016-09-23 09:15  BruceBee  阅读(11943)  评论(0编辑  收藏  举报