FastUI icon indicating copy to clipboard operation
FastUI copied to clipboard

Bug with multi select fields

Open schatimo opened this issue 1 year ago • 4 comments

There is a bug with multi select fields when only one item is selected leading to an incorrect 422 error.

E.g., when selecting only one item for the "Select Multiple" select element on https://fastui-demo.onrender.com/forms/select, the POST request throws an unexpected 422 error:

{
    "detail": {
        "form": [
            {
                "type": "list_type",
                "loc": [
                    "select_multiple"
                ],
                "msg": "Input should be a valid list"
            }
        ]
    }
}

This might be related to https://github.com/tiangolo/fastapi/discussions/8741. A work-around, not elegant though, would be to add the following validation to SelectForm (https://github.com/pydantic/FastUI/blob/main/demo/forms.py):

@field_validator('select_multiple', mode='before')
@classmethod
    def correct_list_fields(cls, value: List[str] | str) -> List[str]:
        if isinstance(value, list):
            return value
        else:
            return [value]

Any thoughts on a neater solution?

schatimo avatar Dec 23 '23 16:12 schatimo

Ye I've seen that too.

Either we'll need a way to work out which fields expect lists, or you can just add a before validator to that field to convert v to [v].

samuelcolvin avatar Dec 23 '23 16:12 samuelcolvin

can we do something like this? may I create a separate pull request?

https://github.com/parthjoshi90/FastUI/commit/0a1b4642d650531d2954fcf7a0a2526efdecf0dd

parthjoshi90 avatar Jan 20 '24 05:01 parthjoshi90

Thanks, this looks neater to me as it does not require to add the before validation to every pydantic model separately.

schatimo avatar Jan 20 '24 15:01 schatimo

We could add something like the following to check whether a field is of type array

@cache
def get_array_fields(model: pydantic.BaseModel) -> dict[str, bool]:
    """Iterate through json schema and identify which fields accept an array-like type."""
    from . import json_schema

    schema = _t.cast(json_schema.JsonSchemaObject, model.model_json_schema())
    defs = schema.get('$defs', {})
    is_array_dict = {}
    for f, prop in schema['properties'].items():
        field_schema, _ = json_schema.deference_json_schema(prop, defs, required=False)
        # Get nested items (this only goes one level deep and does no recursion), can be improved
        if 'properties' in field_schema.keys():
            field_schema, _ = json_schema.deference_json_schema(field_schema['properties'], defs, required=False)
            f = list(field_schema.keys())[0]
        try:
            is_array_dict[f] = True if json_schema.schema_is_array(field_schema) else False
        except Exception:
            is_array_dict[f] = False
    return is_array_dict

and then use this function in the unflatten method in forms.py to replace https://github.com/pydantic/FastUI/blob/4605e6d0158926ae06642ecde2d31e66ef908c1b/src/python-fastui/fastui/forms.py#L197C9-L197C29

if len(values) == 1:

with

if len(values) == 1 and not get_array_fields()[last_key]:

schatimo avatar Feb 22 '24 10:02 schatimo