Aller au contenu

Gestion des erreurs

Pydantic générera une ValidationError chaque fois qu'il trouvera une erreur dans les données qu'il valide.

!!! note Le code de validation ne doit pas déclencher ValidationError lui-même, mais plutôt générer une ValueError ou AssertionError (ou une sous-classe de celle-ci) qui sera interceptée et utilisée pour remplir ValidationError .

Une exception sera générée quel que soit le nombre d'erreurs trouvées, ValidationError contiendra des informations sur toutes les erreurs et comment elles se sont produites.

Vous pouvez accéder à ces erreurs de plusieurs manières:


Méthode
Description
e.errors()
Renvoie une liste des erreurs trouvées dans les données d'entrée.
e.error_count()
Renvoie le nombre d'erreurs trouvées dans errors .
e.json()
Renvoie une représentation JSON des errors .
str(e)
Renvoie une représentation lisible par l'homme des erreurs.

Chaque objet d'erreur contient:


Propriété
Description
ctx
Un objet facultatif qui contient les valeurs requises pour afficher le message d'erreur.
input
Entrée fournie pour validation.
loc
L'emplacement de l'erreur sous forme de liste.
msg
Une explication lisible par l'homme de l'erreur.
type
Un identifiant lisible par ordinateur du type d’erreur.
url
L'URL vers des informations supplémentaires sur l'erreur.

Le premier élément de la liste loc sera le champ dans lequel l'erreur s'est produite, et si le champ est un sous-modèle , les éléments suivants seront présents pour indiquer l'emplacement imbriqué de l'erreur.

En guise de démonstration :

from typing import List

from pydantic import BaseModel, ValidationError, conint


class Location(BaseModel):
    lat: float = 0.1
    lng: float = 10.1


class Model(BaseModel):
    is_required: float
    gt_int: conint(gt=42)
    list_of_ints: List[int] = None
    a_float: float = None
    recursive_model: Location = None


data = dict(
    list_of_ints=['1', 2, 'bad'],
    a_float='not a float',
    recursive_model={'lat': 4.2, 'lng': 'New York'},
    gt_int=21,
)

try:
    Model(**data)
except ValidationError as e:
    print(e)
    """
    5 validation errors for Model
    is_required
      Field required [type=missing, input_value={'list_of_ints': ['1', 2,...ew York'}, 'gt_int': 21}, input_type=dict]
    gt_int
      Input should be greater than 42 [type=greater_than, input_value=21, input_type=int]
    list_of_ints.2
      Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='bad', input_type=str]
    a_float
      Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='not a float', input_type=str]
    recursive_model.lng
      Input should be a valid number, unable to parse string as a number [type=float_parsing, input_value='New York', input_type=str]
    """

try:
    Model(**data)
except ValidationError as e:
    print(e.errors())
    """
    [
        {
            'type': 'missing',
            'loc': ('is_required',),
            'msg': 'Field required',
            'input': {
                'list_of_ints': ['1', 2, 'bad'],
                'a_float': 'not a float',
                'recursive_model': {'lat': 4.2, 'lng': 'New York'},
                'gt_int': 21,
            },
            'url': 'https://errors.pydantic.dev/2/v/missing',
        },
        {
            'type': 'greater_than',
            'loc': ('gt_int',),
            'msg': 'Input should be greater than 42',
            'input': 21,
            'ctx': {'gt': 42},
            'url': 'https://errors.pydantic.dev/2/v/greater_than',
        },
        {
            'type': 'int_parsing',
            'loc': ('list_of_ints', 2),
            'msg': 'Input should be a valid integer, unable to parse string as an integer',
            'input': 'bad',
            'url': 'https://errors.pydantic.dev/2/v/int_parsing',
        },
        {
            'type': 'float_parsing',
            'loc': ('a_float',),
            'msg': 'Input should be a valid number, unable to parse string as a number',
            'input': 'not a float',
            'url': 'https://errors.pydantic.dev/2/v/float_parsing',
        },
        {
            'type': 'float_parsing',
            'loc': ('recursive_model', 'lng'),
            'msg': 'Input should be a valid number, unable to parse string as a number',
            'input': 'New York',
            'url': 'https://errors.pydantic.dev/2/v/float_parsing',
        },
    ]
    """

Erreurs personnalisées

Dans vos types de données personnalisés ou validateurs, vous devez utiliser ValueError ou AssertionError pour générer des erreurs.

Voir validateurs pour plus de détails sur l'utilisation du décorateur @validator .

from pydantic import BaseModel, ValidationError, field_validator


class Model(BaseModel):
    foo: str

    @field_validator('foo')
    def value_must_equal_bar(cls, v):
        if v != 'bar':
            raise ValueError('value must be "bar"')

        return v


try:
    Model(foo='ber')
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    foo
      Value error, value must be "bar" [type=value_error, input_value='ber', input_type=str]
    """
    print(e.errors())
    """
    [
        {
            'type': 'value_error',
            'loc': ('foo',),
            'msg': 'Value error, value must be "bar"',
            'input': 'ber',
            'ctx': {'error': ValueError('value must be "bar"')},
            'url': 'https://errors.pydantic.dev/2/v/value_error',
        }
    ]
    """

Vous pouvez également utiliser PydanticCustomError pour contrôler entièrement la structure de l'erreur:

from pydantic_core import PydanticCustomError

from pydantic import BaseModel, ValidationError, field_validator


class Model(BaseModel):
    foo: str

    @field_validator('foo')
    def value_must_equal_bar(cls, v):
        if v != 'bar':
            raise PydanticCustomError(
                'not_a_bar',
                'value is not "bar", got "{wrong_value}"',
                dict(wrong_value=v),
            )
        return v


try:
    Model(foo='ber')
except ValidationError as e:
    print(e)
    """
    1 validation error for Model
    foo
      value is not "bar", got "ber" [type=not_a_bar, input_value='ber', input_type=str]
    """

Messages d'erreur

Pydantic tente de fournir des messages d'erreur par défaut utiles pour les erreurs de validation et d'utilisation.

Nous avons fourni une documentation sur les codes d'erreur par défaut dans les sections suivantes:

Personnaliser les messages d'erreur

Vous pouvez personnaliser les messages d'erreur en créant un gestionnaire d'erreurs personnalisé.

from typing import Dict, List

from pydantic_core import ErrorDetails

from pydantic import BaseModel, HttpUrl, ValidationError

CUSTOM_MESSAGES = {
    'int_parsing': 'This is not an integer! 🤦',
    'url_scheme': 'Hey, use the right URL scheme! I wanted {expected_schemes}.',
}


def convert_errors(
    e: ValidationError, custom_messages: Dict[str, str]
) -> List[ErrorDetails]:
    new_errors: List[ErrorDetails] = []
    for error in e.errors():
        custom_message = custom_messages.get(error['type'])
        if custom_message:
            ctx = error.get('ctx')
            error['msg'] = (
                custom_message.format(**ctx) if ctx else custom_message
            )
        new_errors.append(error)
    return new_errors


class Model(BaseModel):
    a: int
    b: HttpUrl


try:
    Model(a='wrong', b='ftp://example.com')
except ValidationError as e:
    errors = convert_errors(e, CUSTOM_MESSAGES)
    print(errors)
    """
    [
        {
            'type': 'int_parsing',
            'loc': ('a',),
            'msg': 'This is not an integer! 🤦',
            'input': 'wrong',
            'url': 'https://errors.pydantic.dev/2/v/int_parsing',
        },
        {
            'type': 'url_scheme',
            'loc': ('b',),
            'msg': "Hey, use the right URL scheme! I wanted 'http' or 'https'.",
            'input': 'ftp://example.com',
            'ctx': {'expected_schemes': "'http' or 'https'"},
            'url': 'https://errors.pydantic.dev/2/v/url_scheme',
        },
    ]
    """

Un cas d'utilisation courant serait de traduire des messages d'erreur. Par exemple, dans l'exemple ci-dessus, nous pourrions traduire les messages d'erreur en remplaçant le dictionnaire CUSTOM_MESSAGES par un dictionnaire de traductions.

Un autre exemple consiste à personnaliser la façon dont la valeur 'loc' d'une erreur est représentée.

from typing import Any, Dict, List, Tuple, Union

from pydantic import BaseModel, ValidationError


def loc_to_dot_sep(loc: Tuple[Union[str, int], ...]) -> str:
    path = ''
    for i, x in enumerate(loc):
        if isinstance(x, str):
            if i > 0:
                path += '.'
            path += x
        elif isinstance(x, int):
            path += f'[{x}]'
        else:
            raise TypeError('Unexpected type')
    return path


def convert_errors(e: ValidationError) -> List[Dict[str, Any]]:
    new_errors: List[Dict[str, Any]] = e.errors()
    for error in new_errors:
        error['loc'] = loc_to_dot_sep(error['loc'])
    return new_errors


class TestNestedModel(BaseModel):
    key: str
    value: str


class TestModel(BaseModel):
    items: List[TestNestedModel]


data = {'items': [{'key': 'foo', 'value': 'bar'}, {'key': 'baz'}]}

try:
    TestModel.model_validate(data)
except ValidationError as e:
    print(e.errors())  # (1)!
    """
    [
        {
            'type': 'missing',
            'loc': ('items', 1, 'value'),
            'msg': 'Field required',
            'input': {'key': 'baz'},
            'url': 'https://errors.pydantic.dev/2/v/missing',
        }
    ]
    """
    pretty_errors = convert_errors(e)
    print(pretty_errors)  # (2)!
    """
    [
        {
            'type': 'missing',
            'loc': 'items[1].value',
            'msg': 'Field required',
            'input': {'key': 'baz'},
            'url': 'https://errors.pydantic.dev/2/v/missing',
        }
    ]
    """
  1. Par défaut, e.errors() produit une liste d'erreurs avec des valeurs loc qui prennent la forme de tuples.
  2. Avec notre fonction personnalisée loc_to_dot_sep , nous avons modifié la forme de la représentation loc .

本文总阅读量