Validateurs annotés¶
??? API "Documentation API" [pydantic.functional_validators.WrapValidator
][pydantic.function_validators.WrapValidator]
[pydantic.functional_validators.PlainValidator
][pydantic.function_validators.PlainValidator]
[pydantic.functional_validators.BeforeValidator
][pydantic.function_validators.BeforeValidator]
[pydantic.functional_validators.AfterValidator
][pydantic.function_validators.AfterValidator]
Pydantic fournit un moyen d'appliquer des validateurs via l'utilisation d' Annotated
. Vous devez l'utiliser chaque fois que vous souhaitez lier la validation à un type plutôt qu'à un modèle ou un champ.
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]
"""
Dans cet exemple, nous avons utilisé des alias de type ( MyNumber = Annotated[...]
). Bien que cela puisse aider à la lisibilité du code, ce n'est pas obligatoire, vous pouvez utiliser Annotated
directement dans un indice de type de champ de modèle. Ces alias de type ne sont pas non plus des types réels, mais vous pouvez utiliser une approche similaire avec TypeAliasType
pour créer des types réels. Voir Types personnalisés pour une explication plus détaillée des types personnalisés.
Il convient également de noter que vous pouvez imbriquer Annotated
dans d’autres types. Dans cet exemple, nous l'avons utilisé pour appliquer la validation aux éléments internes d'une liste. La même approche peut être utilisée pour les clés de dict, etc.
Validateurs Avant, Après, Wrap et Plain¶
Pydantic fournit plusieurs types de fonctions de validation:
After
les validateurs s'exécutent après l'analyse interne de Pydantic. Ils sont généralement plus sûrs et donc plus faciles à mettre en œuvre.Before
les validateurs ne s'exécutent avant l'analyse et la validation internes de Pydantic (par exemple, la coercition d'unstr
vers unint
). Ceux-ci sont plus flexibles que les validateursAfter
puisqu'ils peuvent modifier l'entrée brute, mais ils doivent également gérer l'entrée brute, qui en théorie pourrait être n'importe quel objet arbitraire.- Les validateurs
Plain
sont comme un validateurmode='before'
mais ils terminent la validation immédiatement, aucun autre validateur n'est appelé et Pydantic n'effectue aucune de ses validations internes. - Les validateurs
Wrap
sont les plus flexibles de tous. Vous pouvez exécuter du code avant ou après que Pydantic et d'autres validateurs aient fait leur travail ou vous pouvez mettre fin à la validation immédiatement, à la fois avec une valeur réussie ou une erreur.
Vous pouvez utiliser plusieurs validateurs avant, après ou mode='wrap'
, mais un seul PlainValidator
puisqu'un validateur simple n'appellera aucun validateur interne.
Voici un exemple de validateur mode='wrap'
:
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]
"""
Les mêmes «modes» s’appliquent à @field_validator
, qui est abordé dans la section suivante.
Ordre des validateurs dans Annotated
¶
Ordre des métadonnées de validation dans les sujets Annotated
. La validation se fait de droite à gauche et inversement. Autrement dit, il va de droite à gauche en exécutant tous les validateurs "avant" (ou en appelant les validateurs "wrap"), puis de gauche à droite en appelant tous les validateurs "après".
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',
]
"""
Validation des valeurs par défaut¶
Les validateurs ne s'exécuteront pas lorsque la valeur par défaut est utilisée. Cela s'applique à la fois aux validateurs @field_validator
et aux validateurs Annotated
. Vous pouvez les forcer à s'exécuter avec Field(validate_default=True)
. Définir validate_default
sur True
a le comportement le plus proche de l'utilisation always=True
dans validator
dans Pydantic v1. Cependant, il est généralement préférable d'utiliser un @model_validator(mode='before')
où la fonction est appelée avant l'appel du validateur interne.
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'
Validateurs de terrain¶
??? API "Documentation API" [pydantic.functional_validators.field_validator
][pydantic.function_validators.field_validator]
Si vous souhaitez attacher un validateur à un champ spécifique d'un modèle, vous pouvez utiliser le décorateur @field_validator
.
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]
"""
Quelques points à noter sur les validateurs:
- Les
@field_validator
sont des "méthodes de classe", donc la première valeur d'argument qu'ils reçoivent est la classeUserModel
, pas une instance deUserModel
. Nous vous recommandons d'utiliser le décorateur@classmethod
sous le décorateur@field_validator
pour obtenir une vérification de type appropriée. - le deuxième argument est la valeur du champ à valider ; il peut être nommé comme bon vous semble
- le troisième argument, s'il est présent, est une instance de
pydantic.ValidationInfo
- les validateurs doivent soit renvoyer la valeur analysée, soit déclencher une
ValueError
ouAssertionError
(des instructionsassert
peuvent être utilisées). - Un seul validateur peut être appliqué à plusieurs champs en lui transmettant plusieurs noms de champs.
- Un seul validateur peut également être appelé sur tous les champs en passant la valeur spéciale
'*'
.
!!! avertissement Si vous utilisez des instructions assert
, gardez à l'esprit que l'exécution de Python avec l' indicateur d'optimisation -O
désactive les instructions assert
et les validateurs cesseront de fonctionner .
!!! Remarque FieldValidationInfo
est obsolète dans la version 2.4, utilisez plutôt ValidationInfo
.
Si vous souhaitez accéder aux valeurs d'un autre champ à l'intérieur d'un @field_validator
, cela peut être possible en utilisant ValidationInfo.data
, qui est un dict du nom du champ à la valeur du champ. La validation se fait dans la commande les champs sont définis, vous devez donc faire attention lorsque vous utilisez ValidationInfo.data
à ne pas accéder à un champ qui n'a pas encore été validé/rempli — dans le code ci-dessus, par exemple, vous ne pourrez pas accéder info.data['id']
depuis name_must_contain_space
. Cependant, dans la plupart des cas où vous souhaitez effectuer une validation en utilisant plusieurs valeurs de champ, il est préférable d'utiliser @model_validator
qui est abordé dans la section ci-dessous.
Validateurs de modèles¶
??? API "Documentation API" [pydantic.functional_validators.model_validator
][pydantic.function_validators.model_validator]
La validation peut également être effectuée sur l'ensemble des données du modèle à l'aide de @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]
"""
!!! note "Lors de la vérification du type de retour" Les méthodes décorées avec @model_validator
doivent renvoyer l'instance self à la fin de la méthode. À des fins de vérification de type, vous pouvez utiliser Self
à partir de typing
ou du backport typing_extensions
comme type de retour de la méthode décorée. Dans le contexte de l'exemple ci-dessus, vous pouvez également utiliser def check_passwords_match(self: 'UserModel') -> 'UserModel'
pour indiquer que la méthode renvoie une instance du modèle.
!!! note "Sur l'héritage" Un @model_validator
défini dans une classe de base sera appelé lors de la validation d'une instance de sous-classe.
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.
Les validateurs de modèles peuvent être mode='before'
, mode='after'
ou mode='wrap'
.
Avant que les validateurs de modèles ne reçoivent l'entrée brute qui est souvent un dict[str, Any]
mais peut également être une instance du modèle lui-même (par exemple si UserModel.model_validate(UserModel.construct(...))
est appelé) ou toute autre chose puisque vous pouvez transmettre des objets arbitraires dans model_validate
. Pour cette raison, les validateurs mode='before'
sont extrêmement flexibles et puissants, mais peuvent être lourds et sujets aux erreurs à mettre en œuvre. Avant que les validateurs de modèles ne soient des méthodes de classe. Le premier argument doit être cls
(et nous vous recommandons également d'utiliser @classmethod
sous @model_validator
pour une vérification de type appropriée), le deuxième argument sera l'entrée (vous devez généralement le saisir comme Any
et utiliser isinstance
pour affiner le type) et le troisième L'argument (s'il est présent) sera un pydantic.ValidationInfo
.
Les validateurs mode='after'
sont des méthodes d'instance et reçoivent toujours une instance du modèle comme premier argument. Assurez-vous de renvoyer l'instance à la fin de votre validateur. Vous ne devez pas utiliser (cls, ModelType)
comme signature, mais simplement utiliser (self)
et laisser les vérificateurs de type déduire le type de self
pour vous. Comme ils sont entièrement sécurisés, ils sont souvent plus faciles à implémenter que les validateurs mode='before'
. Si un champ ne parvient pas à être validé, les validateurs mode='after'
pour ce champ ne seront pas appelés.
Gestion des erreurs dans les validateurs¶
Comme mentionné dans les sections précédentes, vous pouvez déclencher une ValueError
ou AssertionError
(y compris celles générées par les instructions assert ...
) au sein d'un validateur pour indiquer l'échec de la validation. Vous pouvez également générer une PydanticCustomError
qui est un peu plus verbeuse mais vous offre une flexibilité supplémentaire. Toutes les autres erreurs (y compris TypeError
) sont bouillonnées et non enveloppées dans un 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]
"""
Types spéciaux¶
Pydantic propose quelques types spéciaux qui peuvent être utilisés pour personnaliser la validation.
-
[
InstanceOf
][pydantic.function_validators.InstanceOf] est un type qui peut être utilisé pour valider qu'une valeur est une instance d'une classe donnée.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
][pydantic.function_validators.SkipValidation] est un type qui peut être utilisé pour ignorer la validation sur un champ.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]¶
-
Notez que la validation du deuxième élément est ignorée. S'il est du mauvais type, il émettra un avertissement lors de la sérialisation.
Vérifications sur le terrain¶
Lors de la création de la classe, les validateurs sont vérifiés pour confirmer que les champs qu'ils spécifient existent réellement sur le modèle.
Cela peut s'avérer indésirable si, par exemple, vous souhaitez définir un validateur pour valider des champs qui ne seront présents que sur les sous-classes du modèle où le validateur est défini.
Si vous souhaitez désactiver ces vérifications lors de la création de la classe, vous pouvez transmettre check_fields=False
comme argument mot-clé au validateur.
Validateurs de classes de données¶
Les validateurs fonctionnent également avec les classes de données Pydantic.
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')
Contexte de validation¶
Vous pouvez transmettre un objet contextuel aux méthodes de validation accessibles depuis l'argument info
aux fonctions de validation décorées:
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'
Ceci est utile lorsque vous devez mettre à jour dynamiquement le comportement de validation pendant l'exécution. Par exemple, si vous souhaitez qu'un champ ait un ensemble de valeurs autorisées contrôlables dynamiquement, cela peut être fait en transmettant les valeurs autorisées par contexte et en disposant d'un mécanisme distinct pour mettre à jour ce qui est autorisé:
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]
"""
De même, vous pouvez utiliser un contexte pour la sérialisation .
Utilisation du contexte de validation avec l'initialisation BaseModel
¶
Bien qu'il n'existe aucun moyen de spécifier un contexte dans l'initialiseur BaseModel
standard, vous pouvez contourner ce problème en utilisant contextvars.ContextVar
et une méthode __init__
personnalisée:
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
Réutiliser les validateurs¶
Parfois, vous souhaiterez utiliser le même validateur sur plusieurs champs/modèles (par exemple pour normaliser certaines données d'entrée). L'approche "naïve" consisterait à écrire une fonction distincte, puis à l'appeler à partir de plusieurs décorateurs. Évidemment, cela implique beaucoup de répétitions et de codes passe-partout. L'approche suivante montre comment réutiliser un validateur afin que la redondance soit minimisée et que les modèles redeviennent presque déclaratifs.
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')
本文总阅读量次