Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature Request: Support custom field validators #23

Open
alexdrydew opened this issue Nov 7, 2023 · 5 comments
Open

Feature Request: Support custom field validators #23

alexdrydew opened this issue Nov 7, 2023 · 5 comments

Comments

@alexdrydew
Copy link

alexdrydew commented Nov 7, 2023

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

@ddanier
Copy link
Member

ddanier commented Nov 11, 2023

@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. 😉

@ADR-007
Copy link

ADR-007 commented Feb 26, 2024

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

@AlmogBaku
Copy link

AlmogBaku commented May 21, 2024

@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. 😉

I guess that we should trigger field validation only if not None (and was not Optional originally)

@ddanier
Copy link
Member

ddanier commented May 22, 2024

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!

@ADR-007
Copy link

ADR-007 commented May 22, 2024

@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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants