This is a workaround, but I could write a custom init and raise RequestValidationError but isn't it ugly?
import uvicorn
from fastapi import FastAPI, Depends
from fastapi.exceptions import RequestValidationError
from pydantic import BaseModel
from pydantic.error_wrappers import ErrorWrapper
app = FastAPI(docs_url=f"/docs")
class MyClass(BaseModel):
my_string_field: str
def __init__(self, **kwargs):
super().__init__(**kwargs)
if self.my_string_field != 'good':
raise RequestValidationError(errors=[ErrorWrapper(ValueError(f'Error expected "good"'), loc=('body', 'my_string_field'))])
@app.post("/myendpoint")
def scope(variable: MyClass = Depends()):
return 'ok'
uvicorn.run(app, host="0.0.0.0", port=8000)
A different approach is creating a subclass (extending str in this case) and implementing custom validators similarly as in How to include non-pydantic classes in fastapi responses? and https://github.com/tiangolo/sqlmodel/issues/235#issuecomment-1162063590
import uvicorn
from pydantic import BaseModel
from fastapi import FastAPI, Depends
from typing import Type, Iterable, Callable, Any
app = FastAPI(docs_url=f"/docs")
class CustomString(str):
@classmethod
def __get_validators__(cls: Type["CustomString"]) -> Iterable[Callable[..., Any]]:
yield cls.validate
@classmethod
def validate(cls, v: str) -> Any:
if v != "good":
raise ValueError(f"Expected good, received: {v}")
return v
class MyClass(BaseModel):
my_string_field: CustomString
@app.post("/myendpoint")
def scope(variable: MyClass = Depends()):
return 'ok'
uvicorn.run(app, host="0.0.0.0", port=8000)
This works, 422 is generated "automatically".
But... how would I combine validation for several fields like this? It was possible in a root_validator. Example use case is to have an UploadFile and some_param in MyModel(BaseModel) and then try to check if the uploaded file name contains some_param.