pythonflaskflask-restx

Validate Custom Field in Flask-Restx using @api.expect()


I want to make a custom field when making an API model and validate that it is either a string or boolean or a list

I have tried

class CustomField(fields.Raw):
    __schema_type__ = ["String", "Boolean", "List"]
    __schema_example__ = "string or boolean or list"

    def format(self, value):
        if isinstance(value, str) or isinstance(value, bool) or isinstance(value, list):
            return value
        else:
            raise fields.MarshallingError(
                "Invalid type. Allowed types: str, bool, or list."
            )

but get the following error when trying to use it.

jsonschema.exceptions.UnknownType: Unknown type 'String' for validator with schema:
    {'description': 'Value for the name type of foo',
     'example': 'string or boolean or list',
     'type': ['String', 'Boolean', 'List']}

I have also tried

class StringOrBooleanOrList(fields.Raw):
    """
    Marshal a value as a string or list.
    """

    def validate(self, value):
        if isinstance(value, str):
            return value
        elif isinstance(value, list):
            return value
        elif isinstance(value, bool):
            return value
        else:
            raise ValidationError(
                "Invalid input type. Must be a string or list or boolean"
            )

With that, strings do not work at all I get the validation error saying

string value must be valid JSON

With lists and booleans it passes through but then I get an error from the API

{
  "errors": {
    "foo.0.value": "['bar'] is not of type 'object'"
  },
  "message": "Input payload validation failed"
}

What is the correct way of achieving this in Flask restx?


Solution

  • Fixed it

    To do what I wanted I did

    class StringOrBooleanOrList(fields.Raw):
        __schema_type__ = ["string", "array", "boolean"]
        __schema_example__ = "'hello_word' or ['10.0.1.0/24'] or False"
    

    the schema_type is important. You don't put stuff in python terms but in JSON terms. 

    Hence if you try List, it won't work because JSON only has the following types

    an "array" is basically a python list so instead of list, we can use array

    And to use this custom field, it's fairly straightforward, instead of field.whatever, you use the class you defined earlier (in this case StringOrBooleanOrList)

    example_model = api.model(
        "ModelName",
        {
            "value": StringOrBooleanOrList(),
        },
    )