I have been attempting to place a Dash App inside Flask. While I have achieved this using the recommended methods in the docs, I am really only integrating Dash into a Flask app to utilize Okta Cloud Security. I have basically followed this link to a T to see if this is even possible.
The only issue I am having is that Dash seems to be a master controller. If “url_base_pathname” or “routes_pathname_prefix” is used, it ignored any flask.route rules for that specific path. Vise versa, if neither of these are used the server defaults to just the Dash APP. Given that the template for okta integrating into flask requires a standard flask.route routing, is it possible to call the Dash App only within the flask.route function?
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import dash_table
from flask import Flask, render_template, g, redirect, url_for
from flask_oidc import OpenIDConnect
from okta import UsersClient
app = Flask(__name__)
app.config["OIDC_CLIENT_SECRETS"] = "client_secrets.json"
app.config["OIDC_COOKIE_SECURE"] = False
app.config["OIDC_CALLBACK_ROUTE"] = "/oidc/callback"
app.config["OIDC_SCOPES"] = ["openid", "email", "profile"]
app.config["SECRET_KEY"] = "{{ LONG_RANDOM_STRING }}"
app.config["OIDC_ID_TOKEN_COOKIE_NAME"] = "oidc_token"
oidc = OpenIDConnect(app)
okta_client = UsersClient("{{ OKTA_ORG_URL }}", "{{ OKTA_AUTH_TOKEN }}")
dash_app = dash.Dash(
__name__,
server=app,
url_base_pathname='/dummypath/'
)
@app.before_request
def before_request():
if oidc.user_loggedin:
g.user = okta_client.get_user(oidc.user_getfield("sub"))
else:
g.user = None
@app.route("/")
def index():
return render_template("index.html")
@app.route("/dashboard")
@oidc.require_login
def dashboard():
#this is where there needs to be a callback which pushes the app through to the /dashboard extension
return render_template("dashboard.html", dash_url = dash_app.index)
@app.route("/login")
@oidc.require_login
def login():
return redirect(url_for(".dashboard"))
@app.route("/logout")
def logout():
oidc.logout()
return redirect(url_for(".index"))
dash_app.layout = ...
...
if __name__ == '__main__':
app.run(debug=True)
You may get something useful from this. With dash 1.X, dash can work like any standard flask extension. This is especially helpful with the factory pattern with blueprints.
Thanks for the reply. I have been playing with this a bit the last day and I am not seeing it work at all. Not sure what I am missing.
I have added all of the Dash code into a function:
def dash_page():
# Create a Dash app
dash_app = Dash(__name__,
server=False, # Don't give dash a server just yet.
url_base_pathname='/dashapp/')
....
return dash_app
Not Found
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
Having tried a few other setups and variations, I am not finding a method to perform that late pass of the server to the function (like adding a param to dash_page method, ect.)
Hey @WolVes, is there a reason for adding the flask server to the dash app inside the dashboard route? That is not how flask works. You’ll need to initialize the flask app and attach it to the dash app before the routes will work.
Your dashboard function is returning an initialized dash object. That cannot render the dashboard.
from dash import Dash
from flask_login import login_required
def protect_dashviews(dashapp):
"""If you want your Dash app to require a login,
call this function with the Dash app you want to protect"""
for view_func in dashapp.server.view_functions:
if view_func.startswith(dashapp.config.url_base_pathname):
dashapp.server.view_functions[view_func] = login_required(
dashapp.server.view_functions[view_func]
)
dashapp = Dash()
protect_dashviews(dashapp)