I have a model for a search endpoint that looks like this:
class ArtistInboundSearchModel(BaseModel):
ids: Optional[str] = Query(default=None)
name: Optional[str] = Query(default=None)
name_like: Optional[str] = Query(default=None)
page: Optional[int] = Query(default=None)
page_length: Optional[int] = Query(default=None)
As expected, if I post to an endpoint taking this model with "page_length" equal to "dog" AND "page" equal to "cat", I get a 422 response as Pydantic intercepts the request and validates it. This 422 response has 2 errors in it, one for each failed validation, as expected:
Response to "/artists?page=cat&page_length=dog":
{
"detail": [
{
"type": "int_parsing",
"loc": [
"query",
"page"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "cat"
},
{
"type": "int_parsing",
"loc": [
"query",
"page_length"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "dog"
}
]
}
GREAT. Now let's talk about the query arg "ids". The purpose of this query arg is to allow the requestor to supply a comma-delimited list of ids and restrict the search to those ids. We will need to validate the ids input and make sure each id is a valid guid, otherwise, when we tokenize the string and try to convert them to ids, we'll get an exception.
The only real documentation I found around this suggested doing the following to implement a property validator (validate_comma_delimited_ids and generate_invalid_comma_delimited_ids_message are mine; they are unit tested and work as expected):
@field_validator('ids')
def ids_valid(cls, value: str):
results = validate_comma_delimited_ids(value)
if(results is not None):
message = generate_invalid_comma_delimited_ids_message("ids", results)
raise ValueError(message)
return value
This performs the proper validation, and if it passes, returns an unchanged value for me to tokenize and pass along to BL. If the validation fails, though, instead of adding it to the list of failures, it raises the ValueError and i get a 500 response for unhandled errors.
I looked around for a long time trying to find some information, maybe another parameter that is the rolling list of errors, or something like that and I just cannot.
The desired behavior, and the behavior I would expect from any other modern validation framework or my own validation framework, would be that I could have my validation block add a validation error to a running list so that it worked something like:
Response to "/artists?page=cat&page_length=dog?ids=notanid,ohboy"
{
"detail": [
{
"type": "int_parsing",
"loc": [
"query",
"page"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "cat"
},
{
"type": "int_parsing",
"loc": [
"query",
"page_length"
],
"msg": "Input should be a valid integer, unable to parse string as an integer",
"input": "dog"
},
{
"type": "custom_parsing",
"loc": [
"query",
"ids"
],
"msg": "Property should be a comma-delimited list of valid uuidv4 values.",
"input": "notanid,ohboy"
}
]
}
Or really whatever I want. How do I access the rolling list of validation errors in my validation block so I can push failures to it and integrate with Pydantics usual operations?
I want a solution that makes it easy to define custom validation right in the model alongside the other validation definitions, as this is what other frameworks do and also is the preferred developer experience for my devs. I believe that pydantic isn't bad, per se, so I think there must be some way to do this, otherwise it is really not a useful tool for building apis.