I am making a web app using python and dash. Everything seems to go well except when I add the last callback function add_new_parcel_submit to update my database. When I add this, the login page is not showing and the navbar is not responding to change links.
Here is my index.py:
app.layout = html.Div(
[
# Location Variable -- contains details about the url
dcc.Location(id='url', refresh=True),
# LOGIN DATA
# 1) logout indicator, storage_type='session' means that data will be retained
# until browser/tab is closed (vs clearing data upon refresh)
dcc.Store(id='sessionlogout', data=True, storage_type='session'),
# 2) current_user_id -- stores user_id
dcc.Store(id='currentuserid', data=-1, storage_type='session'),
# 3) currentrole -- stores the role
# we will not use them but if you have roles, you can use it
dcc.Store(id='currentrole', data=-1, storage_type='session'),
# Adding the navbar
html.Div(
cm.navbar,
id='navbar_div'
),
# Page Content -- Div that contains page layout
html.Div(id='page-content', style=CONTENT_STYLE),
]
)
@app.callback(
[
Output('page-content', 'children'),
Output('sessionlogout', 'data'),
Output('navbar_div', 'className'),
],
[
# If the path (i.e. part after the website name;
# in url = youtube.com/watch, path = '/watch') changes,
# the callback is triggered
Input('url', 'pathname')
],
[
State('sessionlogout', 'data'),
State('currentuserid', 'data'),
]
)
def displaypage (pathname,
sessionlogout, userid):
ctx = dash.callback_context
if ctx.triggered:
eventid = ctx.triggered[0]['prop_id'].split('.')[0]
else:
raise PreventUpdate
if eventid == 'url':
if userid < 0: # if logged out
if pathname == '/':
returnlayout = login.layout
elif pathname == '/signup':
returnlayout = signup.layout
else:
returnlayout = '404: request not found'
else:
if pathname == '/logout':
returnlayout = login.layout
sessionlogout = True
elif pathname == '/' or pathname == '/home':
# From the imported module 'home', we get the layout variable
returnlayout = home.layout
elif pathname == '/parcel_management':
returnlayout = parcel_management.layout
elif pathname == '/parcel_management/add_new_group':
returnlayout = add_new_parcel_group.layout
elif pathname == '/parcel_management/add_new_parcel':
returnlayout = add_new_parcel.layout
elif pathname == '/parcel_management/update_parcel_group':
returnlayout = update_parcel_group.layout
elif pathname == '/parcel_management/update_parcel_status':
returnlayout = update_parcel_status.layout
elif pathname == '/dashboard':
returnlayout = dashboard.layout
elif pathname == '/parcel_forecast':
returnlayout = parcel_forecast.layout
else:
returnlayout = '404: request not found'
# decide sessionlogout value
logout_conditions = [
pathname in ['/', '/logout'],
userid == -1,
not userid
]
sessionlogout = any(logout_conditions)
# hide navbar if logged-out; else, set class/style to default
navbar_classname = 'd-none' if sessionlogout else ''
return [returnlayout, sessionlogout, navbar_classname]
else:
raise PreventUpdate
if __name__ == '__main__':
webbrowser.open('http://127.0.0.1:8050/', new=0, autoraise=True)
app.run_server(debug=False)
here is my login.py
from app import app
from apps import dbconnect as db
layout = html.Div(
[
html.H2('Please Login'),
html.Hr(),
dbc.Alert('Username or password is incorrect.', color="danger", id='login_alert',
is_open=False),
dbc.Row(
[
dbc.Label("Username", width=2),
dbc.Col(
dbc.Input(
type="text", id="login_username", placeholder="Enter username"
),
width=6,
),
],
className="mb-3",
),
dbc.Row(
[
dbc.Label("Password", width=2),
dbc.Col(
dbc.Input(
type="password", id="login_password", placeholder="Enter password"
),
width=6,
),
],
className="mb-3",
),
dbc.Button('Login', color="secondary", id='login_loginbtn'),
html.Hr(),
html.A('Signup Now!', href='/signup'),
]
)
@app.callback(
[
Output('login_alert', 'is_open'),
Output('currentuserid', 'data'),
],
[
Input('login_loginbtn', 'n_clicks'), # begin login query via button click
Input('sessionlogout', 'modified_timestamp'), # reset session userid to -1 if logged out
],
[
State('login_username', 'value'),
State('login_password', 'value'),
State('sessionlogout', 'data'),
State('currentuserid', 'data'),
State('url', 'pathname'),
]
)
def loginprocess(loginbtn, sessionlogout_time,
username, password,
sessionlogout, currentuserid,
pathname):
ctx = callback_context
if ctx.triggered:
openalert = False
eventid = ctx.triggered[0]['prop_id'].split('.')[0]
else:
raise PreventUpdate
if eventid == 'login_loginbtn': # trigger for login process
if loginbtn and username and password:
sql = """SELECT user_id
FROM users
WHERE
user_name = %s AND
user_password = %s AND
NOT user_delete_ind"""
# we match the encrypted input to the encrypted password in the db
encrypt_string = lambda string: hashlib.sha256(string.encode('utf-8')).hexdigest()
values = [username, encrypt_string(password)]
cols = ['userid']
df = db.querydatafromdatabase(sql, values, cols)
if df.shape[0]: # if query returns rows
currentuserid = df['userid'][0]
else:
currentuserid = -1
openalert = True
elif eventid == 'sessionlogout' and pathname == '/logout': # reset the userid if logged out
currentuserid = -1
else:
raise PreventUpdate
return [openalert, currentuserid]
@app.callback(
[
Output('url', 'pathname'),
],
[
Input('currentuserid', 'modified_timestamp'),
],
[
State('currentuserid', 'data'),
]
)
def routelogin(logintime, userid):
ctx = callback_context
if ctx.triggered:
if userid > 0:
url = '/home'
else:
url = '/'
else:
raise PreventUpdate
return [url]
here is my update_parcep_status.py
dash.register_page(__name__, path='/')
layout = dbc.Container([
html.H1("Update Parcel Status"),
dbc.Form([
dbc.CardGroup([ # Use CardGroup instead of FormGroup
dbc.Label("Parcel ID*"),
dcc.Dropdown(
id='parcel-id',
placeholder="Select Status",
style={'width': '100%'}
),
]),
dbc.CardGroup([ # Use CardGroup instead of FormGroup
dbc.Label("Status*"),
dcc.Dropdown(
id='status-id',
placeholder="Select Status",
style={'width': '100%'}
),
]),
dbc.CardGroup([ # Use CardGroup instead of FormGroup
dbc.Label("Location"),
dbc.Input(type="text", id="location"),
]),
html.Br(),
dbc.Button("Update Status", id="update-button", color="primary"),
html.Div(id='output-container-button'), # Placeholder for callback output
]),
dcc.Interval(
id='interval-component',
interval=60000, # in milliseconds
n_intervals=0
)
], fluid=True)
@app.callback(
Output('status-id', 'options'),
[Input('interval-component', 'n_intervals')]
)
def update_status_options(n):
print("Callback1") # Debug statement to check if callback is triggered
try:
# Fetch data from the parcelstatus_mapping table
query = """
SELECT status_id, status_desc
FROM parcelstatus_mapping;
"""
df = db.querydatafromdatabase(query, values=[], dfcolumns=['status_id', 'status_desc'])
# print (df)
# Convert DataFrame to a list of dictionaries suitable for dropdown options
options = [{'label': desc, 'value': str(status_id)} for status_id, desc in zip(df['status_id'], df['status_desc'])]
except Exception as e:
print(f"Error fetching data for status options: {e}")
options = []
return options
@app.callback(
Output('parcel-id', 'options'),
[Input('interval-component', 'n_intervals')]
)
def update_parcel_options(n):
print("Callback2") # Debug statement to check if callback is triggered
try:
# Fetch data from the parcel_status table
# print('a')
query = """
SELECT parcel_id
FROM parcel_status;
"""
df = db.querydatafromdatabase(query, values=[], dfcolumns=['parcel_id'])
# Convert DataFrame to a list of dictionaries suitable for dropdown options
options = [{'label': str(parcel_id), 'value': str(parcel_id)} for parcel_id in df['parcel_id']]
except Exception as e:
print(f"Error fetching data for parcel options: {e}")
options = []
return options
@app.callback(
Output('output-container-button', 'children'),
[Input('update-button', 'n_clicks'),
Input('url', 'pathname')],
[State('parcel-id', 'value'),
State('location', 'value')]
)
def add_new_parcel_submit(n_clicks, parcel_id, location, pathname):
if pathname != '/parcel_management/update_parcel_status':
raise PreventUpdate # Callback only runs when URL matches '/parcel_management/update_parcel_status'
if not n_clicks:
raise PreventUpdate
# return [html.Div([f"Parcel ID: {parcel_id}, Location: {location}"])]
print("Callback triggered with n_clicks:", n_clicks) # Debug statement to check if callback is triggered
if not n_clicks:
raise PreventUpdate
alert_open = True
alert_color = 'danger'
alert_text = 'Failed to add new parcel.'
modal_open = False
if not all([parcel_id, location]):
alert_text = 'Check your inputs. Please fill in all the required fields.'
print("Not all fields filled:", parcel_id, location) # Debug statement for incomplete fields
else:
try:
# Constructing the SQL query
sql = '''
INSERT INTO parcel_status (parcel_id, update_timestamp, location)
'''
# Adding the values to the values list
values = [parcel_id, 1, datetime.now(), location]
print("SQL values:", values) # Debug statement for SQL values
sql += ') VALUES (' + ', '.join(['%s'] * len(values)) + ')'
print("SQL query:", sql) # Debug statement for constructed SQL query
# db.modifydatabase(sql, values)
print("Database modification successful") # Debug statement for successful database modification
alert_open = True
alert_color = 'success'
alert_text = 'New parcel added successfully.'
modal_open = True
except Exception as e:
alert_text = f'Failed to add new parcel: {str(e)}'
print("Exception occurred:", e) # Debug statement for exception
return [html.Div(alert_text, className=f'alert alert-{alert_color}', role='alert'), modal_open]
I tried to check the ids and all seems to be correct. i tried to put the preventupdate for the
add_new_parcel_submit to only be called when in that app, but adding that callback still breaks the code. Can you help pls? I am stucked for days here huhu
Acid is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.