Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

automatically add fields_validators based on hint type with pydantic

I’d like to define once for all fields_validators function in a BaseModel class, and inherit this class in my model, and the validators should apply to the updated class attributes.

MWE

def to_int(v: Union[str, int]) -> int:
    if isinstance(v, str):
        if v.startswith("0x"):
            return int(v, 16)
        return int(v)
    return v


def to_bytes(v: Union[str, bytes, list[int]]) -> bytes:
    if isinstance(v, bytes):
        return v
    elif isinstance(v, str):
        if v.startswith("0x"):
            return bytes.fromhex(v[2:])
        return v.encode()
    else:
        return bytes(v)


class BaseModelCamelCase(BaseModel):
    model_config = ConfigDict(
        populate_by_name=True,
        alias_generator=AliasGenerator(
            validation_alias=lambda name: AliasChoices(to_camel(name), name)
        ),
    )

    # FIXME: should apply to int type from get_type_hints only
    @field_validator("*", mode="before")
    def to_int(cls, v: Union[str, int]) -> int:
        return to_int(v)

    # FIXME: should apply to bytes type from get_type_hints only
    @field_validator("*", mode="before")
    def to_bytes(cls, v: Union[str, bytes, list[int]]) -> bytes:
        return to_bytes(v)

class BaseTransactionModel(BaseModelCamelCase):
    nonce: int
    gas: int = Field(validation_alias=AliasChoices("gasLimit", "gas_limit", "gas"))
    to: Optional[bytes]
    value: int
    data: bytes
    r: int = 0
    s: int = 0

I tried to use model_validate but then I lost the alias parsing

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

>Solution :

To automatically add field validators based on type hints in Pydantic, you can use the Annotated type from typing_extensions along with Pydantic’s validator functions. This allows you to bind validation logic directly to a type, making your code more modular and reusable.

Here’s an example of how you can achieve this:

from typing import Any, List
from typing_extensions import Annotated
from pydantic import BaseModel, ValidationError
from pydantic.functional_validators import AfterValidator

# Define a validator function
def check_positive(value: int) -> int:
    if value <= 0:
        raise ValueError(f'{value} is not a positive number')
    return value

# Create an annotated type with the validator
PositiveInt = Annotated[int, AfterValidator(check_positive)]

# Use the annotated type in a Pydantic model
class MyModel(BaseModel):
    positive_number: PositiveInt

# Example usage
try:
    model = MyModel(positive_number=10)
    print(model)
except ValidationError as e:
    print(e)

try:
    model = MyModel(positive_number=-5)
except ValidationError as e:
    print(e)

Explanation:
Validator Function: check_positive ensures the value is a positive integer.
Annotated Type: PositiveInt combines the int type with the check_positive validator using Annotated.
Pydantic Model: MyModel uses PositiveInt for the positive_number field, automatically applying the validator.
Benefits:
Modularity: Validators are tied to types, making them reusable across different models.
Clarity: The validation logic is separated from the model definition, improving readability.

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading