Reputation: 113
I defined a custom pydantic class and a FastAPI route as below:
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
....
When I do a this request:
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'
I get the following error message:
{
"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"
}
}
]
}
I don't understand why the query parameter number_of_questions
is considered as string input besides it's an int value (10) I past as shown in curl request or FastAPI docs.
Could someone explain me what is wrong ? Thanks!
Upvotes: 3
Views: 68
Reputation: 1935
This is a known issue. One user suggested this workaround using an annotation and a BeforeValidator
to convert passed strings into ints before validation. In your case, it would look something like this:
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
This is obviously a little verbose. If Python 3.10 or later is an option, you can use the cleaner syntax for unions (as well as the 3.9+ syntax for lists) to cut it down a little:
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
Here's a small test suite; it should work for either version.
>>> 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
Upvotes: 1