Multi_select for st.data_editor

Hi,

I have exhausted all of my options of finding a solution on this forum, google, chat_gpt etc. so posting here as a last resort, hoping to seek expert’s help for something which was supposed to be a simple fix.

I’m fetching a table from my database which has a column lets say ‘A’, the table is displayed using st.data_editor. My use case needs to have a multi_select option for this col which is not available for st.data_editor natively.

So as a work around, I made a new column in my table, let’s say ‘B’ with st.selectbox, I have defined Col A as st.column_config.ListColumn, now I want to write a simple function which takes the value of col B and append it in Col A, Col A can either be empty or it may have some value in it. and then col B should get empty (for user to select any new value, if required), and the st.data_editor should show the updated table.

Any ideas will be highly appreciated. Thanking in advance.

1 Like

Hello, how are you?

In my project, I use some cases where updates are made through a dropdown selection in a table cell, using st.data_editor and st.dataframe, where the user makes a change, and that change is saved to the database.

I might have something that could help you, but I need you to explain your requirement more clearly. If you have visual examples, even better, so I can understand and assist you more effectively.

Thanks @753247 for responding,

So the requirement is pretty simple, allow users to select single options from one column and add the single selected options to another column, then clear it for the user to select the next option in an attempt to make multi_select option in st.data_editor.

first i’m fetching my table from the db:
data = fetch_sla()

then adding a new col, which is not part of the table fetched from the db:
data = fetch_sla()
data[“Add_Vendor”] = “” # Add a new column with default value empty string

My col_config are for the two col under discussion are as following:
column_config = {
“Vendor” : st.column_config.ListColumn(“Vendor(s)”, help=“Use Detailed: Popup View to edit Vendor(s) name.”),
“Add_Vendor” : st.column_config.SelectboxColumn(“Add Vendor”, options=Vendor)
}

As can be seen, the “Vendor” is of list type and “Add_Vendor” is of selectbox type. Here I want that the option selected by the user in “Add_Vendor” col should be added/appended in the list col “Vendor” and “Add_Vendor” col should return to being an empty cell, so the user cal select any other option (if required). This way I want to achieve that the user can select as many options as he want from “Add_Vendor” col which should be getting added in the “Vendor” Col.

One of my failed attempts at doing so is as following:

Display the SLA table in editable form

edit_data=st.data_editor(data, key=“sla_editor”, column_config=column_config, column_order=final_col, use_container_width=True, hide_index=True, num_rows=“dynamic”)

Check if the data has been edited

if edit_data is not None and not edit_data.equals(st.session_state.data):
# Iterate through rows and handle Add_Vendor logic
for idx, row in edit_data.iterrows():
if row[“Add_Vendor”]: # Check if Add_Vendor is not None
if isinstance(row[“Vendor”], list): # Ensure Vendor is a list
row[“Vendor”].append(row[“Add_Vendor”]) # Add Add_Vendor value to Vendor
else:
row[“Vendor”] = [row[“Add_Vendor”]] # Initialize Vendor as a list with Add_Vendor value
row[“Add_Vendor”] = “” # Clear the Add_Vendor column

# Update the session state with the modified data
st.session_state.data = edit_data

st.rerun()



So after couple of attempts, I have found a solution that works for me atleast.

  1. It allows user to select option from one col (i.e. “Add_Vendor”), which are populated in another col (i.e. “Vendor”). This way user can mimic multi_select in st.data_editor and all of the selected options are available in “Vendor” col.
  2. If the selected option from “Add_Vendor” col is not available in “Vendor” col, it is added, if it’s already available, then its deleted. This allows to user to add/remove entries from list col, which is also natively not editable.
  3. The addition/deletion is updated in the DB and the table is refreshed.
    I’m sharing my code below for the community. This might not be an exact solution for the named topic i.e. [Multi_select for st.data_editor] but sometimes, stumbling upon different options can help brainstorm ideas.

function to push changes made in the table to the DB

def update_edit_row():
edited_rows = st.session_state.sla_editor.get(“edited_rows”, {})
added_rows = st.session_state.sla_editor.get(“added_rows”, )
deleted_rows = st.session_state.sla_editor.get(“deleted_rows”, )
# Establish a connection to the database
conn = get_connection()
cursor = conn.cursor()
# Process edited rows, email is being used as index here
if edited_rows:
row_index, row_data = next(iter(edited_rows.items()))
if “Add_Vendor” in row_data:
add_vendor_value = row_data[“Add_Vendor”]
# Fetch current Vendor column value from the database
cursor.execute(
“SELECT Vendor FROM SLA WHERE ROWID = ?”, (row_index + 1,)
)
result = cursor.fetchone()
current_vendor = result[0] if result else “”
# Split the current vendor values into a list and check for duplicates
vendor_list = current_vendor.split(“,”) if current_vendor else
if add_vendor_value in vendor_list:
# Remove Add_Vendor value if it already exists
vendor_list.remove(add_vendor_value)
else:
# Append Add_Vendor value to the existing Vendor column value
vendor_list.append(add_vendor_value)
# Update the Vendor column with the modified list
updated_vendor = “,”.join(vendor_list).strip(“,”)
# Update the Vendor column in the database
cursor.execute(
“”"
UPDATE SLA
SET Vendor = ?
WHERE ROWID = ?
“”“,
(updated_vendor, row_index + 1),
)
else:
column, value = next(iter(row_data.items()))
cursor.execute(
f”“”
UPDATE SLA
SET {column} = ?
WHERE ROWID = ?
“”“,
(value, row_index + 1), # SQLite ROWID starts at 1
)
# Process added rows
if added_rows:
for row_data in added_rows:
# Handle Add_Vendor column
add_vendor_value = row_data.pop(“Add_Vendor”, None)
current_vendor = row_data.get(“Vendor”, “”)
# Split the current vendor values into a list
vendor_list = current_vendor.split(”,“) if current_vendor else
if add_vendor_value:
if add_vendor_value in vendor_list:
# Remove Add_Vendor value if it already exists
vendor_list.remove(add_vendor_value)
else:
# Append Add_Vendor value to the Vendor column
vendor_list.append(add_vendor_value)
# Update the Vendor column in the row data
row_data[“Vendor”] = “,”.join(vendor_list).strip(”,“)
# Insert the row into the database
columns = “, “.join(row_data.keys())
placeholders = “, “.join([”?” for _ in row_data])
values = tuple(row_data.values())
cursor.execute(
f”””
INSERT INTO SLA ({columns})
VALUES ({placeholders})
“”“,
values,
)
# Process deleted rows to delete users from the database
if deleted_rows:
for row_index in deleted_rows:
try:
cursor.execute(“DELETE FROM SLA WHERE ROWID = ?”, (row_index + 1,)) # SQLite ROWID starts at 1
except Exception as e:
st.error(f"Error deleting row #{row_index}: {str(e)}”)
# Commit changes to DB, Clear Cached SLA Table, Close Connection, and rerun to update table in the UI
conn.commit()
fetch_sla.clear()
conn.close()

Display the SLA table in editable form

edit_data=st.data_editor(data, key=“sla_editor”, column_config=column_config, column_order=final_col, use_container_width=True, hide_index=True, num_rows=“dynamic”)
if any(st.session_state.sla_editor.values()):
update_edit_row()
st.rerun()

Screenshots

In the below screenshot, please observe that selecting “22 Media Corporation” in “Add/Delete Vendor” Col adds it in the “Vendor” Col, selecting it again, deletes it. Though the screenshot is for only one option, the user can select multiple options as well.





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