Date_input not working on form submit

When I submit a form with 2 date inputs for start and end dates, I am not able to get the values that I select. Not sure at all what I am doing wrong.

Code snippet:

def calendarForm(mindate,maxdate):
    with col2:
        with st.form(key="metercalendar",clear_on_submit=True):
            sd = st.date_input('Select a Start Date (Earliest date is shown)',value=mindate,min_value=mindate,max_value=maxdate,key='startDate')
            ed = st.date_input('Select an End Date (Latest date is shown)',value=maxdate,min_value=mindate,max_value=maxdate,key='endDate')
            submitBtn = st.form_submit_button('Get Hourly Consumption',on_click=getHourly,args=(sd,ed))

def getHourly(sd,ed):

If applicable, please provide the steps we should take to reproduce the error or specified behavior.

Expected behavior:
sd and ed should have the dates that I select and not the default dates with which the widget is initialized
Actual behavior:
I always get sd and ed to be the default dates with which I initialize the widget.

Explain the undesired behavior or error you see when you run the code above.
If you’re seeing an error message, share the full contents of the error message here.

Debug info

  • Streamlit version: 1.14.1
  • Python version: 3.10.6
  • Using PipEnv
  • OS version: Ubuntu 22
  • Browser version: Google Chrome Version 107.0.5304.110 (Official Build) (64-bit)

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.

Hey @cdiwadkar,

Thanks for sharing your question! Can you share a more complete code snippet (i.e. where you’re calling the functions you’ve defined) so we can reproduce the issue?

Here’s the entire script:

import taos
import streamlit as st
import pandas as pd
import taosrest
from datetime import datetime
import time
import streamlit.components.v1 as components
import as px

st.sidebar.markdown("## Beijing Multi-site Air Quality Demo")
st.sidebar.markdown("** This database contains data from the Beijing Multi-site Air Quality Dataset.**")
st.sidebar.markdown("** This dataset can be found here: [Beijing Multi-site Air Quality Dataset](https://archive.ics.uci.e

conn = taos.connect(host="localhost",

containerHeader = st.container()
with containerHeader:
    st.title("Beijing Multi-site Air Quality Demo")
containerTop = st.container()
with containerTop:
    col1,col2 = st.columns([2,4])
containerBottom = st.container()

def countRows (): 
    stmt = 'SELECT COUNT(*) FROM pollution'
    result: taos.TaosResult = conn.query(stmt)
    rows = result.fetch_all_into_dict()
    col1.markdown("This executes the following statement - ")

def execDesc (stablename): 
    stmt = 'describe '+ stablename
    print('--------- describe '+stablename)
    result: taos.TaosResult = conn.query(stmt)
    rows = result.fetch_all_into_dict()
    return rows

def showStables():
    col1.markdown("This executes the following statement -")
    col1.markdown("``SHOW STABLES``")
    result: taos.TaosResult = conn.query("show stables")
    rows = result.fetch_all_into_dict()

def showSchema():
    col1.markdown("This executes the following statement on each Super Table - ")
    col1.markdown("``DESCRIBE <Super Table Name>``")
    result: taos.TaosResult = conn.query("show stables")
    rows = result.fetch_all_into_dict()
    for i in rows:
        col2.write('Super Table: ' + i["stable_name"])
        rows = execDesc(i["stable_name"])

def countTables():
    col1.markdown("This executes the following statement - ")
    selectstmt = 'SELECT COUNT(*) FROM (SELECT DISTINCT TBNAME FROM pollution)'
    result: taos.TaosResult = conn.query(selectstmt)
    rows = result.fetch_all_into_dict()

def calendarForm(mindate,maxdate):
    with col1:
        with st.form(key="metercalendar",clear_on_submit=True):
            sd = st.date_input('Select a Start Date (Earliest date is shown)',value=mindate,min_value=mindate,max_value=ma
            ed = st.date_input('Select an End Date (Latest date is shown)',value=maxdate,min_value=mindate,max_value=maxda
            submitBtn = st.form_submit_button('Get Hourly Consumption',on_click=getWeeklyTWA,args=(sd,ed))

def weeklyTWA():
    selectstmt = 'SELECT first(ts) FROM pollution'
    result: taos.TaosResult = conn.query(selectstmt)
    rows = result.fetch_all_into_dict()
    st.session_state['minDate'] = rows[0]['first(ts)']
    selectstmt = 'SELECT last(ts) FROM pollution'
    result: taos.TaosResult = conn.query(selectstmt)
    rows = result.fetch_all_into_dict()
    st.session_state['maxDate'] = rows[0]['last(ts)']['maxDate'])['minDate'])
def getWeeklyTWA(sd,ed):
    wklyselect = f"SELECT _wstart, twa(pm25),station FROM weather.pollution  where ts> '{sd}' and ts < '{ed}' partition by
 station INTERVAL(1w)"
    print('##' + wklyselect)
    col2.markdown('### This executes the following statement and then plots it in Plotly and in Grafana - ')
    df: pd.DataFrame = pd.read_sql(wklyselect, conn)
    fig = px.line(df,x='_wstart',y='twa(pm25)',color='station', 
                "twa(pm25)":"PM 2.5(ppm)",
    with containerBottom:
        st.markdown('### Plotly Line Chart')
        st.markdown('### Grafana Chart')
000&panelId=4", width=950,height=500,scrolling=True)

st.sidebar.button("Show Super Tables",on_click=showStables)

st.sidebar.button("Show the schema of STables",on_click=showSchema)

st.sidebar.button("Show total # of tables",on_click=countTables)
st.sidebar.button("Show total # of rows",on_click=countRows)

st.sidebar.button("Weekly PM 2.5 Time Weighted Average",on_click=weeklyTWA)

From what I see:

  1. You have a button, and via a callback
  2. you run your weeklyTWA function inside which
  3. you call your calendarForm function inside which
  4. you create a form and submit button, and via another callback
  5. you run your getWeeklyTWA function, feeding it the outputs from your form that were present when they were created on the page, not when they were submitted

When you put arguments into a callback, they will be evaluated at the time the widget with the callback was created and not at the time the widget is clicked. In case of your form, you have specified a default value equal to the minimum for your two dates, so the submit button is all ready to run your getWeeklyTWA function with those default minimum values no matter what the user does to change the selection before clicking submit.

In general, I shy away from creating widgets within a callback. (Not that it can’t be done, but I usually find it a bit clearer and more direct to get at data associated with widgets outside of callbacks.) As soon as a widget disappears, so too does its data/state and you’ll never have any use for storing the output of a widget if it does not remain on the screen to feed the updated value to that variable receiving the output.

@mathcatsand I see. But it’s a little confusing because if that is the case what does the submit button for my form do? So for e.g. if a form is created, and a widget is created inside it, and the widget has a default value, but I change the value on the widget, shouldn’t the form submit get the latest value?
Is the behavior an artifact of the callback function or the default behavior in Streamlit?
Thanks for helping!

When you click a form submit button, all of the widgets within the form will update. You have two options of retrieving the information:

  1. By having variables take in the output of the widgets
  2. By collecting it from session state

However, if the widgets do not exist on the page after a submit button is clicked, option 1 is out the window. Furthermore, you will be restricted on how you implement option 2 as the information (as natively stored) will only be available for one page load, then gone.

As such, a better workflow with a submit button if you are having the form disappear is to use a callback function to grab and store that information for later use.

Here’s a little toy case so you can see what happens to session state when a form disappears from view (Show/Hide button) and what happens with reloads/other interactions with the page (Page Reload button). In particular if you submit a form, hide it, then click the reload button, you’ll see the information associated directly to the widgets goes away, but the copy I made in the callback to other keys will stay.

import streamlit as st


def submit ():
    st.session_state.last_A = st.session_state.A
    st.session_state.last_B = st.session_state.B
if 'show' not in st.session_state: = True

def toggle(): = not

st.button('Show/Hide Form', on_click=toggle)

    with st.form('my_form'):
        st.text_input('Text', key='A')
        st.number_input('Number', key='B')
        st.form_submit_button('Submit', on_click = submit)

st.button('Page Reload')


And yes, the logic flow of the callback is coming in to play, namely that the values you want to pass as arguments are evaluated at page load and not at click. So you have to get “new information” from inside the callback, with the lookup happening to session state after the click.