flask开发桌面应用程序
Full disclosure: I am not an expert in developing web applications. I don’t even consider myself a data scientist. Since I found Dash I have been on a journey to build a system that works for my use-case. This includes trying to glue together multiple loosely related groups of Dash apps developed using a windows computer but deployed to an Ubuntu VM behind a company’s firewall. Add in some authentication that users wont hate (i.e. something they already use) and I have arrived at something that I couldn’t find a clean example of so I thought I’d share my current solution and its evolution.
完全公开:我不是开发Web应用程序的专家。 我什至都不认为自己是数据科学家。 自从我找到Dash以来,我就一直在构建适用于我的用例的系统。 这包括尝试将使用Windows计算机开发但部署到公司防火墙后的Ubuntu VM的多个松散相关的Dash应用程序组粘合在一起。 添加一些用户不会讨厌的身份验证(即他们已经使用过的身份验证),而我发现了一些我找不到清晰示例的内容,因此我想我将分享我当前的解决方案及其发展。
I am a structural analyst and I use Python daily to process test data, scale or combine analysis results, run optimizations or trade studies, and/or develop methods of analysis specific to a component or mechanism. Most other engineers do not use python. So once I have developed or coded up an existing analysis method, I usually want to deploy it to others in a controlled way, with a familiar and user-friendly interface…enter Plotly’s Dash framework!
我是结构分析员,并且每天使用Python处理测试数据,扩展或合并分析结果,进行优化或进行贸易研究,和/或开发特定于组件或机制的分析方法。 其他大多数工程师都不使用python。 因此,一旦我开发或编码了一种现有的分析方法,我通常希望以一种可控的方式,通过一个熟悉且用户友好的界面将其部署到其他人……输入Plotly的Dash框架!
Plotly的Dash框架 (Plotly’s Dash Framework)
I am a huge fan of Plotly’s Dash framework. Making a Dash app is pretty simple if you are already familiar with python. Add in Dash Bootstrap Components and smooth, good-looking, and responsive web apps are just a few extra lines of code away. Going from existing Python scripts to web app is a matter of 1–2–3
我是Plotly Dash框架的忠实拥护者。 如果您已经熟悉python,则制作Dash应用非常简单。 只需添加几行代码,即可添加Dash Bootstrap组件和流畅,美观且响应Swift的Web应用程序。 从现有的Python脚本转到Web应用大约需要1–2–3
Creating the layout with components for user inputs (buttons, dropdowns, number/text input, etc.) and regions with results (plots, tables, etc.) using layout elements (div, row, column, etc.)
使用布局元素( div,行,列等)为用户输入( 按钮 , 下拉菜单 , 数字/文本输入等)和具有结果的区域( 图 , 表等)创建具有组件的布局
Linking up the user inputs to the functions that actually do the work with callbacks
将用户输入链接到实际使用回调进行工作的功能
- Deploy the app or just run it on a host machine or VM accessible to your intended users 部署应用程序或仅在目标用户可访问的主机或VM上运行应用程序
Dash apps look pretty, you can code them in Python (avoiding javascript and raw html), and can have a wide audience using the same ‘controlled’ tool (no passing around excel files thank you very much). Check out the Dash documentation if you aren’t familiar with it. There are a lot of other excellent tutorials on building Dash apps by people that are undoubtedly better than me so I’ll keep this section short. The Dash community forum and stack overflow are your friends.
Dash应用程序看起来很漂亮,您可以使用Python(避免使用javascript和原始html)进行编码,并且可以使用相同的“受控”工具来吸引更多的用户(不要绕过excel文件非常感谢)。 如果您不熟悉Dash文档 ,请查阅它。 毫无疑问,还有很多其他有关构建Dash应用程序的优秀教程,它们比我强得多,因此,我将在本节中简短介绍。 Dash社区论坛和堆栈溢出是您的朋友。
Ok so now we have a sweet way to get a pretty tool out to the team. But I have lots of tools, some related, others not. If we could only group these apps together somehow…
好的,现在我们有了一种向团队提供漂亮工具的好方法。 但是我有很多工具,有些是相关的,有些则没有。 如果我们只能将这些应用程序以某种方式组合在一起……
在Flask应用程序中嵌入多个Dash应用程序 (Embed Multiple Dash Apps in a Flask App)
This seems to be a fairly common request. Plotly offers Dash Enterprise to help deploy multiple apps to a larger audience while maintaining security. This could be an easier more scalable solution for your team especially if you have multiple developers. For those of us where Dash Enterprise is not an option, the Plotly team discusses several methods to create multi-page apps in the open-source documentation. The first is to create multi-page dash apps by sending different layouts to the view using dcc.Location & dcc.Link components interacting through a callback. This worked ok for me for a few small apps, but as the number of apps increased it seemed to get messy. I prefer to have separate apps be stand-alone objects. I usually start development by creating a stand-alone Dash app locally before integrating into the rest of the apps for deployment. After an app has been deployed I’ll often go back and update it. Once it was integrated (at least the way that I implemented it) I had to load the entire app system just to test one portion. This got old pretty quick. So I started looking for a different way.
这似乎是一个相当普遍的要求。 Plotly提供Dash Enterprise,以帮助在保持安全性的同时将多个应用程序部署到更大的受众群体。 对于您的团队来说,这可能是一个更容易扩展的解决方案,尤其是当您有多个开发人员时。 对于我们这些无法选择Dash Enterprise的人,Plotly团队在开源文档中讨论了几种创建多页应用程序的方法。 第一种是通过使用dcc.Location和dcc.Link组件通过回调进行交互,通过将不同的布局发送到视图来创建多页仪表板应用程序 。 对于一些小型应用程序,这对我来说还可以,但是随着应用程序数量的增加,它似乎变得凌乱。 我更喜欢将单独的应用程序作为独立对象。 我通常会先在本地创建一个独立的Dash应用程序来开始开发,然后再集成到其余应用程序中进行部署。 部署应用程序后,我通常会返回并对其进行更新。 一旦集成了它(至少是我实现它的方式),我就必须加载整个应用程序系统以测试一部分。 这很快就变老了。 所以我开始寻找另一种方式。
At some point in my journey, the Plotly team added a discussion on deploying Dash apps in existing web apps to the documentation. I also came across this excellent article:
在我旅途中的某个时刻,Plotly团队在文档中添加了有关在现有Web应用程序中部署Dash应用程序的讨论。 我也碰上了牛逼他的优秀文章:
At the time I had not worked with Flask at all so templates, views, blueprints, databases were all new to me. It took some time to absorb it all (at least enough to try and work with it). The Flask Mega Tutorial helped a ton. With Oleg’s Medium article and the Flask Mega Tutorial (plus a lot of google -> stackoverflow) I started to piece together a new (to me) way to package my Dash Apps by embedding them into a ‘parent’ Flask app.
当时我根本没有使用过Flask ,所以模板,视图,蓝图和数据库对我来说都是新的。 花了一些时间来吸收所有内容(至少足以尝试和使用它)。 Flask Mega Tutorial帮助了很多人。 通过Oleg的中型文章和Flask Mega教程(以及很多Google-> stackoverflow),我开始通过将它们嵌入到“父” Flask应用程序中来组合一种新的(对我而言)打包Dash应用程序的方法。
一个基本的例子 (A Basic Example)
Most of the examples I came across were basically just toys. I didn’t feel the ones I came across covered enough of a template to make it scalable. I think my biggest issue was how to integrate a unified navigation and Bootstrap formatting in the Flask part. The example here starts the skeleton that I will build on in following sections. The fully functioning project covered in this section can be found on github under the ‘basic’ branch.
我碰到的大多数例子基本上都是玩具。 我觉得我遇到的模板没有覆盖足够的模板以使其可扩展。 我认为我最大的问题是如何在Flask部分中集成统一的导航和Bootstrap格式。 此处的示例开始了我将在以下各节中构建的框架。 本节介绍的功能齐全的项目可以在github的“基本”分支下找到。
Lets start with the project directory structure:
让我们从项目目录结构开始:
dash_in_flask_msal
│ config.py
│ env_p38_dash.yml
│ secret.env
│
├───app
│ __init__.py
│
├───dashapps
│ │ dash_app_1.py
│ │ dash_app_2.py
│ │ routes.py
│ │ __init__.py
│ │
│ ├───templates
│ └───dashapps
│ dash_app.html
│ dash_app_debug.html
|
├───main
│ routes.py
│ __init__.py
│
├───static
├───templates
base.html
index.html
I am using blueprints (‘main’ & ‘dashapps’) and an application factory. The main blueprint doesn’t really do anything right now, it just serves the index. There are 2 Dash apps in the ‘dashapps’ blueprint (dash_app_1.py & dash_app_2.py). This is a fairly standard Flask app layout.
我正在使用蓝图(“ main”和“ dashapps”)和一个应用程序工厂。 主要蓝图目前并没有真正做任何事情,它只是为索引服务。 “ dashapps”蓝图中有2个Dash应用程序(dash_app_1.py和dash_app_2.py)。 这是一个相当标准的Flask应用布局。
应用工厂 (The Application Factory)
The create_app()
application factory lives in the __init__.py
file directly under the app
directory. It is fairly basic at this point. We import the Config
object from the config.py
file (which is basically empty) and attach it to the app. We initialize the bootstrap-flask extension, and with an app_context register the blueprints. That is all normal Flask. Now comes the new stuff: we import a add_dash
function from each of the Dash app files and pass the Flask app through each one.
create_app()
应用程序工厂位于app
目录下的__init__.py
文件中。 在这一点上,这是相当基本的。 我们从config.py
文件(基本上为空)导入Config
对象,并将其附加到应用程序。 我们初始化bootstrap-flask扩展名,并使用app_context注册蓝图。 那是所有正常的烧瓶。 现在出现了新的东西:我们从每个Dash应用程序文件中导入一个add_dash
函数,并将Flask应用程序传递给每个文件。
Lets take a look at one of the Dash app files. The dash_app_1.py
file looks pretty normal. The main differences are:
让我们看一下Dash应用程序文件之一。 dash_app_1.py
文件看起来很正常。 主要区别在于:
The creation of the Dash app and all callbacks are inside of a
add_dash()
function which takes the Flask app (server
) and uses it when creating the Dash app object and after creating all the callbacks returns the Flask appDash应用程序和所有回调的创建都在
add_dash()
函数内部,该函数接收Flask应用程序(server
),并在创建Dash应用程序对象时使用它,并在创建所有回调之后返回Flask应用程序We pass a custom
url_base_pathname
parameter when creating the Dash app via theURL_BASE
global variable在通过
URL_BASE
全局变量创建Dash应用时,我们传递了一个自定义url_base_pathname
参数We use a global variable
APP_ID
as a prefix to all the component ids.我们使用全局变量
APP_ID
作为所有组件ID的前缀。
Ok so now we see how the Dash apps are attached to the mother Flask object but they still look very familiar. To complete the setup we need view functions to expose the Dash app. Lets take a look at the routes.py
file in the dashapps
blueprint folder.
好的,现在我们看到Dash应用如何附加到Flask的母亲对象上,但是它们看起来仍然很熟悉。 要完成设置,我们需要查看功能以显示Dash应用程序。 让我们来看看routes.py
在文件dashapps
蓝图文件夹。
One thing to notice is that the url_base_pathname used to create the app is not the url used for the view function. The Dash app endpoint is at /dash/dash_app_1
but the view function route is /dash_app_1
. To see what is going on we will have to take a look at the template.
需要注意的一件事是,用于创建应用程序的url_base_pathname不是用于视图功能的url。 Dash应用程序端点在/dash/dash_app_1
但视图函数的路由在/dash_app_1
。 要查看发生了什么,我们将不得不看一下模板。
模板和引导程序! (Templates & Bootstrap!)
There is a base template that all other templates inherent (base.html). This is where we will setup the navigation system. In order to use Bootstrap styling in the Flask bits I found the Bootstrap-Flask library. Not to be confused with Flask-Bootstrap (which doesn’t support Bootstrap 4). I really wanted to avoid HTML so at one point I tried Flask-Nav which seemed promising, only later to find that Flask-Bootstrap was looking like it was going to stay at Bootstrap 3. So Bootstrap-Flask and a little HTML and off we go! I started with the Bootstrap-Flask Starter Template (of course!). I then went to the Bootstrap 4.0 website and grabbed an example navbar section that included drop down menus and plopped it in a jinja2 block named ‘navbar’ which was placed in a container. I added a messages section for sending messages with categories just blow the navbar. You can see the dropdown links are created by url_for(‘dashapps.dash_app_1’) where dash_app_1 is the view function for that app. There is also a {% content %}
block that all the other templates will be working in.
所有其他固有模板都有一个基本模板(base.html)。 这是我们将设置导航系统的地方。 为了在Flask位中使用Bootstrap样式,我找到了Bootstrap-Flask库。 不要与Flask-Bootstrap(不支持Bootstrap 4)混淆。 我真的很想避免HTML,所以有一次我尝试了Flask-Nav ,这似乎很有希望,直到后来才发现Flask-Bootstrap看起来像要留在Bootstrap 3中。于是Bootstrap-Flask和一些HTML都关闭了走! 我从Bootstrap-Flask入门模板开始 (当然!)。 然后,我去了Bootstrap 4.0网站,并获取了一个示例navbar部分,该部分包含下拉菜单,并将其放入放置在容器中的名为“ navbar”的jinja2块中。 我添加了一个“消息”部分,用于发送带有类别的消息,这只会使导航栏失效。 您可以看到下拉链接是由url_for('dashapps.dash_app_1')创建的,其中dash_app_1是该应用的查看功能。 还有一个{% content %}
块,所有其他模板都将在其中运行。
Now we see the difference in url_base_pathname
in the dash app and the endpoint of the view function. The method I’m using for embedding Dash apps into Flask is to run the Dash app on its own url and then inject it into the template via an iFrame element. In the view function that calls the dash_app.html
template I pass the Dash app url (dash_url
) and a min_height
argument to define the height of the iFrame (I couldn’t find a way for the iFrame to be responsive).
现在,我们在破折号应用程序和视图函数的端点中看到了url_base_pathname
的区别。 我用于将Dash应用程序嵌入Flask的方法是在其自己的url上运行Dash应用程序,然后通过iFrame元素将其注入模板。 在调用dash_app.html
模板的视图函数中,我传递了Dash应用程序的URL( dash_url
)和min_height
参数来定义iFrame的高度(我无法找到iFrame进行响应的方法)。
Lets see it work!
让我们看看它的工作!
Now for a bonus! At the end of the Dash app file is the following code. This allows the dash app to be run standalone but inside a template (dash_app_debug.html
) which is very similar to the main template.
现在获得奖金! Dash应用程序文件的末尾是以下代码。 这使dash应用程序可以独立运行,但可以在模板( dash_app_debug.html
)内运行,该模板与主模板非常相似。
现在有了持久性数据(即数据库)! (Now with Persistent Data (i.e. a Database)!)
Without persistent data, apps are pretty straightforward. Many of the apps I build are just fancy calculators or data parsers so they don’t need a database. However some do. As a structural analysist I live and breathe material properties, coupon test data, vibration test data. Sure we could copy excel or csv files to the static or asset directory and load them up form there, but that is a pretty fragile way to store data. Ideally we would have something multiple users can read and write to in a safe way…enter the database! I’m using SQLite because I don’t need much else for my use case (I’m not re-creating Instagram here) but I think it could be adapted to another database type without too much trouble. I am also using the Flask-Sqlalchemy extension. The fully functioning project covered in this section can be found on GitHub under the ‘now_with_db’ branch. This example adds a Dash app that users can upload images with some data to and another app that displays the images and data.
没有持久性数据,应用程序将非常简单。 我构建的许多应用程序只是精美的计算器或数据解析器,因此它们不需要数据库。 但是有些。 作为结构分析,我生活和呼吸材料特性,试样测试数据,振动测试数据。 当然,我们可以将excel或csv文件复制到静态或资产目录并在那里加载它们,但这是一种非常脆弱的数据存储方式。 理想情况下,我们可以让多个用户以安全的方式进行读写操作……输入数据库! 我使用SQLite是因为我的用例不需要太多(我不在这里重新创建Instagram),但是我认为它可以适应其他数据库类型而不会带来太多麻烦。 我也在使用Flask-Sqlalchemy扩展。 本节介绍的功能齐全的项目可以在GitHub的'now_with_db'分支下找到。 此示例添加了一个Dash应用程序,用户可以将带有一些数据的图像上传到该应用程序,以及另一个显示图像和数据的应用程序。
Lets review the new directory structure! I trimmed some of the contents that haven’t changed from the ‘basic’ example from the directory structure below. Now we can see a db
folder that lives next to the app folder. It has my_data.db
& users.db
files as well an img
sub directory with some *.jpg
files.
让我们回顾一下新的目录结构! 我修剪了以下目录结构中与“基本”示例相同的内容。 现在,我们可以看到db
文件夹位于app文件夹旁边。 它具有my_data.db
和users.db
文件以及带有*.jpg
文件的img
子目录。
dash_in_flask_msal
├───app
│ │ __init__.py
│ │
│ ├───dashapps
│ │ │ dash_app_1.py
│ │ │ dash_app_2.py
│ │ │ models.py
│ │ │ routes.py
│ │ │ user_image_upload.py
│ │ │ user_image_view.py
│ │ │ __init__.py
│ │ │
│ │ ├───templates
│ │
│ ├───main
│ │
│ ├───static
│ ├───templates
│
├───db
│ my_data.db
│ users.db
│
└───img
649303dc7de4402fb62acbd33a163e37.jpg
ca49fc3d1e944398b42a95b04db14366.jpg
d6f47fcc13e2408a88b512729b280b09.jpg
The config file in the basic example wasn’t much, just the ‘Brand’ for the navbar. We will now need to add some more configuration variables. We are adding the usual SQLALCHEMY_DATABASE_URI
as an SQLite database with a default users.db
filename and SQLALCHEMY_BINDS
with my_data
bind attached to a my_data.db
sqlite file. We aren’t using the users.db
database yet so its technically not needed now (we will get there!). We have a custom config parameter IMG_FILE_DIR
which defaults to the img
directory below the new db
folder.
基本示例中的配置文件并不多,只是导航栏的“品牌”。 现在,我们需要添加更多配置变量。 我们将添加普通SQLALCHEMY_DATABASE_URI
作为具有默认users.db
文件名SQLite数据库,以及SQLALCHEMY_BINDS
my_data
绑定附加到my_data.db
sqlite文件的my_data.db
。 我们尚未使用users.db
数据库,因此从技术上来说现在不需要它(我们将到达那里!)。 我们有一个自定义配置参数IMG_FILE_DIR
,默认为新db
文件夹下的img
目录。
We have also added 2 new Dash apps (user_image_view.py
& user_image_upload.py
) and a new models.py
file. Before we dive in to the new database usage lets take a look at the routes.py
file in the dashapps
blueprint directory:
我们还添加了2个新的Dash应用程序( user_image_view.py
和user_image_upload.py
)和一个新的models.py
文件。 之前,我们在潜水新数据库的使用让我们来看看routes.py
在文件dashapps
蓝图目录:
We added 2 new routes (/image_view
& /image_upload
). The idea for all of this is to try and keep the focus on the individual Dash apps and not spend too much time integrating (once the skeleton is setup). In addition to the routes.py
file additions, I also added a new dropdown group in the base.html
template for navigation. Adding additional apps is looking pretty simple so far…
我们添加了2条新路线( /image_view
和/image_upload
)。 所有这些的想法是尝试将注意力集中在各个Dash应用程序上,而不要花费太多时间进行集成(一旦设置好骨架)。 除了增加routes.py
文件之外,我还在base.html
模板中添加了一个新的下拉菜单组用于导航。 到目前为止,添加其他应用程序看起来非常简单……
Lets now check out the database model for the new Dash apps:
现在让我们检查新Dash应用程序的数据库模型:
Here we create a User_Image
model with some string data columns and a LargeBinary
type. Lets take a look at the user_image_view.py
app for how these get implemented:
在这里,我们创建一个User_Image
模型,其中包含一些字符串数据列和LargeBinary
类型。 让我们看看user_image_view.py
应用程序如何实现这些:
We import the db
from app
and the User_Image
model from app.dashapps.models.
We have the usual global variables and everything is inside an add_dash
function. Whats new is that after creating the Dash app we create the layout as a function serve_layout()
instead of a static layout variable. What this allows is that on every page load this function is called and we can read the database. Look Ma! No callbacks! Right at the top of the function we query the database for all of the User_Image
objects with:
我们从app
导入db
,从app.dashapps.models.
导入User_Image
模型app.dashapps.models.
我们有通常的全局变量,所有内容都在add_dash
函数内部。 新功能是,在创建Dash应用程序后,我们将版面创建为函数serve_layout()
而不是静态布局变量。 这允许在每次页面加载时调用此函数,并且我们可以读取数据库。 妈你看 没有回调! 在函数顶部,我们使用以下命令在数据库中查询所有User_Image
对象:
uimgs = db.session.query(User_Image).all()
uimgs = db.session.query(User_Image).all()
and when creating the layout loop through the uimgs
and populate the layout with the data in Dash Bootstrap Components Cards. Notice the thumb
property of the User_Image
object (LargeBinary
) is actually an image stored directly in the database and the data gets attached to the src
property. Storing images directly in the database is not ideal but storing large blobs can be useful.
当通过uimgs
创建布局循环时,用Dash Bootstrap组件 卡中的数据填充布局。 请注意, User_Image
对象( LargeBinary
)的thumb
属性实际上是直接存储在数据库中的图像,并且数据已附加到src
属性。 将图像直接存储在数据库中并不理想,但是存储较大的Blob可能会有用。
The user_image_upload.py
Dash app is more of a traditional Dash app with a static layout. There is a lot more going on in there so check it out on Github. The basic idea is that there are a few input fields and an upload component as well as a Card component. We are basically building the Card for the user_image_view.py
app. In addition there is a datatable for loading existing image data so they can be modified later. Here we add an image:
user_image_upload.py
Dash应用程序更像是具有静态布局的传统Dash应用程序。 还有更多正在进行中,因此请在Github上查看。 基本思想是有一些输入字段,一个上载组件以及一个Card组件。 我们基本上是为user_image_view.py
应用程序构建卡。 此外,还有一个数据表用于装载现有的图像数据,这样他们可以在以后修改。 在这里我们添加一张图片:
There are a few tricks in the file including using dash.callback_context
to determine which component triggered the callback (mainly so 1 feedback div can be updated by multiple processes). Below is a snippet in the ‘save’ callback where a new image is added to the database. If a row is selected derived_virtual_selected_rows
will be a list of length 1 and will contain the primary key database id for the object. Its retrieved and the contents are overwritten with data in the app fields.
文件中有一些技巧,包括使用dash.callback_context
确定哪个组件触发了回调(主要是因为1个div可以被多个进程更新)。 以下是“保存”回调中的一个片段,其中新图像已添加到数据库中。 如果选择了一行,则derived_virtual_selected_rows
将是长度为1的列表,并将包含对象的主键数据库ID。 在应用程序字段中检索到的内容及其内容将被数据覆盖。
Ok so we can add items to a database and view them! But we can’t let everyone add their favorite cat photos, that would just melt the server. So we need a way to limit access to the upload….
好的,我们可以将项目添加到数据库并查看它们! 但是我们不能让每个人都添加自己喜欢的猫照片,这只会使服务器融化。 因此,我们需要一种限制上传权限的方法……。
添加Microsoft身份验证库(MSAL) (Adding Microsoft Authentication Library (MSAL))
You don’t need another password to try and remember, I know I don’t. Ideally any password we add would be one the target audience is already using. I work in a Microsoft world where a lot of people spend a lot of time using Microsoft products.….enter Microsoft Authentication Library (MSAL).
您不需要其他密码即可记住,我知道不需要。 理想情况下,我们添加的任何密码都是目标受众已经在使用的密码。 我在一个Microsoft世界中工作,在那里很多人花大量时间使用Microsoft产品。…。输入Microsoft身份验证库(MSAL)。
The Microsoft identity platform is a OAuth 2.0 and OpenID Connect standard-compliant authentication service. You can follow Microsoft’s quickstart to add authentication to a python web app. At first it was confusing for me (remember I am not a web developer). This example helped a lot more than the Microsoft documentation. Another blog post by Miguel Grinberg about OAth authentication in Flask also influenced my learning. Miguel’s OAuth post may help if you would like to swap out Microsoft Authentication for Twitter or Facebook OAuth authentication.
Microsoft身份平台是OAuth 2.0和OpenID Connect标准兼容的身份验证服务。 您可以按照Microsoft的快速入门将身份验证添加到python Web应用程序。 起初,这让我感到困惑(请记住我不是Web开发人员)。 这个示例比Microsoft文档提供了更多帮助。 Miguel Grinberg撰写的另一篇有关Flask中OAth身份验证的博客文章也影响了我的学习。 如果您想将Microsoft身份验证换成Twitter或Facebook OAuth身份验证,Miguel的OAuth帖子可能会有所帮助。
The basic steps are:
基本步骤是:
Register your app and callback in Azure (follow the quickstart)
在Azure中注册您的应用并回调(按照快速入门)
- Collect your Tenant ID, Client ID, and Client Secret and get them into your Flask config 收集您的租户ID,客户ID和客户机密,并将其放入Flask配置中
- Add some functions that generate the url to get authentication and a way to catch the return 添加一些生成url以获得身份验证的功能以及捕获返回值的方法
The ‘Azure-Samples’ example didn’t store users in a database. I wanted to use Flask-login so I could simply decorate callbacks with @login_required
for portions of the site that would require credentials, so I mixed the examples and hacked away until something worked. An almost-functional example is posted on Github under the ‘msal_login’ branch. To elevate it to fully functional you need to supply the TENANT_ID
, CLIENT_ID
, CLIENT_SECRET.
“ Azure-Samples”示例未将用户存储在数据库中。 我想使用Flask-login,这样我就可以使用@login_required
需要凭据的站点部分的回调,因此我将示例混合在一起,然后黑掉了,直到可以工作为止。 一个几乎功能正常的示例发布在Github的“ msal_login”分支下。 要将其提升为完整功能,您需要提供TENANT_ID
, CLIENT_ID
, CLIENT_SECRET.
Lets start off with project directory:
让我们从项目目录开始:
dash_in_flask_msal
│ config.py
│ env_p38_dash.yml
│ secret.env
│
├───app
│ │ __init__.py
│ │
│ ├───auth
│ │ │ models.py
│ │ │ routes.py
│ │ │ __init__.py
│ │ │
│ │ └───templates
│ │ └───auth
│ │ login.html
│ │
│ ├───dashapps
│ │ │ dash_app_1.py
│ │ │ dash_app_2.py
│ │ │ models.py
│ │ │ routes.py
│ │ │ user_image_upload.py
│ │ │ user_image_view.py
│ │ │ __init__.py
│ │ │
│ │ ├───templates
│ │ └───dashapps
│ │ dash_app.html
│ │ dash_app_debug.html
│ │
│ ├───main
│ │ routes.py
│ │ __init__.py
│ │
│ ├───static
│ ├───templates
│ base.html
│ index.html
│
├───db
│ my_data.db
│ users.db
│
└───img
649303dc7de4402fb62acbd33a163e37.jpg
770a75b7e77f4914b85eb610582b3cb3.jpg
ca49fc3d1e944398b42a95b04db14366.jpg
d6f47fcc13e2408a88b512729b280b09.jpg
Most of the files are the same from the last example. however we now have a auth
blueprint complete with models, routes, and templates. There is also a secret.env
file shown but its not on github. The missing pieces to get this example to work are defined in this file. You can add them directly to your config though.
大多数文件与上一个示例相同。 但是,我们现在有了一个包含模型,路由和模板的auth
蓝图。 还有一个secret.env
文件,但不在github上。 该文件中定义了使该示例生效的缺失部分。 您可以将它们直接添加到您的配置中。
Before we get into the auth
blueprint, lets take a look at how the application factory function has changed.
在进入auth
蓝图之前,让我们看一下应用程序工厂功能是如何变化的。
We see that we are importing the LoginManager
from flask-login
and we initialize it. We also register the new auth
blueprint with a /auth
prefix. we also see a new parameter in the add_dash
functions: login_req
which takes a boolean. We will come back to that.
我们看到我们正在从flask-login
导入LoginManager
并对其进行初始化。 我们还使用/auth
前缀注册了新的auth
蓝图。 我们还会在add_dash
函数中看到一个新参数: login_req
,它需要一个布尔值。 我们将回到这一点。
Now lets take a look at the auth
blueprint. In the models.py
file you will find a very basic user model that is missing password and login info (because MSAL will take care of that). We just have name
and email
columns (other than the unique id). The interesting bits are in the blueprint __init__.py
file and in the routes.py
file.
现在,让我们看一下auth
蓝图。 在models.py
文件中,您会找到一个非常基本的用户模型,该模型缺少密码和登录信息(因为MSAL会处理该问题)。 我们只有name
和email
列(唯一ID除外)。 有趣的位在__init__.py
蓝图文件和routes.py
文件中。
The __init__.py
file has 2 functions (_build_msal_app
& _build_auth_url
) that we will import and use in the routes.py
file. These functions are basically copied from the Azure-Samples Flask example. In the routes.py file I am also sticking pretty close to the Azure-Samples example. The main difference is I am using the familiar @login_required
and after authentication is complete either add the user to the database (if its their first time logging in) or log them in via the flask-login.login_user()
function. In addition to the auth
blueprint there is also some new features in the dashapps
blueprint.
__init__.py
文件具有2个函数( _build_msal_app
和_build_auth_url
),我们将导入它们并在routes.py
文件中使用。 这些函数基本上是从Azure-Samples Flask示例复制的。 在routes.py文件中,我还非常接近Azure-Samples示例。 主要区别是我使用的是熟悉的@login_required
并且在身份验证完成后,将用户添加到数据库(如果是他们的首次登录),或者通过flask-login.login_user()
函数登录。 除了auth
蓝图, dashapps
蓝图中还具有一些新功能。
We now have this _protect_dashviews()
function that iterates through a Dash app’s server’s view function and if it starts with the url_base_pathname
we wrap it with login_required()
. This function gets imported into each Dash app *.py file and if the new login_req
argument of the add_dash()
function is True
then the Dash app gets passed into _protect_dashviews()
before the server is returned during the creation of the application. All credit goes to Oleg as this methodology is from his medium article (linked above), I just reconfigured it as an option instead of applying it to all Dash views.
现在,我们有了这个_protect_dashviews()
函数,该函数可迭代Dash应用程序的服务器的view函数,并且如果它以url_base_pathname
开头,则用login_required()
包装它。 此函数将导入到每个Dash应用程序* .py文件中,并且如果add_dash()
函数的新login_req
参数为True
则Dash应用程序将在创建应用程序之前返回服务器之前传递到_protect_dashviews()
。 所有的功劳都归功于Oleg,因为这种方法来自他的中篇文章(以上链接),我只是将其重新配置为一个选项,而不是将其应用于所有Dash视图。
In addition to protecting the dash views that get pushed to the iFrame, we also have to protect the main view function for that Dash app with the usual @login_required
decorator. In this example I am protecting one of the Dash examples and the user_image_upload
Dash app.
除了保护推送到iFrame的破折号视图之外,我们还必须使用通常的@login_required
装饰器来保护该Dash应用程序的主视图功能。 在此示例中,我保护Dash示例之一和user_image_upload
Dash应用程序。
结束…。 (The End….)
Well that was quite the journey. It is not a perfect solution and I am sure there are some holes here and there but I hope this walk through helps you on your next project. Thanks for taking the time to read this!
嗯,那真是一段旅程。 这不是一个完美的解决方案,我相信这里到处都有漏洞,但是我希望这个过程对您的下一个项目有所帮助。 感谢您抽时间阅读!
翻译自: https://towardsdatascience.com/embed-multiple-dash-apps-in-flask-with-microsoft-authenticatio-44b734f74532
flask开发桌面应用程序