Skip to content

Commit

Permalink
Fix description for array-like fields (#184)
Browse files Browse the repository at this point in the history
  • Loading branch information
sergue1 authored Apr 24, 2024
1 parent 1f944be commit a19d7bf
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 3 deletions.
4 changes: 2 additions & 2 deletions src/python-fastui/fastui/json_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def json_schema_array_to_fields(
items_schema = schema.get('items')
if items_schema:
items_schema, required = deference_json_schema(items_schema, defs, required)
for field_name in 'search_url', 'placeholder':
for field_name in 'search_url', 'placeholder', 'description':
if value := schema.get(field_name):
items_schema[field_name] = value # type: ignore
if field := special_string_field(items_schema, loc_to_name(loc), title, required, True):
Expand Down Expand Up @@ -312,7 +312,7 @@ def deference_json_schema(
if def_schema is None:
raise ValueError(f'Invalid $ref "{ref}", not found in {defs}')
else:
return def_schema, required
return def_schema.copy(), required # clone dict to avoid attribute leakage via shared schema.
elif any_of := schema.get('anyOf'):
if len(any_of) == 2 and sum(s.get('type') == 'null' for s in any_of) == 1:
# If anyOf is a single type and null, then it is optional
Expand Down
55 changes: 54 additions & 1 deletion src/python-fastui/tests/test_forms.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import enum
from contextlib import asynccontextmanager
from io import BytesIO
from typing import List, Tuple, Union
Expand All @@ -6,7 +7,7 @@
from fastapi import HTTPException
from fastui import components
from fastui.forms import FormFile, Textarea, fastui_form
from pydantic import BaseModel
from pydantic import BaseModel, Field
from starlette.datastructures import FormData, Headers, UploadFile
from typing_extensions import Annotated

Expand Down Expand Up @@ -469,3 +470,55 @@ def test_form_textarea_form_fields():
}
],
}


class SelectEnum(str, enum.Enum):
one = 'one'
two = 'two'


class FormSelectMultiple(BaseModel):
select_single: SelectEnum = Field(title='Select Single', description='first field')
select_single_2: SelectEnum = Field(title='Select Single') # unset description to test leakage from prev. field
select_multiple: List[SelectEnum] = Field(title='Select Multiple', description='third field')


def test_form_description_leakage():
m = components.ModelForm(model=FormSelectMultiple, submit_url='/foobar/')

assert m.model_dump(by_alias=True, exclude_none=True) == {
'formFields': [
{
'description': 'first field',
'locked': False,
'multiple': False,
'name': 'select_single',
'options': [{'label': 'One', 'value': 'one'}, {'label': 'Two', 'value': 'two'}],
'required': True,
'title': ['Select Single'],
'type': 'FormFieldSelect',
},
{
'locked': False,
'multiple': False,
'name': 'select_single_2',
'options': [{'label': 'One', 'value': 'one'}, {'label': 'Two', 'value': 'two'}],
'required': True,
'title': ['Select Single'],
'type': 'FormFieldSelect',
},
{
'description': 'third field',
'locked': False,
'multiple': True,
'name': 'select_multiple',
'options': [{'label': 'One', 'value': 'one'}, {'label': 'Two', 'value': 'two'}],
'required': True,
'title': ['Select Multiple'],
'type': 'FormFieldSelect',
},
],
'method': 'POST',
'submitUrl': '/foobar/',
'type': 'ModelForm',
}

0 comments on commit a19d7bf

Please sign in to comment.