diff --git a/tutorials/streamlit-pydantic/complex_defaults.py b/tutorials/streamlit-pydantic/complex_defaults.py new file mode 100644 index 0000000..2c8c7b3 --- /dev/null +++ b/tutorials/streamlit-pydantic/complex_defaults.py @@ -0,0 +1,66 @@ +from enum import Enum +from typing import Dict, List, Set + +import streamlit as st +from pydantic import BaseModel, Field +from pydantic.color import Color + +import streamlit_pydantic as sp + + +class OtherData(BaseModel): + text: str = "default_text" + integer: int = 99 + + +class SelectionValue(str, Enum): + FOO = "foo" + BAR = "bar" + + +class ExampleModel(BaseModel): + """ + A model to showcase and test different types of pydantic fields with default values" + """ + + long_text: str = Field( + "default string", format="multi-line", description="Unlimited text property" + ) + integer_in_range: int = Field( + 22, + ge=10, + le=30, + multiple_of=2, + description="Number property with a limited range", + ) + single_selection: SelectionValue = Field( + "bar", description="Only select a single item from a set." + ) + multi_selection: Set[SelectionValue] = Field( + "bar", description="Allows multiple items from a set." + ) + read_only_text: str = Field( + "Lorem ipsum dolor sit amet", + description="This is ready only text.", + readOnly=True, + ) + default_color: Color = Field("yellow", description="A defaulted color") + default_object: OtherData = Field( + OtherData(), + description="An object embedded into the model with a default", + ) + overriden_default_object: OtherData = Field( + OtherData(text="overridden object text", integer="12"), + description="Default object overrides the embedded object defaults", + ) + default_dict: Dict[str, str] = {"foo": "bar"} + default_list: List[str] = ["foo", "bar"] + default_object_list: List[OtherData] = Field( + [OtherData()], + description="A list of objects with a default object in the list", + ) + + +data = sp.pydantic_input(key="my_input", model=ExampleModel) +if data: + st.json(data) diff --git a/tutorials/streamlit-pydantic/complex_disabled_showcase.py b/tutorials/streamlit-pydantic/complex_disabled_showcase.py new file mode 100644 index 0000000..27c6c57 --- /dev/null +++ b/tutorials/streamlit-pydantic/complex_disabled_showcase.py @@ -0,0 +1,158 @@ +import datetime +from enum import Enum +from typing import Dict, List, Literal, Optional, Set + +import streamlit as st +from pydantic import BaseModel, Field, SecretStr +from pydantic.color import Color + +import streamlit_pydantic as sp +from streamlit_pydantic.types import FileContent + + +class SelectionValue(str, Enum): + FOO = "foo" + BAR = "bar" + + +class OtherData(BaseModel): + text: str + integer: int + + +class DisabledModel(BaseModel): + short_text: str = Field( + ..., readOnly=True, max_length=60, description="Short text property" + ) + password: SecretStr = Field( + ..., readOnly=True, description="Password text property" + ) + long_text: str = Field( + ..., format="multi-line", readOnly=True, description="Unlimited text property" + ) + integer_in_range: int = Field( + 20, + ge=10, + le=30, + multiple_of=2, + readOnly=True, + description="Number property with a limited range. Optional because of default value.", + ) + positive_integer: int = Field( + ..., + ge=0, + multiple_of=10, + readOnly=True, + description="Positive integer with step count of 10.", + ) + float_number: float = Field(0.001, readOnly=True) + date: Optional[datetime.date] = Field( + datetime.date.today(), + readOnly=True, + description="Date property. Optional because of default value.", + ) + time: Optional[datetime.time] = Field( + datetime.datetime.now().time(), + readOnly=True, + description="Time property. Optional because of default value.", + ) + dt: Optional[datetime.datetime] = Field( + datetime.datetime.now(), + readOnly=True, + description="Datetime property. Optional because of default value.", + ) + boolean: bool = Field( + False, + readOnly=True, + description="Boolean property. Optional because of default value.", + ) + colour: Color = Field( + Color("Blue"), + readOnly=True, + description="Color property. Optional because of default value.", + ) + read_only_text: str = Field( + "Lorem ipsum dolor sit amet", + description="This is a read only text.", + readOnly=True, + ) + file_list: Optional[List[FileContent]] = Field( + None, + readOnly=True, + description="A list of files. Optional property.", + ) + single_file: Optional[FileContent] = Field( + None, + readOnly=True, + description="A single file. Optional property.", + ) + single_selection: SelectionValue = Field( + ..., readOnly=True, description="Only select a single item from a set." + ) + single_selection_with_literal: Literal["foo", "bar"] = Field( + "foo", readOnly=True, description="Only select a single item from a set." + ) + multi_selection: Set[SelectionValue] = Field( + ..., readOnly=True, description="Allows multiple items from a set." + ) + multi_selection_with_literal: Set[Literal["foo", "bar"]] = Field( + ["foo", "bar"], readOnly=True, description="Allows multiple items from a set." + ) + single_object: OtherData = Field( + ..., + readOnly=True, + description="Another object embedded into this model.", + ) + string_list: List[str] = Field( + ..., max_items=20, readOnly=True, description="List of string values" + ) + int_list: List[int] = Field(..., readOnly=True, description="List of int values") + string_dict: Dict[str, str] = Field( + ..., readOnly=True, description="Dict property with string values" + ) + float_dict: Dict[str, float] = Field( + ..., readOnly=True, description="Dict property with float values" + ) + object_list: List[OtherData] = Field( + ..., + readOnly=True, + description="A list of objects embedded into this model.", + ) + + +instance = DisabledModel( + short_text="Some INSTANCE text", + password="$uper_$ecret!", + long_text="This is some really long text from the INSTANCE", + integer_in_range=28, + positive_integer=20, + float_number=0.00444, + date=datetime.date(1999, 9, 9), + time=datetime.time(9, 9, 16), + dt=datetime.datetime(1999, 9, 9), + boolean=True, + colour=Color("Yellow"), + read_only_text="INSTANCE read only text", + single_selection=SelectionValue.FOO, + single_selection_with_literal="bar", + multi_selection=[SelectionValue.FOO, SelectionValue.BAR], + multi_selection_with_literal=["foo", "bar"], + single_object=OtherData(text="nested data INSTANCE text", integer=66), + string_list=["a", "ab", "abc"], + int_list=[9, 99, 999], + string_dict={"key 1": "A", "key 2": "B", "key 3": "C"}, + float_dict={"key A": 9.99, "key B": 66.0, "key C": -55.8}, + object_list=[ + OtherData(text="object list INSTANCE item 1", integer=6), + OtherData(text="object list INSTANCE item 2", integer=99), + ], +) + + +session_data = sp.pydantic_input( + key="my_disabled_input", + model=instance, +) + +with st.expander("Current Input State", expanded=False): + st.json(session_data) diff --git a/tutorials/streamlit-pydantic/complex_instance_model.py b/tutorials/streamlit-pydantic/complex_instance_model.py new file mode 100644 index 0000000..e4cd826 --- /dev/null +++ b/tutorials/streamlit-pydantic/complex_instance_model.py @@ -0,0 +1,153 @@ +import datetime +from enum import Enum +from typing import Dict, List, Literal, Optional, Set + +import streamlit as st +from pydantic import BaseModel, Field +from pydantic.color import Color + +import streamlit_pydantic as sp + + +class OtherData(BaseModel): + text: str + integer: int + + +class SelectionValue(str, Enum): + FOO = "foo" + BAR = "bar" + + +class ExampleModel(BaseModel): + some_number: float = 10.0 # Optional + some_text: str = Field(..., description="A text property") + some_text_with_an_alias: str = Field( + ..., description="A text property with an alias", alias="some_alias" + ) + some_integer: int = Field(20, description="An integer property.") + some_date: datetime.date = Field(..., description="A date.") + some_time: datetime.time = Field(..., description="A time.") + some_datetime: datetime.datetime = Field(..., description="A datetime.") + some_boolean: bool = False # Option + long_text: str = Field( + ..., format="multi-line", description="Unlimited text property" + ) + integer_in_range: int = Field( + 20, + ge=10, + le=30, + multiple_of=2, + description="Number property with a limited range.", + ) + some_colour: Color + single_selection: SelectionValue = Field( + ..., description="Only select a single item from a set." + ) + multi_selection: Set[SelectionValue] = Field( + ..., description="Allows multiple items from a set." + ) + disabled_selection: SelectionValue = Field( + ..., readOnly=True, description="A read only field that is shown as disabled" + ) + read_only_text: str = Field( + "Lorem ipsum dolor sit amet", + description="This is a ready only text.", + readOnly=True, + ) + nested_object: OtherData = Field( + ..., + description="Another object embedded into this model.", + ) + int_dict: Dict[str, int] = Field( + ..., + description="Dict property with int values", + gt=-4, + ) + date_dict: Dict[str, datetime.datetime] = Field( + ..., + description="Dict property with date values", + ) + bool_dict: Dict[str, bool] = Field( + ..., + description="Dict property with bool values", + ) + color_dict: Dict[str, Color] = Field( + ..., + description="A dict of colors embedded into this model.", + ) + int_list: List[int] = Field( + ..., + description="List of int values", + max_items=4, + min_items=2, + gt=2, + ) + color_list: List[Color] = Field( + ..., + description="List of color values", + min_items=2, + ) + object_list: List[OtherData] = Field( + ..., + max_items=5, + description="A list of objects embedded into this model.", + ) + object_dict: Dict[str, OtherData] = Field( + ..., + description="Dict property with complex values", + ) + + +instance = ExampleModel( + some_number=999.99, + some_text="Some INSTANCE text", + some_alias="Some INSTANCE alias text", + some_integer=0, + some_date=datetime.date(1999, 9, 9), + some_time=datetime.time(9, 9, 16), + some_datetime=datetime.datetime(1999, 9, 9), + integer_in_range=28, + some_boolean=True, + long_text="This is some really long text from the INSTANCE", + some_colour=Color("green"), + single_selection=SelectionValue.FOO, + disabled_selection=SelectionValue.BAR, + multi_selection=[SelectionValue.FOO, SelectionValue.BAR], + read_only_text="INSTANCE read only text", + nested_object=OtherData(text="nested data INSTANCE text", integer=66), + int_dict={"key 1": 33, "key 2": 33, "key 3": 333}, + date_dict={"date_key 1": datetime.datetime(1999, 9, 9)}, + bool_dict={"bool_key 1": True}, + color_dict={"Colour A": Color("#F3F3F3"), "Colour B": Color("#4E4E4E")}, + int_list=[9, 99, 999], + color_list=[Color("#F300F3"), Color("#00F3F3")], + object_list=[ + OtherData(text="object list INSTANCE item 1", integer=6), + OtherData(text="object list INSTANCE item 2", integer=99), + ], + object_dict={ + "obj 1": OtherData(text="object list dict item 1", integer=6), + }, +) + + +from_model_tab, from_instance_tab = st.tabs( + ["Form inputs from model", "Form inputs from instance"] +) + +with from_model_tab: + data = sp.pydantic_input(key="my_input_model", model=ExampleModel) + with st.expander("Current Input State", expanded=False): + st.json(data) + + +with from_instance_tab: + data = sp.pydantic_input(key="my_input_instance", model=instance) + with st.expander("Current Input State", expanded=False): + st.json(data) + +st.markdown("---") + +with st.expander("Session State", expanded=False): + st.write(st.session_state) diff --git a/tutorials/streamlit-pydantic/complex_nested_model.py b/tutorials/streamlit-pydantic/complex_nested_model.py new file mode 100644 index 0000000..97f7fc6 --- /dev/null +++ b/tutorials/streamlit-pydantic/complex_nested_model.py @@ -0,0 +1,50 @@ +from enum import Enum +from typing import Set + +import streamlit as st +from pydantic import BaseModel, Field, ValidationError, parse_obj_as + +import streamlit_pydantic as sp + + +class OtherData(BaseModel): + text: str + integer: int + + +class SelectionValue(str, Enum): + FOO = "foo" + BAR = "bar" + + +class ExampleModel(BaseModel): + long_text: str = Field( + ..., format="multi-line", description="Unlimited text property" + ) + integer_in_range: int = Field( + 20, + ge=10, + le=30, + multiple_of=2, + description="Number property with a limited range.", + ) + single_selection: SelectionValue = Field( + ..., description="Only select a single item from a set." + ) + multi_selection: Set[SelectionValue] = Field( + ..., description="Allows multiple items from a set." + ) + read_only_text: str = Field( + "Lorem ipsum dolor sit amet", + description="This is a ready only text.", + readOnly=True, + ) + single_object: OtherData = Field( + ..., + description="Another object embedded into this model.", + ) + + +data = sp.pydantic_form(key="my_form", model=ExampleModel) +if data: + st.json(data.json()) diff --git a/tutorials/streamlit-pydantic/complex_showcase.py b/tutorials/streamlit-pydantic/complex_showcase.py new file mode 100644 index 0000000..2d46ec1 --- /dev/null +++ b/tutorials/streamlit-pydantic/complex_showcase.py @@ -0,0 +1,101 @@ +import datetime +from enum import Enum +from typing import Dict, List, Literal, Optional, Set + +import streamlit as st +from pydantic import BaseModel, Field, SecretStr + +import streamlit_pydantic as sp +from streamlit_pydantic.types import FileContent + + +class SelectionValue(str, Enum): + FOO = "foo" + BAR = "bar" + + +class OtherData(BaseModel): + text: str + integer: int + + +class ShowcaseModel(BaseModel): + short_text: str = Field(..., max_length=60, description="Short text property") + password: SecretStr = Field(..., description="Password text property") + long_text: str = Field( + ..., format="multi-line", description="Unlimited text property" + ) + integer_in_range: int = Field( + 20, + ge=10, + le=30, + multiple_of=2, + description="Number property with a limited range. Optional because of default value.", + ) + positive_integer: int = Field( + ..., ge=0, multiple_of=10, description="Positive integer with step count of 10." + ) + float_number: float = Field(0.001) + date: Optional[datetime.date] = Field( + datetime.date.today(), + description="Date property. Optional because of default value.", + ) + time: Optional[datetime.time] = Field( + datetime.datetime.now().time(), + description="Time property. Optional because of default value.", + ) + boolean: bool = Field( + False, + description="Boolean property. Optional because of default value.", + ) + read_only_text: str = Field( + "Lorem ipsum dolor sit amet", + description="This is a ready only text.", + readOnly=True, + ) + file_list: Optional[List[FileContent]] = Field( + None, + description="A list of files. Optional property.", + ) + single_file: Optional[FileContent] = Field( + None, + description="A single file. Optional property.", + ) + single_selection: SelectionValue = Field( + ..., description="Only select a single item from a set." + ) + single_selection_with_literal: Literal["foo", "bar"] = Field( + "foo", description="Only select a single item from a set." + ) + multi_selection: Set[SelectionValue] = Field( + ..., description="Allows multiple items from a set." + ) + multi_selection_with_literal: Set[Literal["foo", "bar"]] = Field( + ["foo", "bar"], description="Allows multiple items from a set." + ) + single_object: OtherData = Field( + ..., + description="Another object embedded into this model.", + ) + string_list: List[str] = Field( + ..., max_items=20, description="List of string values" + ) + int_list: List[int] = Field(..., description="List of int values") + string_dict: Dict[str, str] = Field( + ..., description="Dict property with string values" + ) + float_dict: Dict[str, float] = Field( + ..., description="Dict property with float values" + ) + object_list: List[OtherData] = Field( + ..., + description="A list of objects embedded into this model.", + ) + + +session_data = sp.pydantic_input( + key="my_input", model=ShowcaseModel, group_optional_fields="sidebar" +) + +with st.expander("Current Input State", expanded=False): + st.json(session_data) diff --git a/tutorials/streamlit-pydantic/custom_form.py b/tutorials/streamlit-pydantic/custom_form.py new file mode 100644 index 0000000..f64cbe4 --- /dev/null +++ b/tutorials/streamlit-pydantic/custom_form.py @@ -0,0 +1,15 @@ +import streamlit as st +from pydantic import BaseModel + +import streamlit_pydantic as sp + + +class ExampleModel(BaseModel): + some_text: str + some_number: int = 10 + some_boolean: bool = True + + +with st.form(key="pydantic_form"): + sp.pydantic_input(key="my_input_model", model=ExampleModel) + submit_button = st.form_submit_button(label="Submit") diff --git a/tutorials/streamlit-pydantic/data_validation.py b/tutorials/streamlit-pydantic/data_validation.py new file mode 100644 index 0000000..827f6e9 --- /dev/null +++ b/tutorials/streamlit-pydantic/data_validation.py @@ -0,0 +1,16 @@ +import streamlit as st +from pydantic import BaseModel, Field, HttpUrl, EmailStr +from pydantic.color import Color + +import streamlit_pydantic as sp + + +class ExampleModel(BaseModel): + url: HttpUrl + color: Color + email: EmailStr + + +data = sp.pydantic_form(key="my_form", model=ExampleModel) +if data: + st.json(data.json()) diff --git a/tutorials/streamlit-pydantic/dataclass_form.py b/tutorials/streamlit-pydantic/dataclass_form.py new file mode 100644 index 0000000..58f4a08 --- /dev/null +++ b/tutorials/streamlit-pydantic/dataclass_form.py @@ -0,0 +1,35 @@ +import dataclasses +import json + +import streamlit as st +from pydantic.json import pydantic_encoder + +import streamlit_pydantic as sp + + +@dataclasses.dataclass +class ExampleModel: + some_number: int + some_boolean: bool + some_text: str = "default input" + + +from_model_tab, from_instance_tab = st.tabs( + ["Form inputs from model", "Form inputs from instance"] +) + +with from_model_tab: + data = sp.pydantic_form(key="my_form", model=ExampleModel) + if data: + st.json(json.dumps(data, default=pydantic_encoder)) + +with from_instance_tab: + instance = ExampleModel( + some_number=999, some_boolean=True, some_text="instance text" + ) + + # FIXME: this should be a pydantic_form to match the "from model.." above + # but initialising a pydantic_form with an instance is not yet supported + instance_input_data = sp.pydantic_input(key="my_form_instance", model=instance) + if instance_input_data: + st.json(json.dumps(instance_input_data, default=pydantic_encoder)) diff --git a/tutorials/streamlit-pydantic/multiple_forms.py b/tutorials/streamlit-pydantic/multiple_forms.py new file mode 100644 index 0000000..e97db8b --- /dev/null +++ b/tutorials/streamlit-pydantic/multiple_forms.py @@ -0,0 +1,25 @@ +import datetime + +import streamlit as st +from pydantic import BaseModel + +import streamlit_pydantic as sp + + +class ExampleModel(BaseModel): + some_text: str + some_number: int = 10 + some_boolean: bool = True + + +col1, col2 = st.columns(2) + +with col1: + data = sp.pydantic_form(key="form_1", model=ExampleModel) + if data: + st.json(data.json()) + +with col2: + data = sp.pydantic_form(key="form_2", model=ExampleModel) + if data: + st.json(data.json()) diff --git a/tutorials/streamlit-pydantic/optional_fields.py b/tutorials/streamlit-pydantic/optional_fields.py new file mode 100644 index 0000000..91e5b9f --- /dev/null +++ b/tutorials/streamlit-pydantic/optional_fields.py @@ -0,0 +1,22 @@ +from typing import Optional + +import streamlit as st +from pydantic import BaseModel, Field + +import streamlit_pydantic as sp + + +class ExampleModel(BaseModel): + some_text: str + some_number: int + some_boolean: bool + optional_boolean: bool = False + optional_text: Optional[str] + optional_number: int = Field(20) + + +data = sp.pydantic_form( + key="my_form", model=ExampleModel, group_optional_fields="expander" +) +if data: + st.json(data.json()) diff --git a/tutorials/streamlit-pydantic/overwrite_streamlit_args.py b/tutorials/streamlit-pydantic/overwrite_streamlit_args.py new file mode 100644 index 0000000..69d36f1 --- /dev/null +++ b/tutorials/streamlit-pydantic/overwrite_streamlit_args.py @@ -0,0 +1,22 @@ +import streamlit as st +from pydantic import BaseModel, Field + +from typing import Optional +import streamlit_pydantic as sp +from streamlit_pydantic.types import FileContent + + +class ExampleModel(BaseModel): + text: str = Field(..., max_length=100, st_kwargs_max_chars=500) + number: int = Field( + 10, st_kwargs_min_value=10, st_kwargs_max_value=100, st_kwargs_step=5 + ) + single_file: Optional[FileContent] = Field( + None, + st_kwargs_type=["png", "jpg"], + ) + + +data = sp.pydantic_form(key="my_form", model=ExampleModel) +if data: + st.json(data.json()) diff --git a/tutorials/streamlit-pydantic/render_input.py b/tutorials/streamlit-pydantic/render_input.py new file mode 100644 index 0000000..0d19f12 --- /dev/null +++ b/tutorials/streamlit-pydantic/render_input.py @@ -0,0 +1,14 @@ +from pydantic import BaseModel + +import streamlit_pydantic as sp + + +class ExampleModel(BaseModel): + some_text: str + some_number: int = 10 # Optional + some_boolean: bool = True # Option + + +input_data = sp.pydantic_input( + "model_input", model=ExampleModel, group_optional_fields="sidebar" +) diff --git a/tutorials/streamlit-pydantic/render_output.py b/tutorials/streamlit-pydantic/render_output.py new file mode 100644 index 0000000..141d1d5 --- /dev/null +++ b/tutorials/streamlit-pydantic/render_output.py @@ -0,0 +1,15 @@ +import datetime + +from pydantic import BaseModel, Field + +import streamlit_pydantic as sp + + +class ExampleModel(BaseModel): + text: str = Field(..., description="A text property") + integer: int = Field(..., description="An integer property.") + date: datetime.date = Field(..., description="A date.") + + +instance = ExampleModel(text="Some text", integer=40, date=datetime.date.today()) +sp.pydantic_output(instance) diff --git a/tutorials/streamlit-pydantic/simple_form.py b/tutorials/streamlit-pydantic/simple_form.py new file mode 100644 index 0000000..67f3826 --- /dev/null +++ b/tutorials/streamlit-pydantic/simple_form.py @@ -0,0 +1,29 @@ +import streamlit as st +from pydantic import BaseModel + +import streamlit_pydantic as sp + + +class ExampleModel(BaseModel): + some_text: str + some_number: int + some_boolean: bool + + +from_model_tab, from_instance_tab = st.tabs( + ["Form inputs from model", "Form inputs from instance"] +) + +with from_model_tab: + data = sp.pydantic_form(key="my_form", model=ExampleModel) + if data: + st.json(data.json()) + +with from_instance_tab: + instance = ExampleModel( + some_number=999, some_boolean=True, some_text="instance text" + ) + + instance_input_data = sp.pydantic_form(key="my_form_instance", model=instance) + if instance_input_data: + st.json(instance_input_data.json()) diff --git a/tutorials/streamlit-pydantic/union_field.py b/tutorials/streamlit-pydantic/union_field.py new file mode 100644 index 0000000..8c8c41b --- /dev/null +++ b/tutorials/streamlit-pydantic/union_field.py @@ -0,0 +1,50 @@ +from typing import Union + +import streamlit as st +from pydantic import BaseModel + +import streamlit_pydantic as sp + + +class PostalAddress(BaseModel): + street: str + city: str + house: int + + +class EmailAddress(BaseModel): + email: str + send_news: bool + + +class ContactMethod(BaseModel): + contact: Union[PostalAddress, EmailAddress] + text: str + + +from_model_tab, from_instance_tab = st.tabs( + ["Form inputs from model", "Form inputs from instance"] +) + +with from_model_tab: + input_data = sp.pydantic_input(key="union_input", model=ContactMethod) + if input_data: + st.json(input_data) + + +with from_instance_tab: + instance = ContactMethod( + contact=EmailAddress(email="instance@example.com", send_news=True), + text="instance text", + ) + + instance_input_data = sp.pydantic_input(key="union_input_instance", model=instance) + + if instance_input_data: + st.json(instance_input_data) + + +st.markdown("---") + +with st.expander("Session State", expanded=False): + st.write(st.session_state) diff --git a/tutorials/streamlit-pydantic/union_field_discriminator.py b/tutorials/streamlit-pydantic/union_field_discriminator.py new file mode 100644 index 0000000..340ce11 --- /dev/null +++ b/tutorials/streamlit-pydantic/union_field_discriminator.py @@ -0,0 +1,55 @@ +from typing import Literal, Optional, Union + +import streamlit as st +from pydantic import BaseModel, Field + +import streamlit_pydantic as sp + + +class PostalAddress(BaseModel): + contact_type: Literal["postal"] + street: str + city: str + house: int + + +class EmailAddress(BaseModel): + contact_type: Literal["email"] + email: str + send_news: bool + + +class ContactMethod(BaseModel): + contact: Optional[Union[PostalAddress, EmailAddress]] = Field( + ..., discriminator="contact_type" + ) + text: str + + +from_model_tab, from_instance_tab = st.tabs( + ["Form inputs from model", "Form inputs from instance"] +) + +with from_model_tab: + input_data = sp.pydantic_input(key="union_input", model=ContactMethod) + if input_data: + st.json(input_data) + + +with from_instance_tab: + instance = ContactMethod( + contact=EmailAddress( + contact_type="email", email="instance@example.com", send_news=True + ), + text="instance text", + ) + + instance_input_data = sp.pydantic_input(key="union_input_instance", model=instance) + + if instance_input_data: + st.json(instance_input_data) + +st.markdown("---") + +with st.expander("Session State", expanded=False): + st.write(st.session_state)