pydantic-partial
pydantic-partial copied to clipboard
Feature Request: Support custom field validators
Hi! First of all, thank you for your work, the library provides a really handy way to reduce boilerplate for partial models.
Unfortunately, I faced a blocker for using this library in my case: currently pydantic partial discards metadata from FieldInfo which contains custom validation logic. It would be great if the pydantic-partial library introduced a way to call custom field validators if the value is present.
Here is an example of a possible usage case:
from datetime import timedelta
from typing import Annotated, Any
from pydantic import BaseModel, PlainSerializer, WrapValidator
from pydantic.functional_validators import ModelWrapValidatorHandler
from pydantic_partial import create_partial_model
def timedelta_validator(value: Any, handler: ModelWrapValidatorHandler[timedelta]) -> timedelta:
if isinstance(value, (timedelta, str)):
return handler(value)
elif isinstance(value, (float, int)):
return timedelta(seconds=value)
else:
raise ValueError
TimedeltaSeconds = Annotated[timedelta, PlainSerializer(lambda td: td.total_seconds()), WrapValidator(timedelta_validator)]
class MyModel(BaseModel):
t: TimedeltaSeconds
MyModelPartial = create_partial_model(MyModel)
assert MyModel(t=timedelta(seconds=10)).model_dump()['t'] == 10. # ok
assert MyModelPartial(t=timedelta(seconds=10)).model_dump()['t'] == 10. # error
Thank you!
Version info
pydantic-partial==0.5.2
pydantic version: 2.4.2 pydantic-core version: 2.10.1 pydantic-core build: profile=release pgo=false python version: 3.11.4 (main, Aug 28 2023, 23:10:28) [Clang 14.0.3 (clang-1403.0.22.14.1)] platform: macOS-14.0-arm64-arm-64bit related packages: mypy-1.6.0 typing_extensions-4.8.0 pydantic-settings-2.0.3 fastapi-0.101.1
@alexdrydew Sorry, I had no time to look into this - but I will do that. I don't think it's that simple...
Currently we want to skip validators, because those might just break when the type changes due to what pydantic-partial does. Your code for example should also accept None in the partial model version (like MyModelPartial(t=None)), which I think (haven't tested this yet) the validator would prevent by triggering a ValueError. So this might (!) introduce strange side effects. 😉
Hi @ddanier, thank you for your answer.
Skipping validators makes the partial models a bit useless in my case :( So, I added PR with this feature. Could you please take a look? PR
@alexdrydew Sorry, I had no time to look into this - but I will do that. I don't think it's that simple...
Currently we want to skip validators, because those might just break when the type changes due to what
pydantic-partialdoes. Your code for example should also acceptNonein the partial model version (likeMyModelPartial(t=None)), which I think (haven't tested this yet) the validator would prevent by triggering aValueError. So this might (!) introduce strange side effects. 😉
I guess that we should trigger field validation only if not None (and was not Optional originally)
I guess that we should trigger field validation only if not None (and was not Optional originally)
@AlmogBaku I hope you understand that this sounds like a very bad developer experience. Sometimes the validators are executed, sometimes not. You might have a validator das explicitly wants to handle None values for example. This would then break.
I didn't come up with a good solution so far, sorry!
@AlmogBaku if you want to have the validation always be enabled, you may want to try my version of this library that I did for that case: pydantic-strict-partial.