Skip to content

Commit

Permalink
Fix form constraints, take 5
Browse files Browse the repository at this point in the history
  • Loading branch information
ischaojie committed Feb 24, 2024
1 parent 4605e6d commit d74e65d
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 3 deletions.
8 changes: 7 additions & 1 deletion demo/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,10 @@ class SizeModel(BaseModel):

class BigModel(BaseModel):
name: str | None = Field(
None, description='This field is not required, it must start with a capital letter if provided'
None,
max_length=10,
min_length=2,
description='This field is not required, it must start with a capital letter if provided, and have length 2-10',
)
info: Annotated[str | None, Textarea(rows=5)] = Field(None, description='Optional free text information about you.')
profile_pic: Annotated[UploadFile, FormFile(accept='image/*', max_size=16_000)] = Field(
Expand All @@ -154,6 +157,9 @@ class BigModel(BaseModel):
human: bool | None = Field(
None, title='Is human', description='Are you human?', json_schema_extra={'mode': 'switch'}
)
number: int | None = Field(
None, title='Number', ge=0, le=10, multiple_of=2, description='This is a number should be 0-10 and step with 2'
)
size: SizeModel

position: tuple[
Expand Down
8 changes: 7 additions & 1 deletion src/npm-fastui/src/components/FormField.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ interface FormFieldInputProps extends FormFieldInput {
}

export const FormFieldInputComp: FC<FormFieldInputProps> = (props) => {
const { name, placeholder, required, htmlType, locked, autocomplete } = props
const { name, placeholder, required, htmlType, locked, autocomplete, maxLength, minLength, ge, le, multipleOf } =
props

return (
<div className={useClassName(props)}>
Expand All @@ -40,6 +41,11 @@ export const FormFieldInputComp: FC<FormFieldInputProps> = (props) => {
placeholder={placeholder}
autoComplete={autocomplete}
aria-describedby={descId(props)}
maxLength={maxLength}
minLength={minLength}
min={ge}
max={le}
step={multipleOf}
/>
<ErrorDescription {...props} />
</div>
Expand Down
7 changes: 7 additions & 0 deletions src/npm-fastui/src/models.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,13 @@ export interface FormFieldInput {
initial?: string | number
placeholder?: string
autocomplete?: string
maxLength?: number
minLength?: number
ge?: number | number
le?: number | number
gt?: number | number
lt?: number | number
multipleOf?: number | number
type: 'FormFieldInput'
}
export interface FormFieldTextarea {
Expand Down
7 changes: 7 additions & 0 deletions src/python-fastui/fastui/components/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,13 @@ class FormFieldInput(BaseFormField):
initial: _t.Union[str, float, None] = None
placeholder: _t.Union[str, None] = None
autocomplete: _t.Union[str, None] = None
max_length: _t.Union[int, None] = pydantic.Field(default=None, serialization_alias='maxLength')
min_length: _t.Union[int, None] = pydantic.Field(default=None, serialization_alias='minLength')
ge: _t.Union[int, float, None] = None
le: _t.Union[int, float, None] = None
gt: _t.Union[int, float, None] = None
lt: _t.Union[int, float, None] = None
multiple_of: _t.Union[int, float, None] = pydantic.Field(default=None, serialization_alias='multipleOf')
type: _t.Literal['FormFieldInput'] = 'FormFieldInput'


Expand Down
9 changes: 9 additions & 0 deletions src/python-fastui/fastui/json_schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ class JsonSchemaString(JsonSchemaBase):
type: _ta.Required[_t.Literal['string']]
default: str
format: _t.Literal['date', 'date-time', 'time', 'email', 'uri', 'uuid', 'password']
maxLength: int
minLength: int


class JsonSchemaStringEnum(JsonSchemaBase, total=False):
Expand Down Expand Up @@ -197,6 +199,13 @@ def json_schema_field_to_field(
initial=schema.get('default'),
autocomplete=schema.get('autocomplete'),
description=schema.get('description'),
max_length=schema.get('maxLength'),
min_length=schema.get('minLength'),
ge=schema.get('minimum'),
le=schema.get('maximum'),
gt=schema.get('exclusiveMinimum'),
lt=schema.get('exclusiveMaximum'),
multiple_of=schema.get('multipleOf'),
)


Expand Down
43 changes: 42 additions & 1 deletion src/python-fastui/tests/test_forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,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 All @@ -16,6 +16,11 @@ class SimpleForm(BaseModel):
size: int = 4


class FormWithConstraints(BaseModel):
name: str = Field(..., max_length=10, min_length=2, description='This field is required, it must have length 2-10')
size: int = Field(4, ge=0, le=10, multiple_of=2, description='size with range 0-10 and step with 2')


class FakeRequest:
"""
TODO replace this with httpx or similar maybe, perhaps this is sufficient
Expand Down Expand Up @@ -89,6 +94,42 @@ def test_inline_form_fields():
}


def test_form_with_constraints_fields():
m = components.ModelForm(model=FormWithConstraints, submit_url='/foobar/')

assert m.model_dump(by_alias=True, exclude_none=True) == {
'submitUrl': '/foobar/',
'method': 'POST',
'type': 'ModelForm',
'formFields': [
{
'name': 'name',
'title': ['Name'],
'required': True,
'locked': False,
'htmlType': 'text',
'type': 'FormFieldInput',
'description': 'This field is required, it must have length 2-10',
'maxLength': 10,
'minLength': 2,
},
{
'name': 'size',
'title': ['Size'],
'initial': 4,
'required': False,
'locked': False,
'htmlType': 'number',
'type': 'FormFieldInput',
'description': 'size with range 0-10 and step with 2',
'le': 10,
'ge': 0,
'multipleOf': 2,
},
],
}


async def test_simple_form_submit():
form_dep = fastui_form(SimpleForm)

Expand Down

0 comments on commit d74e65d

Please sign in to comment.