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

时间:2022-11-20 15:21:03

 1 简介

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

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

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

图1

2 dash_table的基础交互能力

 

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

2.1 分页翻页

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

2.1.1 前端分页

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

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

app1.py

  1. import dash 
  2. import dash_bootstrap_components as dbc 
  3. import dash_table 
  4.  
  5. import seaborn as sns 
  6.  
  7. df = sns.load_dataset('tips'
  8. df.insert(0, '#', df.index
  9.  
  10. app = dash.Dash(__name__) 
  11.  
  12. app.layout = dbc.Container( 
  13.     [ 
  14.         dash_table.DataTable( 
  15.             id='dash-table'
  16.             data=df.to_dict('records'), 
  17.             columns=[ 
  18.                 {'name'column'id'column
  19.                 for column in df.columns 
  20.             ], 
  21.             page_size=15,  # 设置单页显示15行记录行数 
  22.             style_header={ 
  23.                 'font-family''Times New Romer'
  24.                 'font-weight''bold'
  25.                 'text-align''center' 
  26.             }, 
  27.             style_data={ 
  28.                 'font-family''Times New Romer'
  29.                 'text-align''center' 
  30.             } 
  31.         ) 
  32.     ], 
  33.     style={ 
  34.         'margin-top''50px' 
  35.     } 
  36.  
  37. if __name__ == '__main__'
  38.     app.run_server(debug=True
秀啊,用Python快速开发在线数据库更新修改工具

图2

2.1.2 后端分页

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

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

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

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

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

app2.py

  1. import dash 
  2. import dash_bootstrap_components as dbc 
  3. import dash_table 
  4. from dash.dependencies import Input, Output 
  5.  
  6. import seaborn as sns 
  7. import pandas as pd 
  8. from tqdm import tqdm 
  9.  
  10. # 压力测试 
  11. df = pd.concat([sns.load_dataset('tips'for _ in tqdm(range(1000))], ignore_index=True
  12. df.insert(0, '#', df.index
  13.  
  14. app = dash.Dash(__name__) 
  15.  
  16. app.layout = dbc.Container( 
  17.     [ 
  18.         dbc.Spinner( 
  19.             dash_table.DataTable( 
  20.                 id='dash-table'
  21.                 columns=[ 
  22.                     {'name'column'id'column
  23.                     for column in df.columns 
  24.                 ], 
  25.                 page_size=15,  # 设置单页显示15行记录行数 
  26.                 page_action='custom'
  27.                 page_current=0, 
  28.                 style_header={ 
  29.                     'font-family''Times New Romer'
  30.                     'font-weight''bold'
  31.                     'text-align''center' 
  32.                 }, 
  33.                 style_data={ 
  34.                     'font-family''Times New Romer'
  35.                     'text-align''center' 
  36.                 } 
  37.             ) 
  38.         ) 
  39.     ], 
  40.     style={ 
  41.         'margin-top''50px' 
  42.     } 
  43.  
  44.  
  45. @app.callback( 
  46.     [Output('dash-table''data'), 
  47.      Output('dash-table''page_count')], 
  48.     [Input('dash-table''page_current'), 
  49.      Input('dash-table''page_size')] 
  50. def refresh_page_data(page_current, page_size): 
  51.     return df.iloc[page_current * page_size:(page_current + 1) * page_size].to_dict('records'), 1 + df.shape[ 
  52.         0] // page_size 
  53.  
  54.  
  55. if __name__ == '__main__'
  56.     app.run_server(debug=True

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

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

图3

2.2 对单元格内容进行编辑

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

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

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

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

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

app3.py

  1. import dash 
  2. import dash_html_components as html 
  3. import dash_core_components as dcc 
  4. import dash_bootstrap_components as dbc 
  5. import dash_table 
  6. from dash.dependencies import Input, Output 
  7.  
  8. import seaborn as sns 
  9. import pandas as pd 
  10.  
  11. df = sns.load_dataset('tips'
  12. df.insert(0, '#', df.index
  13.  
  14. app = dash.Dash(__name__) 
  15.  
  16. app.layout = dbc.Container( 
  17.     [ 
  18.         dash_table.DataTable( 
  19.             id='dash-table'
  20.             data=df.to_dict('records'), 
  21.             columns=[ 
  22.                 {'name'column'id'column
  23.                 for column in df.columns 
  24.             ], 
  25.             fixed_rows={'headers'True}, 
  26.             page_size=15, 
  27.             editable=True
  28.             style_header={ 
  29.                 'font-family''Times New Romer'
  30.                 'font-weight''bold'
  31.                 'text-align''center' 
  32.             }, 
  33.             style_data={ 
  34.                 'font-family''Times New Romer'
  35.                 'text-align''center' 
  36.             }, 
  37.             style_data_conditional=[ 
  38.                 { 
  39.                     # 对选中状态下的单元格进行自定义样式 
  40.                     "if": {"state""selected"}, 
  41.                     "background-color""#b3e5fc"
  42.                     "border""none" 
  43.                 }, 
  44.             ] 
  45.         ), 
  46.         html.H4('与原表格内容比较:', style={'margin-top''50px'}), 
  47.         dcc.Markdown( 
  48.             '无差别'
  49.             id='markdown'
  50.             dangerously_allow_html=True 
  51.         ) 
  52.     ], 
  53.     style={ 
  54.         'margin-top''50px' 
  55.     } 
  56.  
  57.  
  58. @app.callback( 
  59.     Output('markdown''children'), 
  60.     Input('dash-table''data'), 
  61.     prevent_initial_call=True 
  62. def compare_difference(dash_table_data): 
  63.     print(pd.DataFrame(dash_table_data)) 
  64.  
  65.     return df.compare(pd.DataFrame(dash_table_data)).to_html() 
  66.  
  67.  
  68. if __name__ == '__main__'
  69.     app.run_server(debug=True

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

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

图4

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

 

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

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

  1. from sqlalchemy import create_engine 
  2. import seaborn as sns 
  3.  
  4. df = sns.load_dataset('tips'
  5. df.insert(0, '#', df.index
  6.  
  7. engine = create_engine('mysql+pymysql://root:mysql@localhost/DASH'
  8.  
  9. df.to_sql('tips', con=engine, if_exists='replace'index=False

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

图5

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

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

图6

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

app4.py

  1. import dash 
  2. import dash_bootstrap_components as dbc 
  3. import dash_core_components as dcc 
  4. import dash_html_components as html 
  5. import dash_table 
  6. from dash.dependencies import Input, Output, State 
  7.  
  8. from sqlalchemy import create_engine 
  9. import pandas as pd 
  10.  
  11. engine = create_engine('mysql+pymysql://root:mysql@localhost/DASH'
  12.  
  13. app = dash.Dash(__name__) 
  14.  
  15. app.layout = dbc.Container( 
  16.     [ 
  17.         dbc.Row( 
  18.             [ 
  19.                 dbc.Col(dbc.Button('更新数据表', id='refresh-tables', style={'width''100%'}), width=2), 
  20.                 dbc.Col(dcc.Dropdown(id='table-select', style={'width''100%'}), width=2) 
  21.             ] 
  22.         ), 
  23.         html.Hr(), 
  24.         dash_table.DataTable( 
  25.             id='dash-table'
  26.             editable=True
  27.             page_size=15, 
  28.             style_header={ 
  29.                 'font-family''Times New Romer'
  30.                 'font-weight''bold'
  31.                 'text-align''center' 
  32.             }, 
  33.             style_data={ 
  34.                 'font-family''Times New Romer'
  35.                 'text-align''center' 
  36.             }, 
  37.             style_data_conditional=[ 
  38.                 { 
  39.                     # 对选中状态下的单元格进行自定义样式 
  40.                     "if": {"state""selected"}, 
  41.                     "background-color""#b3e5fc"
  42.                     "border""none" 
  43.                 }, 
  44.             ] 
  45.         ), 
  46.         dbc.Button('同步变动到数据库', id='update-tables', style={'display''none'}), 
  47.         html.P(id='message'
  48.     ], 
  49.     style={ 
  50.         'margin-top''50px' 
  51.     } 
  52.  
  53.  
  54. @app.callback( 
  55.     Output('table-select''options'), 
  56.     Input('refresh-tables''n_clicks'
  57. def refresh_tables(n_clicks): 
  58.     if n_clicks: 
  59.         return [ 
  60.             { 
  61.                 'label'table
  62.                 'value'table 
  63.             } 
  64.             for table in pd.read_sql_query('SHOW TABLES', con=engine)['Tables_in_dash'
  65.         ] 
  66.  
  67.     return dash.no_update 
  68.  
  69.  
  70. @app.callback( 
  71.     [Output('dash-table''data'), 
  72.      Output('dash-table''columns'), 
  73.      Output('update-tables''style')], 
  74.     Input('table-select''value'
  75. def render_dash_table(value): 
  76.     if value: 
  77.         df = pd.read_sql_table(value, con=engine) 
  78.  
  79.         return df.to_dict('records'), [ 
  80.             {'name'column'id'column
  81.             for column in df.columns 
  82.         ], {'margin-top''25px'
  83.  
  84.     else
  85.         return [], [], {'display''none'
  86.  
  87.  
  88. @app.callback( 
  89.     [Output('message''children'), 
  90.      Output('message''style')], 
  91.     Input('update-tables''n_clicks'), 
  92.     [State('dash-table''data'), 
  93.      State('table-select''value')] 
  94. def update_to_database(n_clicks, data, value): 
  95.  
  96.     if n_clicks: 
  97.  
  98.         try: 
  99.             pd.DataFrame(data).to_sql(value, con=engine, if_exists='replace'index=False
  100.  
  101.             return '更新成功!', {'color''green'
  102.         except Exception as e: 
  103.             return f'更新失败!{e}', {'color''red'
  104.  
  105.     return dash.no_update 
  106.  
  107. if __name__ == '__main__'
  108.     app.run_server(debug=True

原文地址:https://mp.weixin.qq.com/s?__biz=MzA3ODYwNDkzOQ==&mid=2659064647&idx=1&sn=6c0d05ed44a69366ab62cf05d9909e4a&chksm=84ca84a3b3bd0db57f425afe9410736ada4bf225fd09246771ad11482f7ba1f1f1ff2411c8e1&mpshare=1&