用Python轻松开发数据库取数下载工具

简介: 用Python轻松开发数据库取数下载工具

1 简介

这是我的系列教程「Python+Dash快速web应用开发」的第十四期,在前两期中,我们针对dash_table的自定义样式、前后端分页、单元格内容编辑等特点展开了介绍。

而在dash_table中还有很多高级特性,可以极大程度上丰富DataTable()所渲染网页表格的交互能力,今天的文章作为「交互表格篇」的下篇,我们就来一起学习其中比较实用的一些特性。

图1

2 dash_table的更多实用功能

2.1 更多表格交互特性

上一期文章最后我们学习了通过设置参数editable=True,使得渲染出的表格可以通过鼠标双击进行编辑,而dash_table除此之外,还有更多实用的交互能力:

2.1.1 按列排序

  • 「普通单列排序」

DataTable()中,我们只需要设置参数sort_action='native',即可开启列排序功能,此时每一列列名单元格内都会出现部件供我们点击切换排序方式:

app1.py

import dash
import dash_table
import dash_bootstrap_components as dbc
import seaborn as sns
df = sns.load_dataset('iris')
app = dash.Dash(__name__)
app.layout = dbc.Container(
    [
        dash_table.DataTable(
            data=df.to_dict('records'),
            columns=[
                {'name': column, 'id': column}
                for column in df.columns
            ],
            style_table={
                'height': '500px',
                'overflow-y': 'auto'
            },
            sort_action='native'
        )
    ],
    style={
        'margin-top': '50px'
    }
)
if __name__ == '__main__':
    app.run_server(debug=True)

图2

  • 「基于后端排序的多列排序」

DataTable()中设置sort_action='native'时,对应的是「按列排序」的前端模式,也即是数据一次性灌注到浏览器的前提下进行排序,这种方式不仅不适合大型数据集,而且只支持「单列排序」

而当数据渲染方式为后端模式时,我们通过设置参数sort_action='custom'以及sort_mode='multi',配合在回调中获取属性sort_by中记录的参与排序的列名及升序降序方式,就可以实现多列排序。

我们在上一期的app2.py的基础上修改得到下面的例子:

app2.py

import dash
import dash_bootstrap_components as dbc
import dash_table
from dash.dependencies import Input, Output
import seaborn as sns
df = sns.load_dataset('iris')
df.insert(0, '#', df.index)
app = dash.Dash(__name__)
app.layout = dbc.Container(
    [
        dbc.Spinner(
            dash_table.DataTable(
                id='dash-table',
                columns=[
                    {'name': column, 'id': column}
                    for column in df.columns
                ],
                page_size=15,  # 设置单页显示15行记录行数
                page_action='custom',
                page_current=0,
                style_header={
                    'font-family': 'Times New Romer',
                    'font-weight': 'bold',
                    'text-align': 'center'
                },
                style_data={
                    'font-family': 'Times New Romer',
                    'text-align': 'center'
                },
                sort_action='custom',
                sort_mode='multi'
            )
        )
    ],
    style={
        'margin-top': '50px'
    }
)
@app.callback(
    [Output('dash-table', 'data'),
     Output('dash-table', 'page_count')],
    [Input('dash-table', 'page_current'),
     Input('dash-table', 'page_size'),
     Input('dash-table', 'sort_by')]
)
def refresh_page_data(page_current, page_size, sort_by):
    if sort_by:
        return (
            df
            .sort_values(
                [col['column_id'] for col in sort_by],
                ascending=[
                    col['direction'] == 'asc'
                    for col in sort_by
                ]
            )
            .iloc[page_current * page_size:(page_current + 1) * page_size]
            .to_dict('records'),
            1 + df.shape[0] // page_size
        )
    return (
        df.iloc[page_current * page_size:(page_current + 1) * page_size].to_dict('records'),
        1 + df.shape[0] // page_size
    )
if __name__ == '__main__':
    app.run_server(debug=True)

图3

2.1.2 按列条件筛选

除了基于指定字段进行排序之外,dash_table还支持列的条件筛选,设置filter_action="native",就可以开启基础的按列条件筛选功能,此时每一列表头下都会多出供用户输入筛选条件的单元格:

app3.py

import dash
import dash_table
import dash_bootstrap_components as dbc
import seaborn as sns
df = sns.load_dataset('iris')
app = dash.Dash(__name__)
app.layout = dbc.Container(
    [
        dash_table.DataTable(
            data=df.to_dict('records'),
            columns=[
                {'name': column, 'id': column}
                for column in df.columns
            ],
            # 自定义条件筛选单元格样式
            style_filter={
                'font-family': 'Times New Romer',
                'background-color': '#e3f2fd'
            },
            style_table={
                'height': '500px',
                'overflow-y': 'auto'
            },
            style_header={
                'font-family': 'Times New Romer',
                'font-weight': 'bold',
                'text-align': 'center'
            },
            style_data={
                'font-family': 'Times New Romer',
                'text-align': 'center'
            },
            filter_action="native"
        )
    ],
    style={
        'margin-top': '50px'
    }
)
if __name__ == '__main__':
    app.run_server(debug=True)

图4

dash_table中自带的条件筛选语法很丰富,有条件的朋友可以前往https://dash.plotly.com/datatable/filtering了解更多。

dash_table同样可以实现后端筛选,和前面的后端排序类似,主要利用filter_query属性的回调变化在后台基于pandas等框架进行数据筛选,比较简单,这里就不再赘述。

2.2 自带的数据表格下载功能

dash_table还自带了将当前所渲染的表格内容直接下载为csvxlsx格式文件的简易功能,通过参数export_format设置导出的文件格式,但自带的下载按钮样式比较丑,如果你对此有比较高的要求,还是建议结合之前的「上传下载篇」自己设计相关功能:

图5

2.3 冻结首行

通过设置参数fixed_rows={'headers': True},我们可以实现下滑查看表格的过程中,始终保持表头被冻结:

图6

3 开发一个在线取数工具

在学习完今天的内容之后,我们来结合之前「上传下载篇」中提到的下载功能,来制作一个简单的对指定数据库中的数据表进行快速条件筛选并下载的工具,其中DataTablederived_virtual_data属性记录了经过排序、条件筛选等操作后当前显示的表格数据:

图7

app4.py

import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.dependencies import Input, Output
from flask import send_from_directory
import os
import uuid
from sqlalchemy import create_engine
import pandas as pd
try:
    os.mkdir("downloads")
except FileExistsError:
    pass
engine = create_engine('mysql+pymysql://root:mysql@localhost/DASH')
app = dash.Dash(__name__)
@app.server.route('/download/<file>')
def download(file):
    return send_from_directory('downloads', file)
app.layout = dbc.Container(
    [
        dbc.Row(
            [
                dbc.Col(dbc.Button('更新数据表', id='refresh-tables', style={'width': '100%'}), width=2),
                dbc.Col(dcc.Dropdown(id='table-select', style={'width': '100%'}), width=2)
            ]
        ),
        html.Hr(),
        dash_table.DataTable(
            id='dash-table',
            editable=True,
            page_size=15,
            style_header={
                'font-family': 'Times New Romer',
                'font-weight': 'bold',
                'text-align': 'center'
            },
            style_data={
                'font-family': 'Times New Romer',
                'text-align': 'center'
            },
            style_data_conditional=[
                {
                    # 对选中状态下的单元格进行自定义样式
                    "if": {"state": "selected"},
                    "background-color": "#b3e5fc",
                    "border": "none"
                },
            ],
            filter_action="native"
        ),
        html.Br(),
        html.A(id='download-url', target="_blank")
    ],
    style={
        'margin-top': '50px'
    }
)
@app.callback(
    Output('table-select', 'options'),
    Input('refresh-tables', 'n_clicks')
)
def refresh_tables(n_clicks):
    if n_clicks:
        return [
            {
                'label': table,
                'value': table
            }
            for table in pd.read_sql_query('SHOW TABLES', con=engine)['Tables_in_dash']
        ]
    return dash.no_update
@app.callback(
    [Output('dash-table', 'data'),
     Output('dash-table', 'columns')],
    Input('table-select', 'value')
)
def render_dash_table(value):
    if value:
        df = pd.read_sql_table(value, con=engine)
        return df.to_dict('records'), [
            {'name': column, 'id': column}
            for column in df.columns
        ]
    else:
        return [], []
@app.callback(
    [Output("download-url", "href"),
     Output("download-url", "children")],
    [Input("dash-table", "derived_virtual_data"),
     Input("dash-table", "filter_query")],
    prevent_initial_call=True
)
def download_table(derived_virtual_data, filter_query):
    if derived_virtual_data:
        print(derived_virtual_data)
        filename = f"output_{uuid.uuid1()}.xlsx"
        pd.DataFrame(derived_virtual_data).to_excel("downloads/" + filename, index=False)
        return "/download/" + filename, "下载当前状态表格"
    return "", ""
if __name__ == '__main__':
    app.run_server(debug=True)
相关文章
|
3天前
|
存储 数据库连接 API
Python环境变量在开发和运行Python应用程序时起着重要的作用
Python环境变量在开发和运行Python应用程序时起着重要的作用
35 15
|
10天前
|
JSON 安全 API
如何使用Python开发API接口?
在现代软件开发中,API(应用程序编程接口)用于不同软件组件之间的通信和数据交换,实现系统互操作性。Python因其简单易用和强大功能,成为开发API的热门选择。本文详细介绍了Python开发API的基础知识、优势、实现方式(如Flask和Django框架)、实战示例及注意事项,帮助读者掌握高效、安全的API开发技巧。
37 3
如何使用Python开发API接口?
|
3天前
|
JSON API 数据格式
如何使用Python开发1688商品详情API接口?
本文介绍了如何使用Python开发1688商品详情API接口,获取商品的标题、价格、销量和评价等详细信息。主要内容包括注册1688开放平台账号、安装必要Python模块、了解API接口、生成签名、编写Python代码、解析返回数据以及错误处理和日志记录。通过这些步骤,开发者可以轻松地集成1688商品数据到自己的应用中。
16 1
|
9天前
|
数据采集 存储 JSON
Python爬虫开发中的分析与方案制定
Python爬虫开发中的分析与方案制定
|
14天前
|
算法 测试技术 开发者
性能优化与代码审查:提升Python开发效率
性能优化与代码审查:提升Python开发效率
27 1
|
8天前
|
SQL 关系型数据库 MySQL
go语言数据库中mysql驱动安装
【11月更文挑战第2天】
22 4
|
6天前
|
SQL 关系型数据库 MySQL
12 PHP配置数据库MySQL
路老师分享了PHP操作MySQL数据库的方法,包括安装并连接MySQL服务器、选择数据库、执行SQL语句(如插入、更新、删除和查询),以及将结果集返回到数组。通过具体示例代码,详细介绍了每一步的操作流程,帮助读者快速入门PHP与MySQL的交互。
19 1
|
1月前
|
存储 关系型数据库 MySQL
Mysql(4)—数据库索引
数据库索引是用于提高数据检索效率的数据结构,类似于书籍中的索引。它允许用户快速找到数据,而无需扫描整个表。MySQL中的索引可以显著提升查询速度,使数据库操作更加高效。索引的发展经历了从无索引、简单索引到B-树、哈希索引、位图索引、全文索引等多个阶段。
61 3
Mysql(4)—数据库索引
|
15天前
|
监控 关系型数据库 MySQL
数据库优化:MySQL索引策略与查询性能调优实战
【10月更文挑战第27天】本文深入探讨了MySQL的索引策略和查询性能调优技巧。通过介绍B-Tree索引、哈希索引和全文索引等不同类型,以及如何创建和维护索引,结合实战案例分析查询执行计划,帮助读者掌握提升查询性能的方法。定期优化索引和调整查询语句是提高数据库性能的关键。
77 1
|
17天前
|
关系型数据库 MySQL Linux
在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。
本文介绍了在 CentOS 7 中通过编译源码方式安装 MySQL 数据库的详细步骤,包括准备工作、下载源码、编译安装、配置 MySQL 服务、登录设置等。同时,文章还对比了编译源码安装与使用 RPM 包安装的优缺点,帮助读者根据需求选择最合适的方法。通过具体案例,展示了编译源码安装的灵活性和定制性。
59 2