I am building a web app using Flask and Dash. The main Flask app handles user login and authentication, and protects the routes of the Dash app. The Dash app is served from within the Flask app.
The main Flask app was built using the Flask Mega Tutorial example (https://github.com/miguelgrinberg/microblog), with the app factory pattern. I stripped out everything except the login/authentication stuff and the app works fine at that stage.
I then added a simple Dash app within the Flask app following the example here: https://github.com/okomarov/dash_on_flask. The Dash app can be access at /dashboard, and the dashboard is properly protected against unauthorised access and redirects back to the main Flask login page as expected.
My challenge is now to access the Flask app's context and other things like the database session from within the Dash app. This is so the Dash app can access and display information from the main database.
I am currently stuck trying to follow examples that I have found online, and how to adapt them to the particular pattern of my app.
I have tried to follow examples linked here: https://github.com/plotly/dash/issues/214#issuecomment-391223557.
However, these examples seem to all be based on a flat app structure where the Flask and Dash apps are created in the same file.
All of my code is located here: https://github.com/danielcopelin/dacy-budget
At the moment, my main Flask app looks like:
dacybudget.py
from app import create_app, db
from app.models import User
app = create_app()
@app.shell_context_processor
def make_shell_context():
return {'db': db, 'User': User}
create_app() looks like:
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
register_dashapps(app)
db.init_app(app)
migrate.init_app(app, db)
login.init_app(app)
mail.init_app(app)
bootstrap.init_app(app)
... and register_dashapps() looks like:
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",
}
external_stylesheets = ["https://codepen.io/chriddyp/pen/bWLwgP.css"]
dashapp1 = dash.Dash(
__name__,
server=app,
url_base_pathname="/dashboard/",
assets_folder=get_root_path(__name__) + "/dashboard/assets/",
meta_tags=[meta_viewport],
external_stylesheets=external_stylesheets,
)
dashapp1.title = "Dashapp 1"
dashapp1.layout = layout
dashapp1.url_base_pathname = "/dashboard/" # I dont know why I had to do this
register_callbacks(dashapp1)
_protect_dashviews(dashapp1)
From within app.dashapp1.layout and app.dashapp1.callbacks I want to be able to access the main Flask app's session and database, etc.
If I try various imports from within app.dashapp1.layout, for example "from .. import db" and then try to do something with that db object, I get errors like:
RuntimeError: No application found. Either work inside a view function or push an application context. See http://flask-sqlalchemy.pocoo.org/contexts/.
Could anyone offer any suggestions on how to navigate this issue?
So the question is can we easily do this with our Plotly plus Flask model? And, of course, the answer is 'Yes'. Anything you can do with Dash you can also do with its underlying technologies: Python, Flask, HTML and Javascript.
Flask automatically pushes an application context when handling a request. View functions, error handlers, and other functions that run during a request will have access to current_app . Flask will also automatically push an app context when running CLI commands registered with Flask. cli using @app.
A Dash app is fundamentally a Flask app that incorporates Plotly. Writing an actual Flask app that uses Ploty is not difficult. Writing a Flask app gives you more control over what you write and is more flexible.
I managed to solve my issue. I was struggling to understand where in the Dash side of the app it was possible to access the main Flask app's context, so that I could provide Dash with access to the database. It turns out that this can be done within the register_callbacks()
function and the actual callback functions defined within there like so:
def register_callbacks(app):
from app import db
from app.models import Transaction
...
@app.callback(
Output("main", "children"),
[Input("transaction_table", "data_previous")],
[
State("transaction_table", "data"),
State("transaction_table", "page_current"),
],
)
def update_database_and_generate_table(old_table_data, table_data, page_current):
with app.server.app_context():
if old_table_data is not None:
update_changed_data(old_table_data, table_data)
transactions = db.session.query(Transaction)
df = pd.read_sql(transactions.statement, transactions.session.bind)
For this to work, another change has to be made to the code of my original question. In the create_app()
function, you need to register the Dash app after the database has been initialised:
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
db.init_app(app)
migrate.init_app(app, db)
login.init_app(app)
mail.init_app(app)
bootstrap.init_app(app)
app = dashapp.add_dash(app)
...
The full working example is located here: https://github.com/danielcopelin/dacy-budget
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With