Hello,
I am new to Dash and also in a beginner level to Python, so this question might be a simple Python concept question and I would be happy to get a little help here.
I try to combine the method from the following example (1) to set up a Flask application and embed a Dash app, in the form of a multipage app that follows the example of a sidebar-with-submenus (2) with the package dash-bootstrap-components.
(1)
(2)
Error description:
I think the problem is that I try to call a function in a for loop that needs the dashapp1 variable which is defined in another function and is not globally available.
It is this line: dashapp1.callback(β¦) in init.py
I get this traceback:
* #### File "/Users/irre/dev/dash_lab/dashapp.py", line *1* , in `<module>`
from app import create_app
* #### File "/Users/irre/dev/dash_lab/app/__init__.py", line *65* , in `<module>`
dashapp1.callback(
> NameError: name 'dashapp1' is not defined
I donβt know how to combine these two concepts properly and especially where to put the following functions in this factory application style. I guess they belong to the layout section, while still calling a callback with the dashapp1 (app) variable, which is not available outside of the def register_dashapps(app): function.
# this function is used to toggle the is_open property of each Collapse
def toggle_collapse(n, is_open):
...
return is_open
# this function applies the "open" class to rotate the chevron
def set_navitem_class(is_open):
...
return ""
for i in [1, 2]:
dashapp1.callback(
...
)(toggle_collapse)
dashapp1.callback(
...
)(set_navitem_class)
So, I try to post the in my opinion relevant code parts.
File structure using application factory pattern:
ββ app
β βββ init.py # app factory / see code example (1)
β βββ dashapp1
β β βββ assets # sidebar .css from the bootstrap example
β β βββ callbacks.py # / see code example (2)
β β βββ layout.py # / see code example (3)
β βββ extensions.py # essential flask extensions
β βββ forms.py # login & registration form
β βββ models.py # database model for users
β βββ templates # html templates
β β βββ β¦
β βββ webapp.py # main Blueprint and Flask index, login, register routes
βββ dashapp.py # creates the server Flask app
(1) init.py
import dash
from flask import Flask
from flask.helpers import get_root_path
from flask_login import login_required
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
from config import BaseConfig
# link fontawesome to get the chevron icons
FA = "https://use.fontawesome.com/releases/v5.8.1/css/all.css"
def create_app():
server = Flask(__name__)
server.config.from_object(BaseConfig)
register_dashapps(server)
register_extensions(server)
register_blueprints(server)
return server
def register_dashapps(app):
from app.dashapp1.layout import layout
from app.dashapp1.callbacks import register_callbacks
# Meta tags for viewport responsiveness
meta_viewport = {"name": "viewport", "content": "width=device-width, initial-scale=1, shrink-to-fit=no"}
dashapp1 = dash.Dash(__name__,
server=app,
url_base_pathname='/dashboard/',
assets_folder=get_root_path(__name__) + '/dashboard/assets/',
meta_tags=[meta_viewport],
external_stylesheets=[dbc.themes.BOOTSTRAP, FA])
with app.app_context():
dashapp1.title = 'Dashapp 1'
dashapp1.layout = layout
register_callbacks(dashapp1)
_protect_dashviews(dashapp1)
# this function is used to toggle the is_open property of each Collapse
def toggle_collapse(n, is_open):
if n:
return not is_open
return is_open
# this function applies the "open" class to rotate the chevron
def set_navitem_class(is_open):
if is_open:
return "open"
return ""
for i in [1, 2]:
dashapp1.callback(
Output(f"submenu-{i}-collapse", "is_open"),
[Input(f"submenu-{i}", "n_clicks")],
[State(f"submenu-{i}-collapse", "is_open")],
)(toggle_collapse)
dashapp1.callback(
Output(f"submenu-{i}", "className"),
[Input(f"submenu-{i}-collapse", "is_open")],
)(set_navitem_class)
def _protect_dashviews(dashapp):
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])
def register_extensions(server):
from app.extensions import db
from app.extensions import login
from app.extensions import migrate
db.init_app(server)
login.init_app(server)
login.login_view = 'main.login'
migrate.init_app(server, db)
def register_blueprints(server):
from app.webapp import server_bp
server.register_blueprint(server_bp)
(2) Callback.py
from datetime import datetime as dt
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import pandas_datareader as pdr
def register_callbacks(dashapp):
@dashapp.callback(Output("page-content", "children"), [Input("url", "pathname")])
def render_page_content(pathname):
if pathname in ["/", "/page-1/1"]:
return html.P("This is the content of page 1.1!")
elif pathname == "/page-1/2":
return html.P("This is the content of page 1.2. Yay!")
elif pathname == "/page-2/1":
return html.P("Oh cool, this is page 2.1!")
elif pathname == "/page-2/2":
return html.P("No way! This is page 2.2!")
# If the user tries to reach a different page, return a 404 message
return dbc.Jumbotron(
[
html.H1("404: Not found", className="text-danger"),
html.Hr(),
html.P(f"The pathname {pathname} was not recognised..."),
]
)
(3) layout.py
import dash
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
# link fontawesome to get the chevron icons
FA = "https://use.fontawesome.com/releases/v5.8.1/css/all.css"
# the style arguments for the sidebar. We use position:fixed and a fixed width
SIDEBAR_STYLE = {
"position": "fixed",
"top": 0,
"left": 0,
"bottom": 0,
"width": "16rem",
"padding": "2rem 1rem",
"background-color": "#f8f9fa",
}
# the styles for the main content position it to the right of the sidebar and
# add some padding.
CONTENT_STYLE = {
"margin-left": "18rem",
"margin-right": "2rem",
"padding": "2rem 1rem",
}
submenu_1 = [
html.Li(
# use Row and Col components to position the chevrons
dbc.Row(
[
dbc.Col("Menu 1"),
dbc.Col(
html.I(className="fas fa-chevron-right mr-3"), width="auto"
),
],
className="my-1",
),
id="submenu-1",
),
# we use the Collapse component to hide and reveal the navigation links
dbc.Collapse(
[
dbc.NavLink("Page 1.1", href="/page-1/1"),
dbc.NavLink("Page 1.2", href="/page-1/2"),
],
id="submenu-1-collapse",
),
]
submenu_2 = [
html.Li(
dbc.Row(
[
dbc.Col("Menu 2"),
dbc.Col(
html.I(className="fas fa-chevron-right mr-3"), width="auto"
),
],
className="my-1",
),
id="submenu-2",
),
dbc.Collapse(
[
dbc.NavLink("Page 2.1", href="/page-2/1"),
dbc.NavLink("Page 2.2", href="/page-2/2"),
],
id="submenu-2-collapse",
),
]
sidebar = html.Div(
[
html.H2("Sidebar", className="display-4"),
html.Hr(),
html.P(
"A sidebar with collapsible navigation links", className="lead"
),
dbc.Nav(submenu_1 + submenu_2, vertical=True),
],
style=SIDEBAR_STYLE,
id="sidebar",
)
content = html.Div(id="page-content", style=CONTENT_STYLE)
layout = html.Div([dcc.Location(id="url"), sidebar, content]) # here I changed app.layout to just layout
thanks for your help!