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表格沒有實現,后續自己慢慢完善該功能。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM