Hello, I am trying to build a multipage app. I have a login page and one other page that is accessible only after a valid user logs in. I am using sqlite db for authentication. Without caching the sqlite db connection, the login page renders seamlessly. However, on caching the db connection, I notice glitchy rendering only on the very first interaction with all the widgets. It gets stable from the second time onwards. The code is as follows:
app.py
import streamlit as st
from page import login_page, species_search_page
from auth import init_db
def app_title():
st.title("Multipage App")
st.write("An Experimental Multipage App")
def main():
init_db()
app_title()
menu = ['Login', 'Species Search']
menu_selection = st.sidebar.radio("Menu", menu, key='menu_selection')
if menu_selection == 'Login':
if 'login' not in st.session_state or not st.session_state['login']:
login_page()
elif 'login' in st.session_state and st.session_state['login']:
st.success(
f"\n You are successfully logged in as user '{st.session_state.username}'")
elif menu_selection == 'Species Search':
if 'login' in st.session_state and st.session_state['login']:
species_search_page()
else:
st.warning("\n Please Login before you proceed \n")
if __name__ == "__main__":
main()
page.py
import streamlit as st
from callback import login_callback
def login_page():
login = st.container()
with login:
st.subheader("Login")
username = st.text_input("Username")
password = st.text_input("Password", type="password")
st.button("Login", on_click=login_callback,
args=(login, username, password,))
def species_search_page():
return
callback.py
import streamlit as st
from auth import is_valid_user
def login_callback(placeholder, username, password):
if is_valid_user(username, password):
st.session_state['username'] = username
st.session_state['password'] = password
st.session_state['login'] = True
else:
placeholder.error("Please Enter Valid Username and Password")
auth.py
import sqlite3
from config import URI_SQLITE_DB, ADMIN_USERNAME, ADMIN_PWD, USERS_TABLE
import streamlit as st
# @st.cache(allow_output_mutation=True)
def get_connection(path: str):
conn = sqlite3.connect(path, check_same_thread=False)
# st.session_state.sqlite_conn = conn
return conn
def init_db():
conn = get_connection(URI_SQLITE_DB)
conn.execute(
f"""CREATE TABLE IF NOT EXISTS {USERS_TABLE}
(
USERNAME TEXT,
PASSWORD TEXT,
IS_ADMIN INTEGER
);"""
)
cur = conn.execute("SELECT * FROM USERS WHERE USERNAME = ? AND PASSWORD = ?",
(ADMIN_USERNAME, ADMIN_PWD))
if cur.fetchone() == None:
conn.execute(
f"INSERT INTO {USERS_TABLE} VALUES (?, ?, ?)", (ADMIN_USERNAME, ADMIN_PWD, 1))
conn.commit()
conn.close()
def is_valid_user(username, password):
conn = get_connection(URI_SQLITE_DB)
# conn = st.session_state.sqlite_conn
cur = conn.execute(f"SELECT * FROM {USERS_TABLE} WHERE USERNAME = ? AND PASSWORD = ?",
(username, password))
if cur.fetchone() == None:
return False
else:
return True
conn.close()
config.py
URI_SQLITE_DB = "auth.db"
ADMIN_USERNAME = "admin"
ADMIN_PWD = "admin"
USERS_TABLE = "USERS"
With Caching:
Without:
Tried storing the db connection in the session state and used it in the is_user_valid function instead of doing a cache hit, and even that seems to work fine. But I wonder which is the best practice.
Appreciate any help!