'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_layouthelped 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 |
