What are some reasons why callbacks fail to function after I have converted the app to a multi-page app?

Based on the most recent video, I converted my app to a multi-page app. but the callbacks no longer fire.

I’ve been working on the problem since yesterday morning. I am at a loss and hoping maybe someone else had the same problem, and was able to solve it. Because this is a Beta version, and there is not much information online, I will do my best to present this issue here, though I don’t really know where to begin.

The multi-page app layout looks fine. The dbc.Components (le.g. the dbc.Accordion format in app.py) also work. but the app no longer processes information. For example, I have a text area with a submit button in the home page (submit_recipe.py), but the submit button no longer works. It is difficult to diagnose the problem.

I realize this question is broad and requests a summary of possible reasons. I wish I could make it more specific, but I need to troubleshoot this time. I think part of the problem is that I had to convert the app.py from the standalone app into one of the “pages” in the pages folder. It is now submit_recipe.py.

I apologize for the amount of code shared, but it is the minimal, meaningful example. This is a very important question to me because the app would performs best in a multi-page app given the amount of information and its potential as a research tool. I was going to display the file structure, but it is too much to expect anyone to digest.

Thank you for your time.

Robert

1. Here is the code from submit_recipe.py that no longer functions:

It is the first Accordion item and its related callback/function.

First Accordion Item:

dash.register.page(__name__, path='/')

external_stylesheets=[dbc.themes.SUPERHERO]

app = dash.Dash(external_stylesheets=external_stylesheets)

server = app.server

app.layout = html.Div(
    [
        html.Br(),
        html.H5("Welcome to Ingredible's Nutrition Assistant"),
        html.Br(),
        dbc.Accordion(
            [
                dbc.AccordionItem( # Step 1: Paste your recipe here.
                    [
                        html.Div("Please paste your recipe here.", className="mb-4"),
                        dcc.Textarea(
                            id="recipe-textarea",
                            value="",
                            style={"width": "100%", "height": 400},
                            placeholder="Please note: All information in brackets or parantheses will be deleted.",
                            className="mb-4"
                        ),
                        dbc.Button(
                            "Submit Recipe", 
                            id="recipe-textarea-submit-button",
                            className="me-2", 
                            n_clicks=0
                        ),
                        dbc.Button(
                            "Reset", 
                            id="recipe-textarea-reset-button",
                            className="me-2", 
                            n_clicks=0
                        ),
                        html.Div(id="MSG-1"),
                    ],
                    title="Step 1",
                    item_id="acc_textarea",
                ),
**Callback and Function for First Accordion Item:**

# This callback and function accepts and processes the recipe,
# producing an editable table of amounts, units of measurement,
# ingredients and modifiers essential to a nutritional profile.
# The user can edit this information in this table to
# ensure its accuracy before the final analysis

@app.callback(
    Output('nutrients-review-datatable', 'data'),
    Input('recipe-textarea-submit-button', 'n_clicks'),
    State('recipe-textarea', 'value')
)
def update_output(n_clicks, value):
    if n_clicks > 0:
        print("Recipe submitted")
        with open("temp/recipe_contents.txt", "w", encoding='utf-8') as recipe_contents:
            recipe = value.split('\n')
            for line in recipe:
                recipe_contents.write(line)
                recipe_contents.write('\n')
        convert_unicode_fractions()
        convert_regular_fractions()
        prepare_recipe_text()
        parse_recipe_ner()
        validate_recipe_ents()
        prepare_data_for_table()

        with open("./json/recipe_ents_list.json", "r") as final_recipe_file:
            data = json.loads(final_recipe_file.read())

            return data

Hi @robertpfaff,

I believe you are mixing what has to be defined in the main app (app.py) and in the pages modules. You shouldn’t define or import app in individual pages, and app.layout in each page should instead be a variable layout or a function layout returning it. Same thing with @app.callback: it should be just @callback and callback can be imported from dash. These are the changes that you need to do in your single page application to make then one of the pages using the new plugin.

Your app.py (where app and app.layout are defined) could look just like this:

import dash
import dash_labs as dl
import dash_bootstrap_components as dbc


app = dash.Dash(
    __name__, plugins=[dl.plugins.pages], external_stylesheets=[dbc.themes. SUPERHERO]
)

app.layout = html.Div(
    [dl.plugins.page_container]
)

if __name__ == "__main__":
    app.run_server(debug=True)

Here is a nice example to start with.

2 Likes

Thank you for your answer.

Unfortunately, when I set it up as shown in the example, I received the error message below.

Line 59 is the start of a callback (’@app.callback(’) so I have to assume I have omitted some code that defines the app as a Dash app, right? Did I miss something obvious from the example? I will paste just the beginning lines of the revised pages below in case you have any insights, but I don’t want to impose.

The app looks and performs well as a standalone app on the development server. But I have exhausted myself trying to 1) restructure it as a simple, three-page app and 2) deploy it on the web - thanks mainly to Github issues, it seems.

I am at the point where I may need to pay someone to teach me how to cross this finish line, but I don’t know if that’s an appropriate question to ask on this platform , where else to look for help, or how much it would cost.

Error Message:

File “C:\Users\rober\AppData\Local\Programs\Python\Python39\lib\importlib_init_.py”, line 127, in import_module
return _bootstrap._gcd_import(name[level:], package, level)
File “”, line 1030, in _gcd_import
File “”, line 1007, in _find_and_load
File “”, line 986, in _find_and_load_unlocked
File “”, line 680, in _load_unlocked
File “”, line 850, in exec_module
File “”, line 228, in _call_with_frames_removed
File “C:\Users\rober\mp_ings\pages\bar_charts.py”, line 59, in
@app.callback(
NameError: name ‘app’ is not defined

1. Revised first lines from app.py:

app = dash.Dash(
    __name__, plugins=[dl.plugins.pages], external_stylesheets=[dbc.themes.BOOTSTRAP]
)

navbar = dbc.NavbarSimple(

2. First lines from submit_recipe.py in pages folder.

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

layout = html.Div(
    [

3. First lines from bar_charts.py in pages folder.

dash.register_page(__name__)

layout = html.Div([

As I mentioned in my previous response, the callbacks for individual pages should be imported from dash and not as an attribute of app.

In the example of bar_charts.py:

import dash

dash.register_page(__name__)

from dash import Dash, dcc, html, Input, Output, callback # HERE!
# ...
layout = html.Div(
# ....
)

# HERE! Not app.callback, just callback
@callback(Output("bar-chart", "figure"), Input("dropdown", "value"))
def update_bar_chart(day):
    mask = df["day"] == day
    fig = px.bar(df[mask], x="sex", y="total_bill", color="smoker", barmode="group")
    return fig
1 Like

I really appreciate your help. Unfortunately, removing “app” from the callbacks on the files in the pages folder produces another error message. Here is a snipped image of the entire Traceback:

I sincerely thank you for your feedback…and your patience.

Here is the entire callback up until I start the graph:

@callback(
    Output('display_barchart', 'figure'),
    Input('gender_radio', 'value'),
    Input('ages_radio', 'value'),
    Input('units_radio', 'value'))

def display_graph(selected_gender, selected_ages, selected_units):

    df = pd.read_csv('final_charts_data.csv')

    Nutrients = df.Nutrients.unique()
    Amounts = df.Amounts.unique()
    Benchmarks = df.Benchmarks.unique()
    Units = df.Units.unique()
    Gender = df.Gender.unique()
    Ages = df.Ages.unique()

    print("Selected Gender: ", selected_gender)
    print("Selected Ages:", selected_ages)
    print("Selected Units:", selected_units)     

    data = df.loc[((df['Gender'] == selected_gender) & (df['Units'] == selected_units)) & ((df['Ages'] == selected_ages))]

    print("Data: ", data)
    fig = go.Figure()

Could you share your entire “bar_charts.py” file as it is now? Do you have a from dash import callback on top of it?

1 Like

I had to leave for a yoga class, and I just now got back.

I included “from dash import callback” in all three pages, but I was still getting that error message.

Before I copied the bar_charts.py file and forced you to pour over it, I decided to exit my “Git CMD” and start over with a fresh virtual environment…just in case.

And it worked! Hallelujah, it worked. Thank you so much.

I guess the wrong code was still “cached” in the system somehow (?), because it decided to work when I essentially rebooted the environment. Can’t thank you enough, man. I was down and close to giving up.

Now all I need to figure out is how to deploy it.

Thanks again,

Robert

P.S. I am assuming you don’t want to see the code now.

:slight_smile:

This is great feedback @robertpfaff , thank you for sharing this! We should make sure that in our documentation we provide extra emphasis on:

  • layout not app.layout
  • callback not app.callback

I can see how these things are easy to miss!

2 Likes