Structure of multi-page app with SQLAlchemy

Hello everybody

I try to build a multi-page -app with the new Dash 2.5 and included SQL-Alchemy. The setup of the app is in my init.py file.

When I include a new page I did so with dash.register_page(name, path=‘/add_new_action’) in the according add_new_action.py file. But I don’t succeed to access db, session or engine (from init.py) from this file. When I normally import db etc. then I run into a circular import. In the dash-help (Multi-Page Apps and URL Support | Dash for Python Documentation | Plotly) I read “In cases where you need access to the app in one of your page layouts, you can access it with dash.get_app”. But what about db, session or engine?

How I have to correctly organize my app to work with db on all pages of my multi-page-app?

Thanks for your help

my ‘init.py’

from dash import Dash
from eva_app.navigation import layout
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from eva_app.config import DB_CONNECTION_STRING


app = Dash(__name__, use_pages=True,
           prevent_initial_callbacks=True)

app.server.config["SQLALCHEMY_DATABASE_URI"] = DB_CONNECTION_STRING
app.server.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
app.config.suppress_callback_exceptions = True

db = SQLAlchemy(app.server) 

engine = create_engine(app.server.config["SQLALCHEMY_DATABASE_URI"],
                       connect_args={},
                       echo=False)

Session = sessionmaker(bind=engine)
session = Session()

db.create_all()
app.layout = layout

my ‘add_new_action.py’

import dash
from dash import html, dcc, callback, dash_table, ALL, no_update

dash.register_page(__name__, path='/add_new_action')

layout = dbc.Container(
    [..]
)

@callback(Output("...", "..."), Input("...", "...")
def here_i_have_to_access_db(input):
   
   query = db.select([Massnahme]).where(Massnahme.name == action_name)
   ...

1 Like

I would move the database related code to a separate database.py file, e.g.

database.py

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from eva_app.config import DB_CONNECTION_STRING

# that's probably not needed
db = SQLAlchemy(app.server) 

engine = create_engine(DB_CONNECTION_STRING],
                       connect_args={},
                       echo=False)

Session = sessionmaker(bind=engine)
db.create_all()

add_new_action.py

from database import Session

session = Session()
query = ...

Let me know if that works!

1 Like

It is not resolving the problem, since you are not using flask_sqlalchemy.

The solution would be to somehow delay pages registration after Dash instance is created. I will create issue on github.

Ok, I think there is one way to use pages with flask-sqlalchemy.

You need to create new module e.g. server.py.

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

server = Flask(__name__)
# here goes other other setting of sqlalchemy
# ...
db = SQLAlchemy(server)

And then in app.py you need to:

from dash import Dash
from server import server

app = Dash(__name__, use_pages=True, server=server)

I think it should work the same as using direct app.server instance. If there are any difference let me know.

1 Like

It is me again. It is also possible to use app.init_app() method to delay pages initialization. If you want to import anything from app.py you need start your app from other module e.g. index.py.

You need to create app like: app = Dash(__name__, use_pages=True, server=False)

And then in index.py

from app import app, server

app.init_app(server)

I was also trying to use app.enable_pages method. I think it should not be public method or it should be modified, since it is not possible to use it directly and its name is misleading. Nevertheless it is possible to use it in this way:

app.use_pages = True
app.config['pages_folder'] = r'absolute\path\to\your\pages'
app.enable_pages()