ajax 回调数据 刷新table_Python Django+SQL+Pandas+Pyecharts自建在线数据分析平台(十一)...

(一)需求分析&技术实现

(二)初步搭建Django环境

(三)页面布局&Django模板

(四)SQL+Pandas初步处理数据

(五)前端表单交互

(六)Ajax异步传参与加载

(七)前端数据格式的处理

(八)DataTables接管前端表格

(九)Pyecharts实现交互图表

(十)静态图表的展示

(十一)“导出数据至Excel”功能

(十二)添加和配置缓存

(十三)用户登录系统

(十四)部署Django至生产环境

本章实现最后一个必须的功能“导出数据至Excel”,因为目标很明确,实现是比较简单直接的,但也有一些需要注意的地方。

首先,我们需要明确导出数据的response这次不再适合用AJAX回调函数调用了,我们的整个导出功能需要和查询功能query方法并行,也就是需要完全新建一个方法以及相应URL。

为了复用,我们先把之前views.py文件里query方法的前一部分改写成get_df方法:

def get_df(form_dict, is_pivoted=True):
    sql = sqlparse(form_dict)  # sql拼接
    df = pd.read_sql_query(sql, ENGINE)  # 将sql语句结果读取至Pandas Dataframe

    if is_pivoted is True:
        dimension_selected = form_dict['DIMENSION_select'][0]
        if dimension_selected[0] == '[':

            column = dimension_selected[1:][:-1]
        else:
            column = dimension_selected

        pivoted = pd.pivot_table(df,
                                 values='AMOUNT',  # 数据透视汇总值为AMOUNT字段,一般保持不变
                                 index='DATE',  # 数据透视行为DATE字段,一般保持不变
                                 columns=column,  # 数据透视列为前端选择的分析维度
                                 aggfunc=np.sum)  # 数据透视汇总方式为求和,一般保持不变
        if pivoted.empty is False:
            pivoted.sort_values(by=pivoted.index[-1], axis=1, ascending=False, inplace=True)  # 结果按照最后一个DATE表现排序

        return pivoted
    else:
        return df


def query(request):
    form_dict = dict(six.iterlists(request.GET))
    pivoted = get_df(form_dict)

    table = ptable(pivoted)
    table = table.to_html(formatters=build_formatters_by_col(table),  # 逐列调整表格内数字格式
                          classes='ui selectable celled table',  # 指定表格css class为Semantic UI主题
                          table_id='ptable'  # 指定表格id
                          )

    # Pyecharts交互图表
    bar_total_trend = json.loads(prepare_chart(pivoted, 'bar_total_trend', form_dict))

    # Matplotlib静态图表
    bubble_performance = prepare_chart(pivoted, 'bubble_performance', form_dict)
    context = {
        'market_size': kpi(pivoted)[0],
        'market_gr': kpi(pivoted)[1],
        'market_cagr': kpi(pivoted)[2],
        'ptable': table,
        'bar_total_trend': bar_total_trend,
        'bubble_performance': bubble_performance
    }

    return HttpResponse(json.dumps(context, ensure_ascii=False), content_type="application/json charset=utf-8") # 返回结果必须是json格式

后端实现导出功能新建的export方法也可以和query方法一样在开头调用get_df:

try:
    from io import BytesIO as IO # for modern python
except ImportError:
    from io import StringIO as IO # for legacy python
import datetime


def export(request, type):
    form_dict = dict(six.iterlists(request.GET))

    if type == 'pivoted':
        df = get_df(form_dict)  # 透视后的数据
    elif type == 'raw':
        df = get_df(form_dict, is_pivoted=False)  # 原始数

    excel_file = IO()

    xlwriter = pd.ExcelWriter(excel_file, engine='xlsxwriter')

    df.to_excel(xlwriter, 'data', index=True)

    xlwriter.save()
    xlwriter.close()

    excel_file.seek(0)

    # 设置浏览器mime类型
    response = HttpResponse(excel_file.read(),
                            content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')

    # 设置文件名
    now = datetime.datetime.now().strftime("%Y%m%d%H%M%S")  # 当前精确时间不会重复,适合用来命名默认导出文件
    response['Content-Disposition'] = 'attachment; filename=' + now + '.xlsx'
    return response

实现导出功能的Python写法五花八门,条条大路通罗马。我们这次代码的特点是使用了pandas的df.to_excel方法,需要加载xlsxwriter这个库。有些场景的导出方法则可能需要其他一些处理Excel的库。这段代码最需要注意的是最后设置浏览器mime类型和文件名部分的写法。

相应地,在url.py里再增加一个url pattern:

urlpatterns = [
    ...
    path(r'export/<str:type>', views.export, name='export'),
]

前端因为也不是一个AJAX URL就返回所有数据了,也产生了代码复用的需求,我们把filter.html下面这段获取表单选择结果的JS代码独立出来:

<script>
    function getForm(){
        // 获取单选下拉框的值
        var form_data = {
            "DIMENSION_select": $("#DIMENSION_select").val(),
            "PERIOD_select": $("#PERIOD_select").val(),
            "UNIT_select": $("#UNIT_select").val(),
        };

        // 获取多选下拉框的值
        var dict = {{ mselect_dict|safe }};
        for (key in dict) {
            var form_name = dict[key]['select'] + "_select";
            jquery_selector_id = "[id='" + form_name + "']";//因为我们的部分多选框id有空格,要用这种写法
            form_data[form_name] = $(jquery_selector_id).val();
        }

        return form_data
    }
</script>

在display.html里新建两个导出按钮,并写上相应的鼠标点击函数,注意这里的传参用了+ '?' + $.param()的写法:

<div class="ui pointing secondary menu">
    ...
    <a class="item" data-tab="export"><i class="download icon"></i>导出数据</a>
</div>
...
<div class="ui tab segment" data-tab="export">
    <div class="ui buttons">
        <input class="ui blue button" type='button' id='export_pivot' value="导出整理后时间序列数据"/>
    </div>
    <div class="ui buttons">
        <input class="ui blue button" type='button' id='export_raw' value="导出原始数据"/>
    </div>
</div>

<script>
    $("#export_pivot").click(function(){
        var form_data = getForm();

        var downloadUrl = '{% url 'chpa:export' 'pivoted' %}'+ '?' + $.param(form_data, true);
        window.location.href = downloadUrl;
    });

    $("#export_raw").click(function(){
        var form_data = getForm();

        var downloadUrl = '{% url 'chpa:export' 'raw' %}'+ '?' + $.param(form_data, true);
        window.location.href = downloadUrl;
    })
</script>

最后需要注意这里的超级大坑,还记得第五章传参时表单多选框[]结尾的问题吗,这里因为不是AJAX传参,这时的多选框又不以[]结尾传参了,这种前后的不一致可能会导致后端混乱。我们需要在后端涉及到的地方顾忌这个问题,比如SQL语句拼接的函数中:

def sqlparse(context):
    print(context)
    sql = "Select * from %s Where PERIOD = '%s' And UNIT = '%s'" % 
          (DB_TABLE, context['PERIOD_select'][0], context['UNIT_select'][0])  # 先处理单选部分

    # 下面循环处理多选部分
    for k, v in context.items():
        if k not in ['csrfmiddlewaretoken', 'DIMENSION_select', 'PERIOD_select', 'UNIT_select']:
            if k[-2:] == '[]':
                field_name = k[:-9]  # 如果键以[]结尾,删除_select[]取原字段名
            else:
                field_name = k[:-7]  # 如果键不以[]结尾,删除_select取原字段名
            selected = v  # 选择项
            sql = sql_extent(sql, field_name, selected)  #未来可以通过进一步拼接字符串动态扩展sql语句
    return sql

完成。

1eb8168e9fe542d50015d14945eb4e68.png
导出按钮可以视为一种可视化输出与其他可视化布局是并列关系

d9fe3c527c7a5dfa3a0006baadfb2784.png
第一种导出形式,透视为时间序列后的数据

d68746570d560db3813e2f22755e29b2.png
第二种导出形式,纯原始数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值