使用 Pydantic 和键入文字的 FastAPI 会抛出literal_error(422 无法处理的实体)

问题描述 投票:0回答:1

我定义了一个自定义 pydantic 类和一个 FastAPI 路由,如下所示:

class QuestionParameters(BaseModel):
    test_type: Union[Literal["single_choice", "multiple_choices"], None] = None
    number_of_questions: Union[Literal[5, 10, 15], None] = None
    categories: Union[List[str], None] = None

@app.post("/generate_quiz")
def generate_quiz(qcm_params: Annotated[QuestionParameters, Query()], user: str = Depends(verify_credentials)):
    """Génère un QCM basé sur les paramètres fournis.
    
    PARAMS:
    -------
    - test_type: str
        Le type de test souhaité. Par exemple "multiple_choices"
    - categories: List[str]
        Une liste des catégories de questions souhaitées.
    - number_of_questions: int
        Le nombre de question à inclure dans le QCM

    RETURN:
    -------
    List[Question]
    
    """
    test_type = qcm_params.test_type
    number_of_questions = qcm_params.number_of_questions
    categories = qcm_params.categories
    ....

当我提出此请求时:

curl -X 'POST' \
  'http://localhost:8000/generate_quiz?test_type=single_choice&number_of_questions=10&categories=Automation&categories=Data%20Science' \
  -H 'accept: application/json'

我收到以下错误消息:

{
  "detail": [
    {
      "type": "literal_error",
      "loc": [
        "query",
        "number_of_questions"
      ],
      "msg": "Input should be 5, 10 or 15",
      "input": "10",
      "ctx": {
        "expected": "5, 10 or 15"
      }
    }
  ]
}

我不明白为什么查询参数

number_of_questions
被视为字符串输入,除了它是一个 int 值(10)之外,我过去如curl请求或FastAPI文档中所示。 enter image description here

有人可以解释一下我出了什么问题吗? 谢谢!

fastapi python-typing pydantic
1个回答
0
投票

这是一个已知问题。一位用户建议使用注释和 BeforeValidator

 
此解决方法 在验证之前将传递的字符串转换为整数。在你的情况下,它看起来像这样:

class QuestionParameters(BaseModel):
    test_type: Union[Literal["single_choice", "multiple_choices"], None] = None
    number_of_questions: Union[Annotated[Literal[5, 10, 15], BeforeValidator(int)], None] = None
    categories: Union[List[str], None] = None

这显然有点冗长。如果可以选择 Python 3.10 或更高版本,您可以使用 unions 的更清晰语法(以及 lists 的 3.9+ 语法)来减少一点:

class QuestionParameters(BaseModel):
    test_type: Literal["single_choice", "multiple_choices"] | None = None
    number_of_questions: Annotated[Literal[5, 10, 15], BeforeValidator(int)] | None = None
    categories: list[str] | None = None

这是一个小型测试套件;它应该适用于任一版本。

>>> QuestionParameters(number_of_questions = None) # None
QuestionParameters(test_type=None, number_of_questions=None, categories=None)
>>> 
>>> QuestionParameters(number_of_questions = "5") # Acceptable "integer"
QuestionParameters(test_type=None, number_of_questions=5, categories=None)
>>> 
>>> QuestionParameters(number_of_questions = "0") # Unacceptable "integer"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/packages/pydantic/main.py", line 209, in __init__
    validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.ValidationError: 1 validation error for QuestionParameters
number_of_questions
  Input should be 5, 10 or 15 [type=literal_error, input_value=0, input_type=int]
    For further information visit https://errors.pydantic.dev/2.9/v/literal_error
>>> 
>>> QuestionParameters(number_of_questions = "Hello World!") # Non-numeric string
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/packages/pydantic/main.py", line 209, in __init__
    validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.ValidationError: 1 validation error for QuestionParameters
number_of_questions
  Value error, invalid literal for int() with base 10: 'Hello World!' [type=value_error, input_value='Hello World!', input_type=str]
    For further information visit https://errors.pydantic.dev/2.9/v/value_error
>>> 
>>> QuestionParameters(number_of_questions = "5.0") # "Float"
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/path/to/packages/pydantic/main.py", line 209, in __init__
    validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
pydantic_core._pydantic_core.ValidationError: 1 validation error for QuestionParameters
number_of_questions
  Value error, invalid literal for int() with base 10: '5.0' [type=value_error, input_value='5.0', input_type=str]
    For further information visit https://errors.pydantic.dev/2.9/v/v
© www.soinside.com 2019 - 2024. All rights reserved.