I am trying to create a test for my callbacks. However I’m running into a page registry error.
raise PageError("`dash.register_page()` must be called after app instantiation")
I’ve seen other people have had similar issues but I can’t seem to find a resolution without changing to the old page layout? Can anyone confirm?
TIA!
Hello @bpolasek,
This typically happens when you are trying to use a page inside the app initialization. Doing this will cause a circle, because as the app initializes, it goes to the page and registers each page.
But when you import it, it imports at the top and there is not app initialized yet.
If you have a function that you need in the app and also pages, create a function inside of a utils folder and import it in each place you need.
Okay so let me make sure I understand.
I currently have a callback function called engine_select that is currently in my pages/home.py where home.py is registered.
Instead, I create a utils folder outside of the pages folder and put my engine select in there and then import it into home.py?
That sounds right.
It sounds like you are trying to reference the app somehow and it’s not up and running yet.
Are you also trying to reference this in your overall app?
I don’t think so but I’m not 100% what you mean. I’m currently just trying to make sure my callback function that is an SQL query is giving me back exactly what I want so I was just going to create a test for that.
Can I see the function and app.py?
Here is app.py
import dash
import dash_bootstrap_components as dbc
from dash import html, dcc
from components import navbar
nav = navbar.Navbar()
app = dash.Dash(__name__, use_pages=True,
external_stylesheets=[dbc.themes.BOOTSTRAP],
meta_tags=[{"name": "viewport", "content": "width=device-width"}],
suppress_callback_exceptions=True,
)
app.layout = html.Div([
dcc.Store(id="store", data={}, storage_type='session'),
nav,
dash.page_container,
]
)
if __name__ == "__main__":
app.run_server(debug=True, dev_tools_props_check=False)
and then the callback
@callback(
Output('store', 'data'),
Input('engine', 'value'),
Input('date', 'value')
)
def engine_selected(engine, date):
end_day = date[:10]
end_day = pd.to_datetime(end_day, infer_datetime_format=True).date()
start_day = end_day - timedelta(days=365)
time = date[-5:]
query = f"""
SELECT D.GADSAbbr, F.MeasureDate, F.DeviationValue
FROM dimPlantAssetStandardAttribute D
JOIN factPointFiveMinutePeriod F ON F.HistorianPoint_id = D.HistorianPoint_id
WHERE D.GADSAbbr = '{engine}'
AND D.StandardAttributeName IN ('Gross Power Output', 'Gross Power Output - KPI')
AND F.MeasureDate BETWEEN '{start_day}' AND '{end_day} {time}:00'
AND F.DeviationValue IS NOT NULL
"""
try:
sql_engine = sa.create_engine(__connection_string)
df = pd.read_sql_query(sql=query, con=sql_engine)
store ={"df": df.to_dict('records'), "date": end_day, "engine": engine}
return store
except Exception as _:
raise (traceback.format_exc())
What happens if you comment out the navbar?
Also,
query = f"""
SELECT D.GADSAbbr, F.MeasureDate, F.DeviationValue
FROM dimPlantAssetStandardAttribute D
JOIN factPointFiveMinutePeriod F ON F.HistorianPoint_id = D.HistorianPoint_id
WHERE D.GADSAbbr = '{engine}'
AND D.StandardAttributeName IN ('Gross Power Output', 'Gross Power Output - KPI')
AND F.MeasureDate BETWEEN '{start_day}' AND '{end_day} {time}:00'
AND F.DeviationValue IS NOT NULL
"""
^ this is at risk for SQL injection, you should instead do something like this:
query = f"""
SELECT D.GADSAbbr, F.MeasureDate, F.DeviationValue
FROM dimPlantAssetStandardAttribute D
JOIN factPointFiveMinutePeriod F ON F.HistorianPoint_id = D.HistorianPoint_id
WHERE D.GADSAbbr = ?
AND D.StandardAttributeName IN ('Gross Power Output', 'Gross Power Output - KPI')
AND F.MeasureDate BETWEEN ? AND ?
AND F.DeviationValue IS NOT NULL
"""
try:
sql_engine = sa.create_engine(__connection_string)
df = pd.read_sql_query(sql=query, con=sql_engine, params=[engine, start_day, f'{end_day} {time}:00'])
store ={"df": df.to_dict('records'), "date": end_day, "engine": engine}
return store
SQL injection is nasty, they can query, drop tables, etc all outside of what you want to do.
Comment out the navbar for the testing? I tried and still the same result. I’ll try moving the callback to a utils.py and see how that works.
And thank you for the note the SQL!
1 Like
The callback I dont think should matter.
What do your page files look like?
/components
/datasets
/pages
-
init.py
- graph.py
- home.py (where the callback is)
app.py
index.py
test_app_callbacks.py
utils.py
Why is _init_
there?
You only started to encounter this issue when adding this callback?
Tbh I have no idea. It’s empty. This is only my second Dash app (the first one is still a work in progress as well), and I was following some format I found.
And I only encountered this issue when testing. My app runs fine, I just wanted to test it.
When I moved the function into utils.py, the testing worked.
Haha, that’s alright.
Glad moving it made it work. If you need some ideas of how to do stuff in a multi-page app, check out here: