Diego L
Diego L

Reputation: 918

How can I apply a validator with each_item in Pydantic 2?

I'm migrating a microservice from Pydantic 1 to Pydantic 2. Previously I had this:

class ComplementQuery(BaseModel):
    imports: List[str] = Field(default_factory=list)
    subquery: str = Field(...)

    @validator("imports", pre=True, each_item=True)
    def complement_imports(cls, value: str) -> str:
        return f'import "{value}"'

I read the documentation and saw that @field_validator must now be used. Replace the validator and change the "pre=True" to "mode="Before". But the each_item I'm not sure how it should be addressed. Now I have something like this:

class ComplementQuery(BaseModel):
    imports: List[Annotated[str, Field(default_factory=List)]]
    subquery: str = Field(...)

    @field_validator("imports", mode="before")
    @classmethod
    def complement_imports(cls, value: str) -> str:
        return f'import "{value}"'

Will the complement_imports validator be applied to each of the imports items? Or should I put the BeforeValidator of pydantic.functional_validators in the Field? I am a little confused.

Maybe it should be something like this:

def complement_imports(v: str) -> str:
    return f'import "{v}"'


class ComplementQuery(BaseModel):
    imports: List[
        Annotated[str, BeforeValidator(complement_imports), Field(default_factory=List)]
    ]
    subquery: str = Field(...)

Upvotes: 1

Views: 1099

Answers (1)

Axel Donath
Axel Donath

Reputation: 1648

I think the correct definition is the following:

from pydantic import BaseModel, Field, BeforeValidator
from typing import Annotated, List

def complement_imports(v: str) -> str:
    return f'import "{v}"'


ImportItem = Annotated[str, BeforeValidator(complement_imports)]

class ComplementQuery(BaseModel):
    imports: List[ImportItem] = Field(default_factory=list)
    subquery: str = Field(...)

print(ComplementQuery(imports=["a", "b"], subquery="c"))
print(ComplementQuery(subquery="c"))

Which prints:

imports=['import "a"', 'import "b"'] subquery='c'
imports=[] subquery='c'

By defining the ImportItem alias you simplify re-use. The default factory applies to the imports field and not to the item, as in your example.

I hope this helps!

Upvotes: 0

Related Questions