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

1 简介

   这是我的系列教程Python+Dash快速web应用开发的第十六期,在过往所有的教程及案例中,我们所搭建的Dash应用的访问地址都是单一的,是个单页面应用,即我们所有的功能都排布在同一个url之下。

  而随着我们所编写的Dash应用功能的日趋健全和复杂,单一url的内容组织方式无法再很好的满足需求,也不利于构建逻辑清晰的web应用。

  因此我们需要在Dash应用中引入路由的相关功能,即在当前应用主域名下,根据不同的url来渲染出具有不同内容的页面,就像我们日常使用的绝大多数网站那样。

  而今天的教程,我们就将一起学习在Dash中编写多url应用并进行路由控制的常用方法。

图1

2 编写多页面Dash应用

2.1 Location()的基础使用

  要想在Dash中实现url路由功能,首先我们需要捕获到浏览器中地址栏对应的url是什么,这在Dash中可以通过在app.layout中构建一个可以持续监听当前Dash应用url信息的部件来实现。

  我们使用官方依赖库dash_core_components中的Location()部件来实现上述功能,它的核心参数或属性有hrefpathnamesearchhash,让我们通过下面的例子来直观的了解它们各自记录了地址栏url中的哪些信息:

app1.py

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output app = dash.Dash(__name__) app.layout = dbc.Container(
[
dcc.Location(id='url'),
html.Ul(id='output-url')
],
style={
'paddingTop': '100px'
}
) @app.callback(
Output('output-url', 'children'),
[Input('url', 'href'),
Input('url', 'pathname'),
Input('url', 'search'),
Input('url', 'hash')]
)
def show_location(href, pathname, search, hash):
return (
html.Li(f'当前href为:{href}'),
html.Li(f'当前pathname为:{pathname}'),
html.Li(f'当前search为:{search}'),
html.Li(f'当前hash为:{hash}'),
) if __name__ == '__main__':
app.run_server(debug=True)

图2

  因此在Dash中编写多url应用的核心策略是利用埋点Location()捕获到地址栏对应信息的变化,并以这些信息作为回调函数的输入,来输出相应的页面内容变化,让我们从下面这个简单的例子中get上述这一套流程的运作方式:

app2.py

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output app = dash.Dash(__name__) app.layout = dbc.Container(
[
dcc.Location(id='url', refresh=False),
dbc.Row(
[
dbc.Col(
[
html.A('页面A', href='/pageA'),
html.Br(),
html.A('页面B', href='/pageB'),
html.Br(),
html.A('页面C', href='/pageC'),
],
width=2,
style={
'backgroundColor': '#eeeeee'
}
),
dbc.Col(
html.H3(id='render-page-content'),
width=10
)
]
)
],
style={
'paddingTop': '20px',
'height': '100vh',
'weight': '100vw'
}
) @app.callback(
Output('render-page-content', 'children'),
Input('url', 'pathname')
)
def render_page_content(pathname):
if pathname == '/':
return '欢迎来到首页' elif pathname == '/pageA':
return '欢迎来到页面A' elif pathname == '/pageB':
return '欢迎来到页面B' elif pathname == '/pageC':
return '欢迎来到页面C' else:
return '当前页面不存在!' if __name__ == '__main__':
app.run_server(debug=True)

图3

2.2 利用Location()实现页面重定向

  在上一小节我们对dcc.Location()的基础用法进行了介绍,而它的功能可不止监听url变化这么简单,我们还可以利用它在Dash中实现重定向,使用方式简单一句话描述就是将Location()作为对应回调的输出(记住一定要定义id属性),这样地址栏url会在回调完成后对应跳转。

  让我们通过下面这个简单的例子来get这个技巧:

app3.py

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output app = dash.Dash(__name__) app.layout = dbc.Container(
[
html.Div(id='redirect-url-container'), dbc.Button('跳转到页面A', id='jump-to-pageA', style={'marginRight': '10px'}), dbc.Button('跳转到页面B', id='jump-to-pageB'),
],
style={
'paddingTop': '100px'
}
) @app.callback(
Output('redirect-url-container', 'children'),
[Input('jump-to-pageA', 'n_clicks'),
Input('jump-to-pageB', 'n_clicks')],
)
def jump_to_target(a_n_clicks, b_n_clicks):
ctx = dash.callback_context if ctx.triggered[0]['prop_id'] == 'jump-to-pageA.n_clicks':
return dcc.Location(id='redirect-url', href='/pageA') elif ctx.triggered[0]['prop_id'] == 'jump-to-pageB.n_clicks':
return dcc.Location(id='redirect-url', href='/pageB') return dash.no_update if __name__ == '__main__':
app.run_server(debug=True)

图4

2.3 用Link()实现“无缝”页面切换

  你应该注意到了,在Dash中利用Location()和普通的A()部件实现跳转时,页面在跳转后会整体刷新,这会一定程度上破坏整个web应用的整体体验。

  而dash_core_components中的Link()部件则是很好的替代,它的基础属性与A()无异,但额外的refresh参数默认为False,会在点击后进行Dash应用内跳转时无缝切换,页面不会整体刷新:

app4.py

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output app = dash.Dash(__name__) app.layout = dbc.Container(
[
dcc.Location(id='url'), dcc.Link('页面A', href='/pageA', refresh=True),
html.Br(),
dcc.Link('页面B', href='/pageB'), html.Hr(), html.H1(id='render-page-content')
],
style={
'paddingTop': '100px'
}
) @app.callback(
Output('render-page-content', 'children'),
Input('url', 'pathname')
)
def render_page_content(pathname):
if pathname == '/':
return '欢迎来到首页' elif pathname == '/pageA':
return '欢迎来到页面A' elif pathname == '/pageB':
return '欢迎来到页面B' elif pathname == '/pageC':
return '欢迎来到页面C' else:
return '当前页面不存在!' if __name__ == '__main__':
app.run_server(debug=True)

图5

  类似的功能还有dash_bootstrap_components中的NavLink(),用法与Link()相似,这里就不再赘述。

3 动手开发个人博客网站

  掌握了今天的知识之后,我们来用Dash开发一个简单的个人博客网站,思路是在Location()监听url变化的前提下,后台利用网络爬虫从我的博客园Dash主题下爬取相应的网页内容,并根据用户访问来渲染出对应的文章:

app5.py

import dash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
import dash_dangerously_set_inner_html # 用于直接渲染html源码字符串
from dash.dependencies import Input, Output import re
from html import unescape
import requests
from lxml import etree app = dash.Dash(__name__, suppress_callback_exceptions=True) app.layout = html.Div(
dbc.Spinner(
dbc.Container(
[
dcc.Location(id='url'), html.Div(
id='page-content'
)
],
style={
'paddingTop': '30px',
'paddingBottom': '50px',
'borderRadius': '10px',
'boxShadow': 'rgb(0 0 0 / 20%) 0px 13px 30px, rgb(255 255 255 / 80%) 0px -13px 30px'
}
),
fullscreen=True
)
) @app.callback(
Output('article-links', 'children'),
Input('url', 'pathname')
)
def render_article_links(pathname):
response = requests.get('https://www.cnblogs.com/feffery/tag/Dash/',
headers={
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/89.0.4389.114 Safari/537.36'
}) tree = etree.HTML(response.text) posts = [
(href, title.strip())
for href, title in zip(
tree.xpath("//div[@class='postTitl2']/a/@href"),
tree.xpath("//div[@class='postTitl2']/a/span/text()")
)
] return [
html.Li(
dcc.Link(title, href=f'/article-{href.split("/")[-1]}', target='_blank')
)
for href, title in posts
] @app.callback(
Output('page-content', 'children'),
Input('url', 'pathname')
)
def render_article_content(pathname):
if pathname == '/': return [
html.H2('博客列表:'), html.Div(
id='article-links',
style={
'width': '100%'
}
)
] elif pathname.startswith('/article-'): response = requests.get('https://www.cnblogs.com/feffery/p/%s.html' % re.findall('\d+', pathname)[0]) tree = etree.HTML(response.text) return (
html.H3(tree.xpath("//title/text()")[0].split(' - ')[0]),
html.Em('作者:费弗里'),
dash_dangerously_set_inner_html.DangerouslySetInnerHTML(
unescape(etree.tostring(tree.xpath('//div[@id="cnblogs_post_body"]')[0]).decode())
)
) return dash.no_update if __name__ == '__main__':
app.run_server(debug=True)

图6

  按照类似的思路,你可以随心所欲地开发自己的多页面应用,进一步丰富完善你的Dash应用功能。


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

(数据科学学习手札119)Python+Dash快速web应用开发——多页面应用的更多相关文章

  1. (数据科学学习手札102)Python+Dash快速web应用开发——基础概念篇

    本文示例代码与数据已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的新系列教程Python+Dash快 ...

  2. (数据科学学习手札108)Python+Dash快速web应用开发——静态部件篇(上)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  3. (数据科学学习手札109)Python+Dash快速web应用开发——静态部件篇(中)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  4. (数据科学学习手札112)Python+Dash快速web应用开发——表单控件篇(上)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  5. (数据科学学习手札115)Python+Dash快速web应用开发——交互表格篇(上)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  6. (数据科学学习手札116)Python+Dash快速web应用开发——交互表格篇(中)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  7. (数据科学学习手札117)Python+Dash快速web应用开发——交互表格篇(下)

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  8. (数据科学学习手札118)Python+Dash快速web应用开发——特殊部件篇

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

  9. (数据科学学习手札120)Python+Dash快速web应用开发——整合数据库

    本文示例代码已上传至我的Github仓库https://github.com/CNFeffery/DataScienceStudyNotes 1 简介 这是我的系列教程Python+Dash快速web ...

随机推荐

  1. 为什么要用Spring Boot?

    什么是Spring Boot?   Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程,该框架使用了特定的方式来进行配置,从而 ...

  2. 解决VM 与 Device/Credential Guard 不兼容(全网有效解决思路)

    为什么要写这篇文章先说背景:前段时间因为学习Linux系统需要,自己本机用的是Windows系统,那这里就需要用到虚拟机来创建虚拟环境用来支持Linux系统 1: 于是乎,自己很激动的下载了vm虚拟机 ...

  3. pip安装更新模块,以及执行更新所有模块

    moudle_name:是对应的模块名:请自行更换为自己需要更新的模块名 查看所有可更新的模块: pip list --outdated 更新某一个模块: pip install --upgrade ...

  4. PTA 中序输出度为1的结点

    6-9 中序输出度为1的结点 (10 分)   本题要求实现一个函数,按照中序遍历的顺序输出给定二叉树中度为1的结点. 函数接口定义: void InorderPrintNodes( BiTree T ...

  5. 用jar命令打包war远程部署

    最近在看jboss的相关漏洞,用jmx-console进行war远程部署的时候碰到一个jsp转war的问题,研究了半天,记录一下免得搞忘了. 一开始网上是说的直接把jsp文件压缩成zip,再把后缀名改 ...

  6. 【Azure 云服务】在Cloud Service的代码中如何修改IIS Application Pool的配置呢? 比如IdleTimeout, startMode, Recycling.PeriodicRestart.Time等

    什么是 PaaS?Platform as a Service 平台即服务 (PaaS) 是云中的完整开发和部署环境,你可以使用其中资源交付内容,从基于云的简单应用到启用云的复杂企业应用程序皆可.你以即 ...

  7. Nacos 2.0 正式发布,性能提升 10 倍!!

    3月20号,Nacos 2.0.0 正式发布了! Nacos 简介: 一个更易于构建云原生应用的动态服务发现.配置管理和服务管理平台. 通俗点讲,Nacos 就是一把微服务双刃剑:注册中心 + 配置中 ...

  8. 利用Navicat premium实现将数据从Oracle导入到MySQL

    背景:我们给用户提供了新的直播系统,但客户之前的老系统用的数据库是Oracle,我们提供的新系统用的是MySQL 客户诉求:将老系统中的所有直播数据导入到MySQL中: 思路:我知道Navicat有数 ...

  9. vue 快速入门 系列 —— 侦测数据的变化 - [vue 源码分析]

    其他章节请看: vue 快速入门 系列 侦测数据的变化 - [vue 源码分析] 本文将 vue 中与数据侦测相关的源码摘了出来,配合上文(侦测数据的变化 - [基本实现]) 一起来分析一下 vue ...

  10. Java基础回顾_第一部分

    Java基础回顾 基本数据类型 数值类型 什么是字节? 位(bit):是计算机中数据的最小单位 字节(byte):是计算机中数据处理的基本单位,习惯上用大写字母B来表示 1 B = 8 bit 字符: ...