Right now we allow users to customise some data, and then hit a button to upload this data into an S3 bucket. Right now, we’re trying to figure out how to ensure this upload happens once, but right now it seems if a user double clicks this button then the streamlit code will run twice.
We’ve tried using st.session_state, global is_uploading variables, and even a threading Lock to try and ensure that this upload can only happen once. I’m sure there must be an easy solution I’ve overlooked, and so here I am, requesting some help in doing this!
Steps to reproduce
import time
import streamlit as st
def dummy_upload():
print("Uploading!")
time.sleep(1)
print("Upload done!")
if __name__ == "__main__":
if st.button("Upload now"):
dummy_upload()
Expected behavior:
A pattern whereby we only trigger the upload once (aka the dummy_upload is only called once, or theres a way to return out of it if something else is already executing).
Actual behavior:
A double click causes a double upload.
Debug info
Streamlit version: 1.25.0
Python version: 3.11.4
pyenv
OS version: Ubuntu
Browser version: All
Requirements file
Using Conda? PipEnv? PyEnv? Pex? Share the contents of your requirements file here.
Not sure what a requirements file is? Check out this doc and add a requirements file to your app.
Links
Link to your GitHub repo:
Link to your deployed app:
Additional information
If needed, add any other context about the problem here.
I’m not sure about other options, but I think st.session_state should work for this:
import time
import streamlit as st
if 'button_clicked' not in st.session_state:
st.session_state['button_clicked']=False
def dummy_upload():
st.write("Uploading!")
time.sleep(1)
st.write("Upload done!")
if __name__ == "__main__":
mybutton = st.button("Upload now")
if not st.session_state['button_clicked']:
if mybutton:
dummy_upload()
st.session_state['button_clicked']=True
else:
st.write('Clicked the button already')
I might be missing something, but I believe this is the behaviour you are looking for. You can click the button only once, and after clicking the button, the button_clicked variable in the session state changes to True, which can be used in a simple if-else to avoid multiple clicks.
Please note that this will persist throughout the user session (until page refresh or app restart), so in your case, if the user modifies the data again, you will need to set the ‘button_clicked’ variable back to False in your modify() function to ensure the button can be clicked again when the user updates data.
Hey Moiz! I think the st.session_state gets us most of the way there, but I’m having trouble understanding how it functions (the initial reason I thought it was not applicable).
Take the following code:
import time
import streamlit as st
def dummy_upload():
key = "can_upload"
if st.session_state.get(key, True):
st.session_state[key] = False
print("Uploading!")
time.sleep(2)
st.session_state[key] = True
print("Upload done!")
if __name__ == "__main__":
with st.form("uploader"):
submit = st.form_submit_button("Upload now")
if submit:
dummy_upload()
print(st.session_state)
If I click, wait for the “Upload Done!” message, and then click again, it works great. If I double click, it stops the second flick from doing the fake upload. But for some reason I’m unsure about, if the user double clicks, it resets the session state.
Here’s the output from the prints if I do a “Click, wait enough, Click, wait enough, spam click”
For some reason the session state can_upload state is not being reset to true, as if the second click causes a copy of the state and then the original “Set it back to true” line has no effect.