I have a function like this:
class Name(BaseModel):
name_id: str
first_name: str
last_name: str
def get_all_names() -> List[Name]:
names = []
try:
# this API returns a list of NAME objects
names = requests.get("some-url")
# I want to validate, that each NAME object comforms to the model "Name" above
# this is what I do currently
validate_name_objects = [Name(**each_name_object) for each_name_object in names]
except Exception as e:
# if any of the NAME object fails the validation check above, then it will automatically
# be caught in this exception block and logged
logger.info(f"log this error (could be requests error, could be validation error: {e}")
return names
FastAPI does this validation check automatically, it somehow takes in type hint of response from function signature which in this case will be List[Name]
and then automatically raise exception if the response does not conform to it.
I have these kind of checks in a lot of places in my code with different custom Pydantic models.
So I am looking for a mechanism of this sort where:
# Here SOME_FUNCTION takes in 2 arguments: A custom model to compare(which could be in any custom form
# made from Pydantic models like List[some-model] or Dict[str, some-model], etc)
# and a payload to validate that model against
validate_name_objects = SOME_FUNCTION(List[Name], names)
How to achieve this?
The closest content I found to my problem is provided by Pydantic here - https://pydantic-docs.helpmanual.io/usage/validation_decorator/ but this is only for validating input arguments of a given function and does not accept custom models dynamically.
Update: After answer by @MatsLindh, here are more flexible ways by which we can use the solution he gave: (but remember, as pointed out, Pydantic is a parsing library -> not a Validation library)
We can use it with just native python data types
from typing import Dict
from pydantic import parse_obj_as
items = parse_obj_as(Dict[int, str], {"asdasd": "23232"})
print(items)
This will as expected give Validation error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pydantic/tools.py", line 35, in pydantic.tools.parse_obj_as
File "pydantic/main.py", line 406, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for ParsingModel[Dict]
__root__ -> __key__
value is not a valid integer (type=type_error.integer)
We can also use the same function, for custom data models:
from typing import List, Dict
from pydantic import BaseModel, parse_obj_as
class Item(BaseModel):
id: int
name: str
items = parse_obj_as(Dict[int, Item], {1: "asdfasdasf"})
print(items)
This will as expected give Validation error:
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "pydantic/tools.py", line 35, in pydantic.tools.parse_obj_as
File "pydantic/main.py", line 406, in pydantic.main.BaseModel.__init__
pydantic.error_wrappers.ValidationError: 1 validation error for ParsingModel[Dict]
__root__ -> 1
value is not a valid dict (type=type_error.dict)
CodePudding user response:
You can use parse_obj_as
to convert a list of dictionaries to a list of given Pydantic models, effectively doing the same as FastAPI would do when returning the response.
from pydantic import parse_obj_as
...
name_objects = parse_obj_as(List[Name], names)
However, it's important to consider that Pydantic is a parser library, not a validation library - so it will do conversions if your models allow for them.