streamlit-pydantic icon indicating copy to clipboard operation
streamlit-pydantic copied to clipboard

Expander for long Forms / inputs

Open ChrisDelClea opened this issue 1 year ago • 6 comments

Hey @LukasMasuch , @HIL340 ,

just a simple feature request: is it somehow possible to have (for long forms) expanders as option for the input? So one would somehow need to define the upper most level (in case of nested BaseModels) and enable expanders between each nested model. For example here:

class Address(BaseModel):
    street: str
    city: str
    house: int


class GeneralData(BaseModel):
    email: str
    gender: bool


class ContactMethod(BaseModel):
    text: str   
   Post: List[PostalAddress]
   EmailAddress: List[GeneralData]

I would like to have an expander between Post and Email . Is that possible?

Best regards Chris

ChrisDelClea avatar Mar 23 '23 21:03 ChrisDelClea

You may not be aware of it but a feature very close to this already exists.

Have a look at the "optional fields" demo in the playground.

Does it work for what your trying to achieve?

HIL340 avatar Mar 24 '23 01:03 HIL340

Hey @HIL340 ,

i actually checked this before i created the issue. In my case, it does not work. I have a pydantic_input which does not work with group_optional_fields="expander" right?

Also in my ContactMethod i have optional fields besides the Subclasses GeneralData and Address e.g. text , that is optional and therefore, it would also get expanded?

Adding something like sp.pydantic_input(group_optional_fields="subclasses") would be the functionallity i am looking for!

ChrisDelClea avatar Mar 24 '23 07:03 ChrisDelClea

You can group the inputs into an expander on a st.pydantic_input (it doesn't have to be a st.pydantic_form like the demo).

Translating the demo to a pydantic_input version and using your data model:

import json
from typing import List, Optional

import streamlit as st
from pydantic import BaseModel
from pydantic.json import pydantic_encoder

import streamlit_pydantic as sp

class PostalAddress(BaseModel):
    street: str
    city: str
    house: int


class GeneralData(BaseModel):
    email: str
    gender: bool


class ContactMethod(BaseModel):
    text: str
    Post: List[PostalAddress]
    EmailAddress: Optional[List[GeneralData]]


data = sp.pydantic_input(
    key="my_form", model=ContactMethod, group_optional_fields="expander"
)

if data:
    st.json(json.dumps(data, default=pydantic_encoder))

Results in: image

(after clicking Add Item on each of the lists)

It does appear though that if you put the Optionals on the subclass fields rather than the parent of the subclass it doesn't work.

eg.

class PostalAddress(BaseModel):
    street: str
    city: str
    house: int

class GeneralData(BaseModel):
    email: Optional[str]
    gender: Optional[bool]

class ContactMethod(BaseModel):
    text: str
    Post: List[PostalAddress]
    EmailAddress: List[GeneralData]

This is probably a mistake/bug but may actually work better for your purposes!

HIL340 avatar Mar 24 '23 08:03 HIL340

Exactly the latter is also the case with me.
Only that I have wrapped for safity reasons both on subclass level and the EmailAddress with Optional.
In addition, I also initialize the whole thing via an instance. That means i get an error, as soon as one field is not set, due to the fact, that field is not optional anymore.

Is this fixable, @HIL340 @LukasMasuch ?

ChrisDelClea avatar Mar 29 '23 11:03 ChrisDelClea

Its very likely fixable.

Its not a feature I currently need for any of my projects right now so its a low priority for me at the moment.

If you have the motivation and specific expectations on how the feature should work the quickest way forward would be to code it up and submit a PR!

HIL340 avatar Apr 13 '23 06:04 HIL340