社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

秀啊,用Python快速开发在线数据库更新修改工具

Python绿色通道 • 5 年前 • 407 次点击  

↑ 关注 + 星标 ,每天学Python新技能
后台回复【大礼包 】送你Python自学大礼包


❝

本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes

❞

1 简介

这是我的系列教程「Python+Dash快速web应用开发」的第十三期,在上一期中,我们一起认识了Dash自带的交互式表格组件dash_table,并学会了如何自定义表格中不同部分的样式。

而今天的教程,我们将继续深入认识dash_table的更多交互方面的功能,学习如何为渲染出的表格分页,并添加动态内容修改等交互功能。

图1

2 dash_table的基础交互能力

dash_table的核心功能是赋予用户与图表进行快捷交互的能力,下面我们来学习其基础常用的一些交互功能:

2.1 分页翻页

当我们要展示的数据行数较多时,在网页中渲染可以选择分页,这在dash_table中实现起来比较方便,根据数据传递方式的不同,可以分为「前端分页」与「后端分页」:

2.1.1 前端分页

前端分页顾名思义,就是在我们访问Dash应用时,表格内所有页面的数据一次性加载完成,适合数据量不大的情况,将数据存储压力转移到浏览器端。

通过参数page_size 设置每页要显示的记录行数,Dash会自动帮我们分好页,并配上翻页部件:

❝

app1.py

❞
import dash
import dash_bootstrap_components as dbc
import dash_table

import seaborn as sns

df = sns.load_dataset('tips')
df.insert(0, '#', df.index)

app = dash.Dash(__name__)

app.layout = dbc.Container(
    [
        dash_table.DataTable(
            id='dash-table',
            data=df.to_dict('records'),
            columns=[
                {'name': column, 'id': column}
                for column in df.columns
            ],
            page_size=15,  # 设置单页显示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={
        'margin-top': '50px'
    }
)

if __name__ == '__main__':
    app.run_server(debug=True)
图2

### 2.1.2 后端分页

虽然前端分页简单易用,但当我们的数据很大时,强行使用前端分页会给「网络传输」和「浏览器端」带来不小的延迟和内存压力,严重影响用户体验,因此Dash贴心地为我们准备了「后端分页」方式。

这时首先我们得为DataTable设置参数page_action='custom',这是使用后端分页的先决条件,接下来我们需要认识一些新的参数:

page_current,int型,对应当前翻到的页码;

page_count,int型,对应显示的总页数;

我们在使用「后端分页」时,实际上就是通过用户当前翻到的页码,以及设定的page_size,来动态地在翻页后加载对应批次的数据,并控制显示的总页数,参考下面这个简单的例子:

❝

app2.py

❞
import dash
import dash_bootstrap_components as dbc
import dash_table
from dash.dependencies import Input, Output

import seaborn as sns
import pandas as pd
from tqdm import tqdm

# 压力测试
df = pd.concat([sns.load_dataset('tips') for _ in tqdm(range(1000))], ignore_index=True)
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'
                }
            )
        )
    ],
    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')]
)
def refresh_page_data(page_current, 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)

可以看到,即使我们完整的数据集被我concat到24万行,加载应用以及网页内翻页时依然轻松自如毫无压力,在实际应用中你还可以将翻页部分改成受到LIMIT与OFFSET控制的数据库查询过程,使得应用运行的更加快速高效:

图3

2.2 对单元格内容进行编辑

讲完了分页翻页,接下来我们来学习dash_table中更加强大的功能——单元格内容编辑。

一个现代化的web应用当然不能局限于仅仅查看数据这么简单,Dash同样赋予了我们双击数据表单元格进行数据编辑的能力,首先得设置参数editable=True,即开启表格编辑模式,接下来就可以对数据区域单元格进行任意的双击选中编辑。

不过Dash默认的单元格被选中的样式忒丑了(是粉色的你敢信),因此我们可以利用下面的参数设置方式来自定义美化:

style_data_conditional=[
                {
                    # 对选中状态下的单元格进行自定义样式
                    "if": {"state": "selected"},
                    "background-color": "#b3e5fc",
                    "border": "none"
                },
            ]

来看一个形象的例子,我们对「前端分页」方式渲染出的表格进行随意的修改,并在下方对利用pandas的compare比较出的数据框之间的差异结果进行打印:

❝

app3.py

❞
import dash
import dash_html_components as html
import dash_core_components as dcc
import dash_bootstrap_components as dbc
import dash_table
from dash.dependencies import Input, Output

import seaborn as sns
import pandas as pd

df = sns.load_dataset('tips')
df.insert(0, '#', df.index)

app = dash.Dash(__name__)

app.layout = dbc.Container(
    [
        dash_table.DataTable(
            id='dash-table',
            data=df.to_dict('records'),
            columns=[
                {'name': column, 'id': column}
                for column in df.columns
            ],
            fixed_rows={'headers': True},
            page_size=15,
            editable=True,
            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"
                },
            ]
        ),
        html.H4('与原表格内容比较:', style={'margin-top': '50px'}),
        dcc.Markdown(
            '无差别',
            id='markdown',
            dangerously_allow_html=True
        )
    ],
    style={
        'margin-top': '50px'
    }
)


@app.callback(
    Output('markdown', 'children'),
    Input('dash-table', 'data'),
    prevent_initial_call=True
)
def compare_difference(dash_table_data):
    print(pd.DataFrame(dash_table_data))

    return df.compare(pd.DataFrame(dash_table_data)).to_html()


if __name__ == '__main__':
    app.run_server(debug=True)

可以看到,我们成功地对指定单元格元素进行了修改。

图4

3 开发数据库内容在线更新工具

在学习完今天的内容之后,我们就可以开发一个简单的,可在线自由修改并同步变动到数据库的小工具,这里我们以MySQL数据库为例,对示例表进行修改和更新:

首先我们利用下列代码向示例数据库中新建表格tips:

from sqlalchemy import create_engine
import seaborn as sns

df = sns.load_dataset('tips')
df.insert(0, '#', df.index)

engine = create_engine('mysql+pymysql://root:mysql@localhost/DASH')

df.to_sql('tips', con=engine, if_exists='replace', index=False)
图5
  

接下来我们就以创建好的tips表为例,开发一个Dash应用,进行数据的修改和更新到数据库:

图6

效果非常的不错,你可以在我这个简单示例的基础上,拓展更多新功能,也可以采取后端分页+条件修改的方式来应对大型数据表的修改,全部代码如下:

❝

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, State

from sqlalchemy import create_engine
import pandas as pd

engine = create_engine('mysql+pymysql://root:mysql@localhost/DASH')

app = dash.Dash(__name__)

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"
                },
            ]
        ),
        dbc.Button('同步变动到数据库', id='update-tables', style={'display': 'none'}),
        html.P(id='message')
    ],
    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'),
     Output('update-tables', 'style')],
    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
        ], {'margin-top': '25px'}

    else:
        return [], [], {'display': 'none'}


@app.callback(
    [Output('message', 'children'),
     Output('message', 'style')],
    Input('update-tables', 'n_clicks'),
    [State('dash-table', 'data'),
     State('table-select', 'value')]
)
def update_to_database(n_clicks, data, value):

    if n_clicks:

        try:
            pd.DataFrame(data).to_sql(value, con=engine, if_exists='replace', index=False)

            return '更新成功!', {'color': 'green'}
        except Exception as e:
            return f'更新失败!{e}', {'color': 'red'}

    return dash.no_update

if __name__ == '__main__':
    app.run_server(debug=True)

以上就是本文的全部内容,欢迎在评论区发表你的意见与观点。

Python自学超级硬核资料


最后送大家一份Python学习大礼包,从Python基础,爬虫,数据分析Web开发等全套资料,吃透资料,你可以扔掉其他资料,这些资料都是视频,学起来非常友好 
Ps:都是视频学习资料,非常适合基础不好或者零基础的同学




推荐阅读


  1. a站、b站、c站、d站、e站、f站、g站、h站、i站、j站、k站、l站、m站、n站…z站?

  2. 悲痛!中南大学一硕士生坠楼身亡,生前聊天记录被爆出,校方回应!

  3. 阿里收182亿罚单背后...

  4. 15岁进少年班,21岁打破西方技术壁垒!这位95后正在改变世界

看完记得关注@Python绿色通道
及时收看更多好文
↓↓↓



点个在看你最好看




    


Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/113641