Home > OS >  Why doesn't FastAPI handle types derived from int and Enum correctly?
Why doesn't FastAPI handle types derived from int and Enum correctly?

Time:08-04

With the following enter image description here

I have tried the solution of using IntEnum instead (source), and I can confirm that it works, but still - why does it have to be this way?

The enum.py source code defines IntEnum as:

class IntEnum(int, Enum):
    """Enum where members are also (and must be) ints"""

CodePudding user response:

Did a little digging, based on the comments below OP. To start of with why this is not working as expected: it is how Starlette handles path parameters. When a request is handled by Starlette (or to be precise, when the Router object is called from which the APIRouter inherits), it determines the path_params as a dictionary with keys are parameter names and values as its value. However, when this happens and you haven't specified a type in the path string, it automatically treats the path parameters as string. It then adds this to the Request, basically as a dict {"a":"1", "b":"2"}. Then, further up the call stack, FastAPI is then trying to find the enum value corresponding to "1", which throws throws the validation error because "1" is not 1. Note that your MyNumber class does not play any role here yet.

This behaviour is by design and can be influenced like so:

@app.get("/add/{a:int}/{b:int}")

This will make sure that Starlette will create a path_params dictionary like {"a":1, "b":2}. Note that 1 and 2 are now integers and will be handled as such by FastAPI.

As to why MyNumber(IntEnum) works out of the box while MyNumber(int, Enum) does not, that is a Pydantic implementation. I can't really seem to pinpoint how exactly, but when the ModelField is created for both parameters (which happens in utils.py -> create_response_field() upon application startup), the list of validators is including an int validator when using IntEnum.

But because ModelField is created using functools.partial() I cannot follow the call stack into Pydantic. So, I am unsure why this behaves like this.

So, in short, there are two options to fix this: either force Starlette to parse the path paramaters a and b as int (using {a:int}) or inherit from IntEnum in your own enum class.

  • Related