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