'Serve layout after Flask routing in Plotly Dash app

let me try to explain what is my issue in simple words and if it's really solvable or not in a different way I already did but it doesn't convince much. Because this is not a personal project, I will not share the exact code I am using but I think we will make an idea with some adjustments. I have a Plotly Dash application embedded into a Flask application with following routes:

# This includes all the login for performing authorisation flow with an identity provider

@app.before_request
def protect_views(): 
    # Protect all the views

@app.route(LOGIN_URL_PATH)
def login():
    # Login

@app.route(LOGOUT_URL_PATH)
def logout():
   # Logout

The app.py entrypoint is roughly the following:

# Startup and run the Flask server attached with some configuration

app = Dash(__name__, server=Flask(), suppress_callback_exceptions=True). # using a flask server

app.layout = serve_layout
callbacks.register_callbacks(app)

if __name__ == "__main__":
    app.run_server(debug=True, host="0.0.0.0", port=5000)

as you can see, I use two functions: serve_layout for serving the layout I prepared into into a layout.py file and register_callbacks for registering my callbacks into a callbacks.py file. Specifically, my layout.py looks like this:

def serve_layout():
    result = get_data_from_database()     # Pseudocode, getting data from a DB 
    input_data = result.to_dict("records")
    table = pac.DataTable(
        id='dash_table',
        data=input_data,
        columns=prepare_columns(input_data),
        editable=True,
        filter_action="native",
        sort_action="native",
        sort_mode="multi",
        column_selectable="multi",
        row_selectable="multi",
        page_action="native",
        page_current=0,
        page_size=10,
        style_table={'overflowY': 'scroll'},
    )

    out = html.Div(id='output', style={'display': 'none'})

    layout = html.Div(children=[
        html.H4(id='page-id', children=["Hello"], style={'text-align': 'right'}),
        html.Div(id="page-content",children=[html.Div(children=[table]), out])
        ]
    )
    return layout

So, I am able to run the server but when it comes to get_data_from_database() my code fails because I have a dependency from my authorisation flow: in other words, I have to perform my authorisation flow first, get a token that I need to provide to my function in order to establish a connection, retrieve the data and visualise them. Even if I am not providing the full code, my question is clear: is it possible to do something like this? I actually was able to solve it by authenticating first and performing my query afterwards if I included my table rendering in a callback, in order to render it after I have a Flask session available and I included all the pieces in my application context. It means, like this:

    @dashapp.callback(
        Output('page-content', 'children'),
        Input("output", "children"),
    )
    def display_table(children):
         result = get_data_from_database()     # Pseudocode, getting data from a DB 
        return pac.DataTable(
            id='dash_table',
            data=input_data,
            columns=prepare_columns(input_data),
            editable=True,
            filter_action="native",
            sort_action="native",
            sort_mode="multi",
            column_selectable="multi",
            row_selectable="multi",
            page_action="native",
            page_current=0,
            page_size=10,
            style_table={'overflowY': 'scroll'},
        )

But, this solution doesn't convince me because of two reasons:

  • I want to achieve something different. The use of serve_layout helped me a bit because it enables dynamic layout, but when I run the server and try to access my application locally, it still tries to build my layout first other than start the routing. I found a few similar examples but not exact as I need. I know and understand how Plotly Dash applications work and the overall flow probably is correct but I was wondering if there's some kind of workaround to do it or a nicer solution.
  • The proposed solutions is extremely slow

I hope that someone can give me some insights? Maybe aslo something I didn't understand properly. Thanks



Solution 1:[1]

I am in exactly the same boat here. I probably don't understand the full scope of your requirements, but as for me, my basic requirements are similar:

  • Authentication token dependency for data loading
  • protecting views

and after some investigation and also looking at the underlying dash endpoints and implementation, I don't think there's a better solution at the moment. I try to keep my serve_layout free of any data loading dependencies so it's fast on server startup, so that is the workaround here. As far as I can tell, the serve_layout function is going to run on startup.

When the user accesses the route, I see the /_dash-update-component endpoint accessed, and serve_layout isn't called again, but rather the callback, so that's where the bulk of the data loading and layout refreshing occurs.

Hope this helps.

Solution 2:[2]

use location /static/ instead of /static

server {
            listen 80;
            server_name 185.46.8.164;

        
            location = /favicon.ico { access_log off; log_not_found off; }
            location /static/ {
                root /myapps/zhanna_first_app/zhanna/static;
            }
            location /media/  {
                root /path-to-media-folder;
            }
    
        location / {
        proxy_pass http://185.46.8.164:8001;
        proxy_set_header X-Forwarded-Host $server_name;
        proxy_set_header X-Real-IP $remote_addr;
        add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
        add_header Access-Control-Allow-Origin *;
    }
    }

Make sure to run collectstatic and restat nginx

Solution 3:[3]

It turned out that the error was that nginx was giving a 502 error. I reconfigured my files and the 502 error went away. But now I have met the 500 error.

line from /var/log/nginx/error.log :

worker_connections are not enough while connecting to upstream...

my configs: my_app.conf (nginx config):

server {
    listen 82;
    server_tokens off;
    server_name 185.46.8.164;

    #root /var/www/
    location / {
        include uwsgi_params;
        uwsgi_pass unix:///run/uwsgi/app/myapp/socket;
        #
        try_files $uri $uri/ =404;
        #
        proxy_pass http://185.46.8.164:82;
        proxy_set_header X-Forwarded-Host $server_name;
        proxy_set_header X-Real-IP $remote_addr;
        add_header P3P 'CP="ALL DSP COR PSAa PSDa OUR NOR ONL UNI COM NAV"';
        add_header Access-Control-Allow-Origin *;
    }

    location /static/ {
        root /eva/static;
    }

}

myapp.ini (uwsgi.ini file):

[uwsgi]
chdir = /root/eva/lawyer
env = DJANGO_SETTINGS_MODULE= lawyer.settings.production
wsgi-file = lawyer/wsgi.py
#module = lawyer.uwsgi:application
workers = 1
max-requests = 5000
#plugins-dir=/usr/lib/uwsgi/plugins/
#plugins = python3
#virtualenv = /root/eva/venv
home = /root/eva/venv
processes = 5
threads = 2
master = true
die-on-term = true
socket = /run/sedova.sock
chmod-socket = 666
vacuum = true
uid = www-data
gui = www-data
  

I found two ways on the Internet. 1) Change my_app.conf. But the changes didn't help me. 2) In /etc/nginx/nginx.conf, change the value of worker_connections and add the worker_rlimit_nofile parameter. But too large values cause me the socket() failed... error (shown below). And too small - do not solve the problem. socket() failed (24: To many open files)...

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 K Chen
Solution 2
Solution 3