Dynamic selection boxes within forms


Hi all!

I’m looking for an efficient way of retrieving data from BigQuery based on user input from multiple selection boxes, where the selection boxes are dynamic. Using forms almost works for my use case - I’m unable to make the selection boxes within a form to dynamically update based on the preceding selections.

I can get the selection boxes to be dynamic if I don’t have them defined inside a form, but the trade-off is the database is queried every time a different selection is made in any one of the selection boxes.

Is there a way to maintain the dynamic selection boxes and only query the database when a button is pressed?

Steps to reproduce

Code snippet:

import pandas as pd
import streamlit as st

data = {
    'year': [2023, 2023, 2023, 2022, 2022, 2022, 2022, 2022, 2022],
    'segment': ['a', 'b', 'c', 'a', 'b', 'c', 'd', 'e', 'f']

df = pd.DataFrame(data)

# Select query parameters using a form to retrieve data from BigQuery
with st.sidebar.form(key='Selection'):
    st.info('Selection using form')
    years = sorted(df.year.unique())
    form_year_select = st.selectbox('Select Year', years)
    segments = sorted(df.segment[df.year == form_year_select].unique())
    form_segment_select = st.selectbox('Select Segment', segments)
    selection_submitted = st.form_submit_button('Submit')

form_query = f"""
select * from table where year = {form_year_select} and segment = '{form_segment_select}' 

# Select query parameters without using a form to retrieve data from BigQuery
st.info('Selection without using form')
years = sorted(df.year.unique())
year_select = st.selectbox('Select Year', years)
segments = sorted(df.segment[df.year == year_select].unique())
segment_select = st.selectbox('Select Segment', segments)

query = f"""
select * from table where year = {year_select} and segment = '{segment_select}' 

Expected behavior:

Each selection box should be dynamic where it selects only possible values from the DF e.g., if modelyear 2023 is selected, it shouldn’t show values for segments d, e, and f.

Actual behavior:

The options in the selection box are based on the default values when the form is initiated. Only when you click the Submit button do the values change in the selection boxes. This can lead to invalid parameters being parsed into the query e.g., model year = 2023 and segment = f.

The nature of a form is that no information is sent to the backend until a user clicks submit. So no, you can’t have (real-time) dynamically changing selection boxes within a form unless the source of the change is outside of the form.

However, you can dispense with the form and use other methods to make sure the database isn’t repeatedly queried. Instead of just running the query inline without restriction, condition it on a button, saving the result to session state so that it won’t get re-queries until the user clicks the button again.

import streamlit as st
import pandas as pd

if 'df' not in st.session_state:
    data = {
        'year': [2023, 2023, 2023, 2022, 2022, 2022, 2022, 2022, 2022],
        'segment': ['a', 'b', 'c', 'a', 'b', 'c', 'd', 'e', 'f']
    st.session_state.df = pd.DataFrame(data)

df = st.session_state.df

year = st.selectbox('Year',df['year'].unique())
segment = st.selectbox('Segment',df[df['year']==year]['segment'].unique())

if st.button('Run Query'):
    #Run the query here, which will only execute one per click
    st.session_state.result = f'Result from query {year},{segment}'

if 'result' in st.session_state:

Thanks for your response @mathcatsand.

Have been playing around with your code suggestion today and have got the app working now.

Any thoughts on design patterns for loading the data (or not) when the app firsts loads?

Some of the code in the app fails if a DF is empty e.g.,

AttributeError: 'DataFrame' object has no attribute 'year'

Is it possible to prompt the user to make an initial selection before the app fully loads? Or would you preload with some data?

I’m not sure exactly the situations that would get you an empty response and thus how to best deal with it, but you can always add a check on the dataframe that it isn’t None or isn’t empty (whatever the cases may be). Then you put the remainder of the page inside some kind of “if valid” conditional before it proceeds with the selection options. The “else” condition could deal with the necessary details to overwrite st.session_state.df if that’s appropriate.

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.