Table of Contents
功能需求描述
用户需要以网页为媒介,从DB里拉取一些数据。
分为两种情况:
- data已经显示在页面上 (除了下载还需要拷贝其中某一列)
- data没有显示在页面上,需要在trigger download的功能时,根据用户给定的条件,去后台query data,然后下载下来。
一 使用Datatables实现纯前端下载、拷贝
对于第一种情况,由于data是通过datatables插件展示的,且已经从DB拉到了页面上,也就没有必要为了下载它们再跑到查询一遍数据库了。
datatables已经给了我们封装好的插件:
(link:https://datatables.net/extensions/buttons/examples/html5/simple.html )
除了“XXHtml5”以外,还有 copy
, excel
, csv
and pdf
四种。它们与XXHtml5之间的区别暂时还没有搞清楚。
我使用的是“csv”button,只是这样写就可以实现下载的功能:
table = $('#psumsTable').DataTable( {//不能使用.dataTable。二者返回值不同。在这里我们需要使用datatable插件自带的api,因此必须使用DataTable。
//...
"dom": '<"user_button"B>ptlTgi',//指定button到datatable的左上角。会自动生成一个user_button的div,将所有的button包裹起来
"buttons": [
{extend: 'csv', //button的功能,包括csv,copy,excel等。这里不能自由发挥,只能使用datatables插件定好的值
text:'export' //button上显示的文字
}
],
很简单,但接下来在实现copy指定的某一行时遇到了困难。因为默认情况下,该功能会将datatable中所有的行和列都导出/拷贝出来。但我只要其中一列呀,其他的必须去掉。
于是去研究copy的文档:
也就是说,datatable的button插件提供customize选项。该选项提供三个参数: 被拷贝的data(字符串类型),button 配置对象,以及Datatables的API实例,这个实例是当前button所属的那个datatable插件的,也就是说,就是我要从中拷贝数据的那个datatable实例。
而它返回的是什么呢? The value that the function returns is the value that will be used for the export.也就是会被拷贝的数据。
也就是说,我可以使用这个option给我的Datatables API实例(第三个参数),找到我想要下载的那一列,作为返回值返回即可。
最终的code大致如下:
"buttons": [
{extend: 'copy', text: 'Copy',
customize: function(data, config, api){
var copy_list = api.column('need_copy:name').data();//注意name的用法。这里通过这种方式来让api找到我想copy的那一列
var copy_data = copy_list .join('\n'); //换行
alert('Copied to ClipBoard!'); //一个提示,有没有都行。
return copy_data;
}
},
{extend: 'csv', text:'export'} //下载功能。前面提到了。
],
//...
"columns": [
//...
{ "data": "column_n", "name": 'need_copy' },
注意上例中的api.column('need_copy:name').data()。因为实现这个功能还有一个坎儿:如何找到我要的那一列。
这就要查Datatables的column-selector了: https://datatables.net/reference/type/column-selector
同样的,csv等button也有customize选项。也可以实现类似的定制化功能。
二 query db 实现下载(django)
以前是这样实现的:
step1:根据前台传回来的搜索条件,到db里头query data。
step2:使用FileResponse下载下来。
代码也很简单,网上一搜一大把:
from django.http import FileResponse
import pandas as pd
conn = pymssql.connect(...) # 连DB
df = pd.read_sql(sql, conn) # query出来的data存在df中。df是dataframe
df.to_csv(csv_file, encoding='utf_8_sig') # csv_file是文件路径。
# add data to response
file = open(csv_file, 'rb')
response = FileResponse(file)
response['Content-Type'] = 'application/octet-stream'
response['Content-Disposition'] = 'attachment;filename="%s"' % output_file_name
前台js的话只需要location.href = XXX, 让上面这段代码可以执行就行了。
氮素!
这里头有一个隐形天坑!
注意到这句code了没有:
df.to_csv(csv_file, encoding='utf_8_sig')
它是将从db中拉出来的data先以csv格式保存在某个地方,然后再在后面打开使用FileResponse,将其传递给浏览器,让用户下载。
不是,我都要下载了,还找个地方存文件干嘛??脱裤子放屁???
但这一次万能的度娘没能直接告诉我答案。网络上能查找到的例子,多是将已经存在的文件下载下来。
我就不能直接把dataframe里头的data直接下载下来吗????(仰天长啸!)
于是浅浅地翻阅了一下django的官方文档:
https://docs.djangoproject.com/en/dev/ref/request-response/
HttpResponse 条目下:
嗯?因缺思厅。
那么这个“my_data”从哪里搞呢?
再去百度dataframe的to_csv()的文档:https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.to_csv.html
也就是说,如果我不对to_csv指定输出文件名,它就会将csv data以字符串的形式返回出来。
这不就是我要的my_data么!
然后就很顺利了:
output_file_name = '' # 这个文件名给用户看的,会在浏览器中显示
response = HttpResponse(df.to_csv(encoding='utf_8_sig'))
response['Content-Type'] = 'application/octet-stream'
# response['Content-Type'] = 'text/csv'
response['Content-Disposition'] = 'attachment;filename="%s"' % output_file_name
齐活~
另外,有关content_type和content_description的资料:
http://tools.jb51.net/table/http_content_type/
https://www.jianshu.com/p/4c52cb691f54