Ga naar inhoud

Geannoteerde validaties

??? api "API-documentatie" pydantic.functional_validators.WrapValidator
pydantic.functional_validators.PlainValidator
pydantic.functional_validators.BeforeValidator
pydantic.functional_validators.AfterValidator

Pydantic biedt een manier om validators toe te passen via het gebruik van Annotated . U moet dit gebruiken wanneer u validatie aan een type wilt koppelen in plaats van aan een model of veld.

from typing import Any, List

from typing_extensions import Annotated

from pydantic import BaseModel, ValidationError
from pydantic.functional_validators import AfterValidator


def check_squares(v: int) -> int:
    assert v**0.5 % 1 == 0, f'{v} is not a square number'
    return v


def double(v: Any) -> Any:
    return v * 2


MyNumber = Annotated[int, AfterValidator(double), AfterValidator(check_squares)]


class DemoModel(BaseModel):
    number: List[MyNumber]


print(DemoModel(number=[2, 8]))
#> number=[4, 16]
try:
    DemoModel(number=[2, 4])
except ValidationError as e:
    print(e)
    """
    1 validation error for DemoModel
    number.1
      Assertion failed, 8 is not a square number
    assert ((8 ** 0.5) % 1) == 0 [type=assertion_error, input_value=4, input_type=int]
    """

In dit voorbeeld hebben we enkele typealiassen gebruikt ( MyNumber = Annotated[...] ). Hoewel dit kan helpen bij de leesbaarheid van de code, is dit niet vereist. U kunt Annotated rechtstreeks gebruiken in een hint van het modelveldtype. Deze typealiassen zijn ook geen werkelijke typen, maar u kunt een vergelijkbare aanpak gebruiken met TypeAliasType om werkelijke typen te maken. Zie Aangepaste typen voor een meer gedetailleerde uitleg van aangepaste typen.

Het is ook vermeldenswaard dat u Annotated in andere typen kunt nesten. In dit voorbeeld hebben we dat gebruikt om validatie toe te passen op de binnenste items van een lijst. Dezelfde aanpak kan worden gebruikt voor dicteertoetsen, enz.

Voor-, Na-, Wrap- en Plain-validators

Pydantic biedt meerdere soorten validatorfuncties:

  • After validators zijn uitgevoerd na de interne parsering van Pydantic. Ze zijn over het algemeen typeveiliger en dus gemakkelijker te implementeren.
  • Before validators worden uitgevoerd vóór de interne parsering en validatie van Pydantic (bijvoorbeeld dwang van a str naar een int ). Deze zijn flexibeler dan After validators omdat ze de onbewerkte invoer kunnen wijzigen, maar ze hebben ook te maken met de onbewerkte invoer, die in theorie elk willekeurig object kan zijn.
  • Plain validators zijn als een mode='before' validator, maar ze beëindigen de validatie onmiddellijk, er worden geen verdere validators aangeroepen en Pydantic voert geen enkele interne validatie uit.
  • Wrap zijn het meest flexibel van allemaal. U kunt code uitvoeren voordat of nadat Pydantic en andere validators hun ding doen, of u kunt de validatie onmiddellijk beëindigen, zowel met een succesvolle waarde als met een fout.

U kunt meerdere validators voor, na of mode='wrap' gebruiken, maar slechts één PlainValidator aangezien een gewone validator geen interne validators zal aanroepen.

Hier is een voorbeeld van een mode='wrap' validator:

import json
from typing import Any, List

from typing_extensions import Annotated

from pydantic import (
    BaseModel,
    ValidationError,
    ValidationInfo,
    ValidatorFunctionWrapHandler,
)
from pydantic.functional_validators import WrapValidator


def maybe_strip_whitespace(
    v: Any, handler: ValidatorFunctionWrapHandler, info: ValidationInfo
) -> int:
    if info.mode == 'json':
        assert isinstance(v, str), 'In JSON mode the input must be a string!'
        # you can call the handler multiple times
        try:
            return handler(v)
        except ValidationError:
            return handler(v.strip())
    assert info.mode == 'python'
    assert isinstance(v, int), 'In Python mode the input must be an int!'
    # do no further validation
    return v


MyNumber = Annotated[int, WrapValidator(maybe_strip_whitespace)]


class DemoModel(BaseModel):
    number: List[MyNumber]


print(DemoModel(number=[2, 8]))
#> number=[2, 8]
print(DemoModel.model_validate_json(json.dumps({'number': [' 2 ', '8']})))
#> number=[2, 8]
try:
    DemoModel(number=['2'])
except ValidationError as e:
    print(e)
    """
    1 validation error for DemoModel
    number.0
      Assertion failed, In Python mode the input must be an int!
    assert False
     +  where False = isinstance('2', int) [type=assertion_error, input_value='2', input_type=str]
    """

Dezelfde "modi" zijn van toepassing op @field_validator , wat in de volgende sectie wordt besproken.

Bestellen van validators binnen Annotated

Volgorde van validatiemetagegevens binnen Annotated zaken. Validatie gaat van rechts naar links en terug. Dat wil zeggen, het gaat van rechts naar links waarbij alle "voor" validators worden uitgevoerd (of de "wrap" validators worden aangeroepen), en vervolgens van links naar rechts weer alle "na" validators worden opgeroepen.

from typing import Any, Callable, List, cast

from typing_extensions import Annotated, TypedDict

from pydantic import (
    AfterValidator,
    BaseModel,
    BeforeValidator,
    PlainValidator,
    ValidationInfo,
    ValidatorFunctionWrapHandler,
    WrapValidator,
)
from pydantic.functional_validators import field_validator


class Context(TypedDict):
    logs: List[str]


def make_validator(label: str) -> Callable[[Any, ValidationInfo], Any]:
    def validator(v: Any, info: ValidationInfo) -> Any:
        context = cast(Context, info.context)
        context['logs'].append(label)
        return v

    return validator


def make_wrap_validator(
    label: str,
) -> Callable[[Any, ValidatorFunctionWrapHandler, ValidationInfo], Any]:
    def validator(
        v: Any, handler: ValidatorFunctionWrapHandler, info: ValidationInfo
    ) -> Any:
        context = cast(Context, info.context)
        context['logs'].append(f'{label}: pre')
        result = handler(v)
        context['logs'].append(f'{label}: post')
        return result

    return validator


class A(BaseModel):
    x: Annotated[
        str,
        BeforeValidator(make_validator('before-1')),
        AfterValidator(make_validator('after-1')),
        WrapValidator(make_wrap_validator('wrap-1')),
        BeforeValidator(make_validator('before-2')),
        AfterValidator(make_validator('after-2')),
        WrapValidator(make_wrap_validator('wrap-2')),
        BeforeValidator(make_validator('before-3')),
        AfterValidator(make_validator('after-3')),
        WrapValidator(make_wrap_validator('wrap-3')),
        BeforeValidator(make_validator('before-4')),
        AfterValidator(make_validator('after-4')),
        WrapValidator(make_wrap_validator('wrap-4')),
    ]
    y: Annotated[
        str,
        BeforeValidator(make_validator('before-1')),
        AfterValidator(make_validator('after-1')),
        WrapValidator(make_wrap_validator('wrap-1')),
        BeforeValidator(make_validator('before-2')),
        AfterValidator(make_validator('after-2')),
        WrapValidator(make_wrap_validator('wrap-2')),
        PlainValidator(make_validator('plain')),
        BeforeValidator(make_validator('before-3')),
        AfterValidator(make_validator('after-3')),
        WrapValidator(make_wrap_validator('wrap-3')),
        BeforeValidator(make_validator('before-4')),
        AfterValidator(make_validator('after-4')),
        WrapValidator(make_wrap_validator('wrap-4')),
    ]

    val_x_before = field_validator('x', mode='before')(
        make_validator('val_x before')
    )
    val_x_after = field_validator('x', mode='after')(
        make_validator('val_x after')
    )
    val_y_wrap = field_validator('y', mode='wrap')(
        make_wrap_validator('val_y wrap')
    )


context = Context(logs=[])

A.model_validate({'x': 'abc', 'y': 'def'}, context=context)
print(context['logs'])
"""
[
    'val_x before',
    'wrap-4: pre',
    'before-4',
    'wrap-3: pre',
    'before-3',
    'wrap-2: pre',
    'before-2',
    'wrap-1: pre',
    'before-1',
    'after-1',
    'wrap-1: post',
    'after-2',
    'wrap-2: post',
    'after-3',
    'wrap-3: post',
    'after-4',
    'wrap-4: post',
    'val_x after',
    'val_y wrap: pre',
    'wrap-4: pre',
    'before-4',
    'wrap-3: pre',
    'before-3',
    'plain',
    'after-3',
    'wrap-3: post',
    'after-4',
    'wrap-4: post',
    'val_y wrap: post',
]
"""

Validatie van standaardwaarden

Validators worden niet uitgevoerd als de standaardwaarde wordt gebruikt. Dit geldt zowel voor @field_validator -validators als voor Annotated validators. Je kunt ze dwingen om uit te voeren met Field(validate_default=True) . Het instellen van validate_default op True komt het dichtst in de buurt van het gebruik van always=True in validator in Pydantic v1. Over het algemeen bent u echter beter af als u een @model_validator(mode='before') waarbij de functie wordt aangeroepen voordat de innerlijke validator wordt aangeroepen.

from typing_extensions import Annotated

from pydantic import BaseModel, Field, field_validator


class Model(BaseModel):
    x: str = 'abc'
    y: Annotated[str, Field(validate_default=True)] = 'xyz'

    @field_validator('x', 'y')
    @classmethod
    def double(cls, v: str) -> str:
        return v * 2


print(Model())
#> x='abc' y='xyzxyz'
print(Model(x='foo'))
#> x='foofoo' y='xyzxyz'
print(Model(x='abc'))
#> x='abcabc' y='xyzxyz'
print(Model(x='foo', y='bar'))
#> x='foofoo' y='barbar'

Veldvalidators

??? api "API-documentatie" pydantic.functional_validators.field_validator

Als u een validator aan een specifiek veld van een model wilt koppelen, kunt u de @field_validator decorateur gebruiken.

from pydantic import (
    BaseModel,
    ValidationError,
    ValidationInfo,
    field_validator,
)


class UserModel(BaseModel):
    name: str
    id: int

    @field_validator('name')
    @classmethod
    def name_must_contain_space(cls, v: str) -> str:
        if ' ' not in v:
            raise ValueError('must contain a space')
        return v.title()

    # you can select multiple fields, or use '*' to select all fields
    @field_validator('id', 'name')
    @classmethod
    def check_alphanumeric(cls, v: str, info: ValidationInfo) -> str:
        if isinstance(v, str):
            # info.field_name is the name of the field being validated
            is_alphanumeric = v.replace(' ', '').isalnum()
            assert is_alphanumeric, f'{info.field_name} must be alphanumeric'
        return v


print(UserModel(name='John Doe', id=1))
#> name='John Doe' id=1

try:
    UserModel(name='samuel', id=1)
except ValidationError as e:
    print(e)
    """
    1 validation error for UserModel
    name
      Value error, must contain a space [type=value_error, input_value='samuel', input_type=str]
    """

try:
    UserModel(name='John Doe', id='abc')
except ValidationError as e:
    print(e)
    """
    1 validation error for UserModel
    id
      Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='abc', input_type=str]
    """

try:
    UserModel(name='John Doe!', id=1)
except ValidationError as e:
    print(e)
    """
    1 validation error for UserModel
    name
      Assertion failed, name must be alphanumeric
    assert False [type=assertion_error, input_value='John Doe!', input_type=str]
    """

Een paar dingen om op te merken over validators:

  • @field_validator s zijn "klassemethoden", dus de eerste argumentwaarde die ze ontvangen is de klasse UserModel , en niet een exemplaar van UserModel . We raden u aan de @classmethod -decorator onder de @field_validator -decorator te gebruiken om de juiste typecontrole te krijgen.
  • het tweede argument is de veldwaarde die moet worden gevalideerd; het kan worden genoemd zoals u wilt
  • het derde argument, indien aanwezig, is een exemplaar van pydantic.ValidationInfo
  • validators moeten de geparseerde waarde retourneren of een ValueError of AssertionError verhogen (er kunnen assert -instructies worden gebruikt).
  • Eén enkele validator kan op meerdere velden worden toegepast door meerdere veldnamen door te geven.
  • Er kan ook één enkele validator op alle velden worden aangeroepen door de speciale waarde '*' door te geven.

!!! waarschuwing Als u gebruik maakt van assert -statements, houd er dan rekening mee dat het uitvoeren van Python met de optimalisatievlag -O assert -statements uitschakelt, en dat validators niet meer werken .

!!! opmerking FieldValidationInfo is verouderd in 2.4. Gebruik in plaats daarvan ValidationInfo .

Als u toegang wilt krijgen tot waarden uit een ander veld binnen een @field_validator , kan dit mogelijk zijn met behulp van ValidationInfo.data , wat een dictaat is van veldnaam tot veldwaarde. Validatie vindt plaats in de volgorde waarin de velden zijn gedefinieerd, dus u moet voorzichtig zijn wanneer u ValidationInfo.data gebruikt om geen toegang te krijgen tot een veld dat nog niet is gevalideerd/ingevuld. In de bovenstaande code zou u bijvoorbeeld geen toegang hebben info.data['id'] vanuit name_must_contain_space . In de meeste gevallen waarin u validatie wilt uitvoeren met behulp van meerdere veldwaarden, is het echter beter om @model_validator te gebruiken, wat in het onderstaande gedeelte wordt besproken.

Modelvalidatoren

??? api "API-documentatie" pydantic.functional_validators.model_validator

Validatie kan ook worden uitgevoerd op de gegevens van het volledige model met behulp van @model_validator .

from typing import Any

from typing_extensions import Self

from pydantic import BaseModel, ValidationError, model_validator


class UserModel(BaseModel):
    username: str
    password1: str
    password2: str

    @model_validator(mode='before')
    @classmethod
    def check_card_number_omitted(cls, data: Any) -> Any:
        if isinstance(data, dict):
            assert (
                'card_number' not in data
            ), 'card_number should not be included'
        return data

    @model_validator(mode='after')
    def check_passwords_match(self) -> Self:
        pw1 = self.password1
        pw2 = self.password2
        if pw1 is not None and pw2 is not None and pw1 != pw2:
            raise ValueError('passwords do not match')
        return self


print(UserModel(username='scolvin', password1='zxcvbn', password2='zxcvbn'))
#> username='scolvin' password1='zxcvbn' password2='zxcvbn'
try:
    UserModel(username='scolvin', password1='zxcvbn', password2='zxcvbn2')
except ValidationError as e:
    print(e)
    """
    1 validation error for UserModel
      Value error, passwords do not match [type=value_error, input_value={'username': 'scolvin', '... 'password2': 'zxcvbn2'}, input_type=dict]
    """

try:
    UserModel(
        username='scolvin',
        password1='zxcvbn',
        password2='zxcvbn',
        card_number='1234',
    )
except ValidationError as e:
    print(e)
    """
    1 validation error for UserModel
      Assertion failed, card_number should not be included
    assert 'card_number' not in {'card_number': '1234', 'password1': 'zxcvbn', 'password2': 'zxcvbn', 'username': 'scolvin'} [type=assertion_error, input_value={'username': 'scolvin', '..., 'card_number': '1234'}, input_type=dict]
    """

!!! opmerking "Bij retourneringstypecontrole" Methoden gedecoreerd met @model_validator zouden de self-instantie aan het einde van de methode moeten retourneren. Voor typecontroledoeleinden kunt u Self from typing of de typing_extensions backport gebruiken als het retourtype van de gedecoreerde methode. In de context van het bovenstaande voorbeeld kunt u ook gebruiken def check_passwords_match(self: 'UserModel') -> 'UserModel' om aan te geven dat de methode een exemplaar van het model retourneert.

!!! opmerking "Bij overerving" Een @model_validator gedefinieerd in een basisklasse zal worden aangeroepen tijdens de validatie van een subklasse-instantie.

Overriding a `@model_validator` in a subclass will override the base class' `@model_validator`, and thus only the subclass' version of said `@model_validator` will be called.

Modelvalidators kunnen mode='before' , mode='after' of mode='wrap' zijn.

Voordat modelvalidators worden doorgegeven, wordt de onbewerkte invoer ingevoerd, die vaak een dict[str, Any] is, maar ook een instantie van het model zelf kan zijn (bijvoorbeeld als UserModel.model_validate(UserModel.construct(...)) wordt genoemd) of iets anders, omdat u willekeurige objecten kunt doorgeven aan model_validate . Hierdoor zijn mode='before' validators uiterst flexibel en krachtig, maar kunnen ze omslachtig en foutgevoelig zijn om te implementeren. Voordat modelvalidators klassemethoden zouden moeten zijn. Het eerste argument moet cls zijn (en we raden u ook aan @classmethod onder @model_validator te gebruiken voor de juiste typecontrole), het tweede argument zal de invoer zijn (u moet het over het algemeen als Any typen en isinstance gebruiken om het type te beperken) en het derde argument (indien aanwezig) zal een pydantic.ValidationInfo zijn.

mode='after' validators zijn instantiemethoden en krijgen altijd een instantie van het model als eerste argument. Zorg ervoor dat u de instantie aan het einde van uw validator retourneert. U moet niet (cls, ModelType) als handtekening gebruiken, maar gewoon (self) gebruiken en typecontroleurs het type self voor u laten afleiden. Omdat deze volledig typeveilig zijn, zijn ze vaak eenvoudiger te implementeren dan mode='before' -validators. Als een veld niet kan worden gevalideerd, worden mode='after' validators voor dat veld niet aangeroepen.

Afhandelen van fouten in validators

Zoals vermeld in de vorige secties kunt u een ValueError of AssertionError genereren (inclusief de fouten die zijn gegenereerd door assert ... -instructies) binnen een validator om aan te geven dat de validatie is mislukt. U kunt ook een PydanticCustomError verhogen, die iets uitgebreider is maar u extra flexibiliteit geeft. Alle andere fouten (inclusief TypeError ) worden weergegeven en niet verpakt in een ValidationError .

from pydantic_core import PydanticCustomError

from pydantic import BaseModel, ValidationError, field_validator


class Model(BaseModel):
    x: int

    @field_validator('x')
    @classmethod
    def validate_x(cls, v: int) -> int:
        if v % 42 == 0:
            raise PydanticCustomError(
                'the_answer_error',
                '{number} is the answer!',
                {'number': v},
            )
        return v


try:
    Model(x=42 * 2)
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    x
      84 is the answer! [type=the_answer_error, input_value=84, input_type=int]
    """

Speciale typen

Pydantic biedt een paar speciale typen die kunnen worden gebruikt om de validatie aan te passen.

  • InstanceOf is een type dat kan worden gebruikt om te valideren dat een waarde een exemplaar van een bepaalde klasse is.

    from typing import List

    from pydantic import BaseModel, InstanceOf, ValidationError

    class Fruit: def repr(self): return self.class.name

    class Banana(Fruit): ...

    class Apple(Fruit): ...

    class Basket(BaseModel): fruits: List[InstanceOf[Fruit]]

    print(Basket(fruits=[Banana(), Apple()]))

    > fruits=[Banana, Apple]

    try: Basket(fruits=[Banana(), 'Apple']) except ValidationError as e: print(e) """ 1 validation error for Basket fruits.1 Input should be an instance of Fruit [type=is_instance_of, input_value='Apple', input_type=str] """

  • SkipValidation is een type dat kan worden gebruikt om de validatie van een veld over te slaan.

    from typing import List

    from pydantic import BaseModel, SkipValidation

    class Model(BaseModel): names: List[SkipValidation[str]]

    m = Model(names=['foo', 'bar']) print(m)

    > names=['foo', 'bar']

    m = Model(names=['foo', 123]) # (1)! print(m)

    > names=['foo', 123]

  • Houd er rekening mee dat de validatie van het tweede item wordt overgeslagen. Als het het verkeerde type heeft, geeft het een waarschuwing tijdens de serialisatie.

Veldcontroles

Tijdens het maken van klassen worden validators gecontroleerd om te bevestigen dat de velden die zij specificeren daadwerkelijk in het model voorkomen.

Dit kan onwenselijk zijn als u bijvoorbeeld een validator wilt definiëren om velden te valideren die alleen aanwezig zullen zijn in subklassen van het model waarin de validator is gedefinieerd.

Als u deze controles wilt uitschakelen tijdens het maken van een klasse, kunt u check_fields=False als trefwoordargument doorgeven aan de validator.

Dataclass-validators

Validators werken ook met Pydantic-dataklassen.

from pydantic import field_validator
from pydantic.dataclasses import dataclass


@dataclass
class DemoDataclass:
    product_id: str  # should be a five-digit string, may have leading zeros

    @field_validator('product_id', mode='before')
    @classmethod
    def convert_int_serial(cls, v):
        if isinstance(v, int):
            v = str(v).zfill(5)
        return v


print(DemoDataclass(product_id='01234'))
#> DemoDataclass(product_id='01234')
print(DemoDataclass(product_id=2468))
#> DemoDataclass(product_id='02468')

Validatiecontext

U kunt een contextobject doorgeven aan de validatiemethoden die toegankelijk zijn via het info -argument voor gedecoreerde validatorfuncties:

from pydantic import BaseModel, ValidationInfo, field_validator


class Model(BaseModel):
    text: str

    @field_validator('text')
    @classmethod
    def remove_stopwords(cls, v: str, info: ValidationInfo):
        context = info.context
        if context:
            stopwords = context.get('stopwords', set())
            v = ' '.join(w for w in v.split() if w.lower() not in stopwords)
        return v


data = {'text': 'This is an example document'}
print(Model.model_validate(data))  # no context
#> text='This is an example document'
print(Model.model_validate(data, context={'stopwords': ['this', 'is', 'an']}))
#> text='example document'
print(Model.model_validate(data, context={'stopwords': ['document']}))
#> text='This is an example'

Dit is handig wanneer u het validatiegedrag tijdens runtime dynamisch moet bijwerken. Als u bijvoorbeeld wilt dat een veld een dynamisch bestuurbare set toegestane waarden heeft, kunt u dit doen door de toegestane waarden per context door te geven en een apart mechanisme te hebben voor het bijwerken van wat is toegestaan:

from typing import Any, Dict, List

from pydantic import (
    BaseModel,
    ValidationError,
    ValidationInfo,
    field_validator,
)

_allowed_choices = ['a', 'b', 'c']


def set_allowed_choices(allowed_choices: List[str]) -> None:
    global _allowed_choices
    _allowed_choices = allowed_choices


def get_context() -> Dict[str, Any]:
    return {'allowed_choices': _allowed_choices}


class Model(BaseModel):
    choice: str

    @field_validator('choice')
    @classmethod
    def validate_choice(cls, v: str, info: ValidationInfo):
        allowed_choices = info.context.get('allowed_choices')
        if allowed_choices and v not in allowed_choices:
            raise ValueError(f'choice must be one of {allowed_choices}')
        return v


print(Model.model_validate({'choice': 'a'}, context=get_context()))
#> choice='a'

try:
    print(Model.model_validate({'choice': 'd'}, context=get_context()))
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Model
    choice
      Value error, choice must be one of ['a', 'b', 'c'] [type=value_error, input_value='d', input_type=str]
    """

set_allowed_choices(['b', 'c'])

try:
    print(Model.model_validate({'choice': 'a'}, context=get_context()))
except ValidationError as exc:
    print(exc)
    """
    1 validation error for Model
    choice
      Value error, choice must be one of ['b', 'c'] [type=value_error, input_value='a', input_type=str]
    """

Op dezelfde manier kunt u een context gebruiken voor serialisatie .

Validatiecontext gebruiken met BaseModel initialisatie

Hoewel er geen manier is om een context op te geven in de standaard BaseModel initializer, kunt u dit omzeilen door het gebruik van contextvars.ContextVar en een aangepaste __init__ methode:

from contextlib import contextmanager
from contextvars import ContextVar
from typing import Any, Dict, Iterator

from pydantic import BaseModel, ValidationInfo, field_validator

_init_context_var = ContextVar('_init_context_var', default=None)


@contextmanager
def init_context(value: Dict[str, Any]) -> Iterator[None]:
    token = _init_context_var.set(value)
    try:
        yield
    finally:
        _init_context_var.reset(token)


class Model(BaseModel):
    my_number: int

    def __init__(self, /, **data: Any) -> None:
        self.__pydantic_validator__.validate_python(
            data,
            self_instance=self,
            context=_init_context_var.get(),
        )

    @field_validator('my_number')
    @classmethod
    def multiply_with_context(cls, value: int, info: ValidationInfo) -> int:
        if info.context:
            multiplier = info.context.get('multiplier', 1)
            value = value * multiplier
        return value


print(Model(my_number=2))
#> my_number=2

with init_context({'multiplier': 3}):
    print(Model(my_number=2))
    #> my_number=6

print(Model(my_number=2))
#> my_number=2

Validatoren hergebruiken

Af en toe zult u dezelfde validator op meerdere velden/modellen willen gebruiken (bijvoorbeeld om bepaalde invoergegevens te normaliseren). De "naïeve" benadering zou zijn om een aparte functie te schrijven en deze vervolgens door meerdere decorateurs aan te roepen. Uiteraard brengt dit veel herhaling en boilerplate-code met zich mee. De volgende aanpak laat zien hoe je een validator kunt hergebruiken, zodat redundantie wordt geminimaliseerd en de modellen weer bijna declaratief worden.

from pydantic import BaseModel, field_validator


def normalize(name: str) -> str:
    return ' '.join((word.capitalize()) for word in name.split(' '))


class Producer(BaseModel):
    name: str

    _normalize_name = field_validator('name')(normalize)


class Consumer(BaseModel):
    name: str

    _normalize_name = field_validator('name')(normalize)


jane_doe = Producer(name='JaNe DOE')
print(repr(jane_doe))
#> Producer(name='Jane Doe')
john_doe = Consumer(name='joHN dOe')
print(repr(john_doe))
#> Consumer(name='John Doe')

本文总阅读量