Black Lives Matter. Please consider donating to Black Girls Code today.

App.layout problem with working outside Request Context of Flask

I’ve got a problem running the dash app in flask.
It always throws the error “working outside of request context” because I’m using variables for generating the dropdown menus of the dashboard. Does anyone know how I can pass the variable to my app layout outside the request context?
The login is working fine and the sql connection as well. Just passing informations causes problems.

from flask import Flask, session, render_template, request,Response, redirect, url_for, escape
from sqlalchemy import Table, MetaData, create_engine


def getData(vertreternummer, engine):  
sqlBefehl="""SELECT customer_id, Firma, Vertreternummer FROM 
    Belegdaten  WHERE Vertreternummer='{0}';""".format(vertreternummer)
    table_umsatz= pd.read_sql_query(sqlBefehl, con = engine, index_col=None)   
    table_umsatz['Preis']=table_umsatz['Preis'].round(2)
    return table_umsatz

server = Flask(__name__)
engine2 = create_engine("postgres database")
conn2 = engine2.connect()

@server.route('/')
def index():
    if 'username' in session:
         username_session = escape(session['username']).capitalize()
         return redirect(url_for('app'))
   return redirect(url_for('login'))

@server.route('/login', methods=['GET', 'POST'])
def login():
      error = None
      if 'username' in session:
            return redirect(url_for('index'))
            if request.method == 'POST':
                username_form  = request.form['username']
                password_form  = request.form['password']
                result=conn2.execute("SELECT COUNT(1) FROM benutzer WHERE login =                                                           '{0}';".format(username_form)) # CHECKS IF USERNAME EXSIST
                if result.fetchone():
                    pw_result=conn2.execute("SELECT passwort FROM benutzer WHERE login = '{0}';".format(username_form)) # FETCH THE HASHED PASSWORD
                    for row in pw_result:
                        if password_form == row[0]:
                            session['username'] = request.form['username']
                            return redirect(url_for('index'))
                        else:
                            error = "invalid credentials"
                else:
                    error = "invalid credentials"
            return render_template('login.html', error=error)

        @server.route('/app')
        def do_something():
            if 'username' in session:
                username_session = escape(session['username']).capitalize()
                engine = create_engine('NEW Database connection')
                if (username_session=='user1'):  
                    return getData(9, engine)
                if (username_session=='user4'):
                    return getData(8, engine)
                else:
                    return getData(99, engine)
                **firma=pd.Series(table_umsatz["Firma"].unique()).sort_values(ascending=True)**
**                firma=list(firma)**
            
            return app.index()


    @server.route('/logout')
    def logout():
        session.pop('username', None)
        return redirect(url_for('/index'))

    server.secret_key = 'some_secret_key'

The Dash App:

app = dash.Dash(name='app', url_base_pathname='/app', server=server)

app.scripts.config.serve_locally = True


app.layout = html.Div([
            html.Div([
            html.H3("Dash app")
            ], className='Title'),  
                    html.Div([
                        html.Label('Filter nach Firma:', style={'font-weight': 'bold'}),
                        dcc.Dropdown(
                            id='firma-filter',
                            options=[{'label': i, 'value': i} for i in **fima**],
                            value=[1,2],
                            clearable=False,
                            multi=True
                        )],
                        style={'width': '15%', 'margin': '3px','display': 'inline-block'}),       
              ])
            
external_css = ["https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css",
                        "https://fonts.googleapis.com/css?family=Raleway:400,400i,700,700i",
                "https://fonts.googleapis.com/css?family=Product+Sans:400,400i,700,700i"]

Any help is really appreciated! Thank you!

I’m not entirely sure what’s going on here, but I’m wondering if this is not a Dash-specific issue. Is there a line number associated with the Exception?

Is there a reason why the @server.route('/logout') is nested within another route function? That looks a bit odd, so wondering if that could be an issue.

Also, I think your approach to applying login-protection to the Dash index route might not work for pages of multipage apps. As there is a separate Flask route for these pages than from the index:

Thank you @nedned for replying so fast!

I haven’t tested the @server.route(’/logout’) just the login stuff but everything worked so far as long as I am not adjusting the SQL statement to the ‘vertreternummer’. If I’m changing the SQL statements and the variables inside the @server.route(’/app’) I can’t use these variables within my app.layout. So my question is how can I get these variables within my app.layout without errors.

Thank for your help in advance!

Ah right, then you want to assign your layout to a function that uses the variables, but don’t evaluate the function. Dash will then dynamically evaluate the function at page load, loading those variables as needed). See the “Updates on page load” section of the Dash Docs.

Traceback (most recent call last):
File “test_token.py”, line 290, in
app.layout = serve_layout
File “/usr/local/lib/python3.5/dist-packages/dash/dash.py”, line 148, in layout
layout_value = self._layout_value()
File “/usr/local/lib/python3.5/dist-packages/dash/dash.py”, line 131, in _layout_value
self._cached_layout = self._layout()
File “test_token.py”, line 189, in serve_layout
options=[{‘label’: i, ‘value’: i} for i in firma],
NameError: name ‘firma’ is not defined

I tried it with serve_layout but I’m getting the error “firma is not defined” again

You’re still going to have to get the value from your DB and assign to the firma variable within the layout function before it’s used. Python is just complaining that you have yet to define the variable.

Also now I see a bit more clearly what your issue is, if you don’t expect your dropdown values from the DB to change during the life of the app, then you probably don’t actually need a function layout, the variable with the dropdown could be calculated just once on app-load.

(my guess is that the original working outside a request context issue was due to the incorrectly indented @server.route('/app'))