When using data_editor, together with a button, whole script runs from top

I am trying to create a small app, that receives some data from an API, pushes it into a dataframe, which I want to modify and then send to another API. The issue is that I really want a button to trigger the download of the initial data. After receiving the data, I would like to modify it and then click another button to send it away.

What happens though, is after clicking the send button the app just runs from start without ever sending the data. If I remove the first button to receive the data, everything works as expected.
I think I do not fully understand how the session state works here. Maybe someone can explain.

Here is my code to reproduce (1.35.0):

import streamlit as st
import pandas as pd


def send_df_to_db(df):
    print(f"Sending df to DB:\n{df}")
    st.write("Sending df to DB:")
    st.write(df)

def get_df(dummy):
    df = pd.DataFrame(
        [
        {"command": "st.selectbox", "rating": 4, "is_widget": True},
        {"command": "st.balloons", "rating": 5, "is_widget": False},
        {"command": "st.time_input", "rating": 3, "is_widget": True},
    ]
    )
    return df


if st.button("Fetch Data"):
        st.write("Fetching data...")
        df = get_df(1)
        with st.form("Edit form:"):
            edited_df = st.data_editor(df, num_rows="dynamic")

            submitted = st.form_submit_button("Submit modified data")
            if submitted:
                send_df_to_db(edited_df)
1 Like

Take a look at: GitHub - seedoogithub/streamlitextend: streamlit_Extend , this the same problem we encountered . Its part of the streamlit design philosophy - the page always reloads on every event (which is great, and simplifies a lot of things and allows to build apps easily and quickly and with less bugs) . Yet if you want something a bit more advanced you always have to go for full react app . We implemented our project to solve this middle gap for our own app. It allows you to do the middle ground. Have events, without page reloading, but without doing a fully custom react app.

Thank you, this kind of confirms what I thought. There is no real solution, Streamlit just is this way.
Your project looks promising. I was working on something where I wanted mqtt data to update a df in a callback function. Your project could have lead me to the correct direction.

Hi

With session.state and you may be able to see @st.experimental_fragment

import streamlit as st
import pandas as pd

def send_df_to_db(df):
    print(f"Sending df to DB:\n{df}")
    st.write("Sending df to DB:")
    st.write(df)

def get_df():
    df = pd.DataFrame(
        [
            {"command": "st.selectbox", "rating": 4, "is_widget": True},
            {"command": "st.balloons", "rating": 5, "is_widget": False},
            {"command": "st.time_input", "rating": 3, "is_widget": True},
        ]
    )
    return df

# Initialize session state for the DataFrame
if 'df' not in st.session_state:
    st.session_state.df = None

if st.button("Fetch Data"):
    st.write("Fetching data...")
    st.session_state.df = get_df()

if st.session_state.df is not None:
    with st.form("Edit form:"):
        edited_df = st.data_editor(st.session_state.df, num_rows="dynamic")
        
        submitted = st.form_submit_button("Submit modified data")
        if submitted:
            send_df_to_db(edited_df)
            # Clear the session state after sending
            st.session_state.df = None
else:
    st.write("No data fetched yet.")
1 Like

Generally speaking, you will want to avoid nesting buttons inside buttons. Buttons don’t retain state; they’ll only be True immediately after they are clicked and then go back to False when the user makes their next click.

When you have something you don’t want to redo, you can use caching or Session State to save the info. Rule of thumb: if the loaded data is the same for all users, use caching. If the loaded data is specific to the user, use Session State. Additionally, if you have a process (or subprocess) you want to rerun independently of the rest of the app, use fragments.

@Oscar’s solution above shows a common way to use Session State with data download triggered by a button press.

1 Like