python-3.xfastapipydantic-v2

Why can't my FastAPI Router parse Pydantic's Enum type when I use Union params?


My Question

Here is the definition of my three Pydantic Model:

class SplitType(str, enum.Enum):
    AUTO = "auto"
    CUSTOM = "custom"


class DocumentConfigBase(BaseModel):
    '''
    document config detail
    '''
    model_config = ConfigDict(use_enum_values=True)
    mode: Optional[SplitType] = Field(
        default=SplitType.AUTO, validate_default=True)
    chunk_size: Optional[str] = "800"
    identifier: Optional[str]
    text_preprocessing_rules: Dict[str, Any]


class DocumentConfigCreate(BaseModel):
    '''
    document config create
    '''
    knowledge_id: int
    config: DocumentConfigBase


class SheetConfigCreate(BaseModel):
    '''
    need to update
    '''
    knowledge_id: int
    pass


class ImageConfigCreate(BaseModel):
    '''
    need to update
    '''
    knowledge_id: int
    pass

In the first model, I defined the mode field as an enum type. Then I wrote the Router code.

@router.post("/config", response_model=KnowledgeConfigModel)
def create_knowledge_config(user_id: Annotated[int, Body()], config_type: Annotated[KnowledgeType, Body()], knowledge_config: Union[DocumentConfigCreate, SheetConfigCreate, ImageConfigCreate], session: Session = Depends(get_db)):

    print('knowledge_config', knowledge_config)

    try:
        knowledge_model = KnowledgeConfigService.create(
            operator=user_id, config_type=config_type,
            knowledge_config=knowledge_config, session=session)
        print('knowledge_model', knowledge_model)
        if knowledge_model is None:
            raise HTTPException(
                status_code=404, detail="Config create failed")
        return knowledge_model
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e)) from e

I used the Union type for the knowledge_config field, but when I request this API with the parameters below, I get an error: "detail": "'config'".

{
  "user_id": 0,
  "config_type": "document",
  "knowledge_config": {
    "knowledge_id": 1,
    "config": {
      "mode": "auto",
      "chunk_size": "1000",
      "identifier": "string",
      "text_preprocessing_rules": {}
    }
  }
}

And The print statement(print('knowledge_config', knowledge_config)) in the code outputs knowledge_config knowledge_id=1. It seems to have lost the rest of the parameters.

My attempts:

  1. When I don't pass the mode parameter, there is no error. I get all the parameters I passed, and mode field is automatically set to "auto".
  2. When I stop using the Union type and just use knowledge_config: DocumentConfigCreate, there is no error. I get all the parameters I passed, and mode field is automatically set to "auto" as well.

Therefore, I believe there is no error in my parameters; the issue likely lies in my use of the Union type, causing FastAPI to fail in parsing.

I'm new to Python, please help me fix this issue, Thanks a lot!


Solution

  • When you use Union to define input parameters, FastAPI should choose the right type at the validation stage. I don't know the exact algorithm, but you can think of it as FastAPI will try using these models in unknown order and FastAPI will choose first model that passes the validation as a type for this parameter.

    To solve this you can use discriminated unions. This way you give to FastAPI the criteria to choose the right model.

    In some cases you can solve this by adding model_config = ConfigDict(extra="forbid") to all your models. This will force the model validation to fail if there are unknown fields. So, SheetConfigCreate will not pass the validation when there is a field config in the input JSON.

    The following approach also helps. Define new type: KnowledgeConfig: TypeAlias = Annotated[Union[DocumentConfigCreate, SheetConfigCreate, ImageConfigCreate], Field(union_mode='left_to_right')] and use it instead of Union[DocumentConfigCreate, ...]