zoukankan      html  css  js  c++  java
  • 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表格没有实现,后续自己慢慢完善该功能。

  • 相关阅读:
    出现 could not open jvm.cfg 的解决办法
    powerdesigner相关概念理解
    UML建模类图
    LAMP环境折腾
    ThinkPHP学习笔记1
    ubuntu14在kDE界面下的关于eclipse提示框黑色背景的修改!
    LAMP环境安装与apache配置
    Unix网络编程---第四次作业
    Unix网络编程---第三次作业
    Unix网络编程---第二次作业
  • 原文地址:https://www.cnblogs.com/mzpy1119/p/5898691.html
Copyright © 2011-2022 走看看