Previously I used the marshmallow library with the Flask. Some time ago I have tried FastAPI with Pydantic. At first glance pydantic
seems similar to masrhmallow
but on closer inspection they differ. And for me the main difference between them is post_load methods which are from marshmallow
. I can't find any analogs for it in pydantic
.
post_load
is decorator for post-processing methods. Using it I can handle return object on my own, can do whatever I want:
class ProductSchema(Schema):
alias = fields.Str()
category = fields.Str()
brand = fields.Str()
@post_load
def check_alias(self, params, **kwargs):
"""One of the fields must be filled"""
if not any([params.get('alias'), params.get('category'), params.get('brand')]):
raise ValidationError('No alias provided', field='alias')
return params
Besides it used not only for validation. Code example is just for visual understanding, do not analyze it, I have just invented it.
So my question is:
is there any analog for post_load
in pydantic
?
It is not obvious but pydantic's
validator returns value of the field.
There are two ways to handle post_load
conversions: validator
and
root_validator
.
validator
gets the field value as argument and returns its value.
root_validator
is the same but manipulates with the whole object.
from pydantic import validator, root_validator
class PaymentStatusSchema(BaseModel):
order_id: str = Param(..., title="Order id in the shop")
order_number: str = Param(..., title="Order number in chronological order")
status: int = Param(..., title="Payment status")
@validator("status")
def convert_status(cls, status):
return "active" if status == 1 else "inactive"
@root_validator
def validate_order_id(cls, values):
"""Check order id"""
if not values.get('orderNumber') and not values.get('mdOrder'):
raise HTTPException(status_code=400, detail='No order data provided')
return values
By default pydantic
runs validators as post-processing methods. For pre-processing you should use validators with pre
argument:
@root_validator(pre=True)
def validate_order_id(cls, values):
"""Check order id"""
# some code here
return values
In Pydantic v2 validators migrated: validator
to field_validator
and root_validator
to model_validator
.
@field_validator('status')
@classmethod
def convert_status(cls, status: int):
return "active" if status == 1 else "inactive"
@model_validator(mode='after')
@classmethod
def validate_order_id(cls, values):
"""Check order id"""
if not values.get('orderNumber') and not values.get('mdOrder'):
raise HTTPException(status_code=400, detail='No order data provided')
return values
For post or pre-processing use mode
parameter.