Introduction à Pydantic : Validation des Données en Python
Apprenons Pydantic, une bibliothèque Python pour la validation et l’analyse de données à l’aide d’indications de type. Découvrez comment définir des modèles, valider des données, gérer des structures imbriquées et l’intégrer à des frameworks web comme FastAPI.
Progression du tutoriel
1 Bienvenue dans Pydantic !
Vous êtes-vous déjà retrouvé à devoir trier des données entrantes — peut-être d'une API, d'une base de données ou d'une saisie utilisateur — en vous demandant : "Est-ce que la structure est correcte ?" Pydantic intervient pour résoudre ce problème particulier. C'est une bibliothèque Python qui, franchement, rend la validation et l'analyse des données presque agréables.
Considérez-la comme une gardienne méticuleuse de vos données. Vous indiquez à Pydantic à quoi vos données devraient ressembler en utilisant des annotations de type Python standard, et elle vérifie rigoureusement tout ce qui arrive à la porte. Si quelque chose ne correspond pas, elle déclenche une erreur polie, mais ferme. Il ne s'agit pas seulement d'attraper les erreurs tôt ; il s'agit de rendre votre code plus robuste, plus lisible et, de manière générale, moins sujet aux accès de colère imprévus.
- Sécurité des types : Applique les types de données attendus, réduisant les erreurs d'exécution.
- Validation automatique : Convertit les données brutes en objets validés sans effort.
- Sérialisation : Convertit facilement les modèles en dictionnaires ou en JSON.
- Idéal pour les API : Une pierre angulaire pour des frameworks comme FastAPI, gérant les modèles de requête et de réponse.
Prêt à faire obéir vos données ? Cliquez sur "Suivant" pour installer Pydantic.
2 Installation de Pydantic
Avant que Pydantic puisse commencer son bon travail, vous devez l'intégrer à votre projet. Si vous avez travaillé avec FastAPI, il y a de fortes chances que Pydantic soit déjà quelque part dans vos dépendances, car FastAPI s'appuie fortement sur lui en coulisses. Cependant, pour un projet autonome, une simple commande pip install suffit.
Tout d'abord, assurez-vous d'être dans l'environnement virtuel de votre projet. Si vous avez besoin d'un rappel sur la configuration d'un tel environnement, revenez à l'étape précédente de vos tutoriels FastAPI habituels — c'est toujours une bonne habitude. Une fois votre environnement activé, exécutez cette commande :
pip install pydantic
Une fois que c'est fait, Pydantic est prêt à fonctionner. Vous ne verrez pas de défilé, mais croyez-moi, il est là, attendant patiemment de mettre de l'ordre dans vos objets Python.
3 Votre premier modèle - C'est comme une classe, mais en mieux
À la base, Pydantic étend les classes de données de Python en ajoutant une couche de validation. Vous définissez une classe qui hérite de pydantic.BaseModel, puis vous utilisez des annotations de type Python standard pour chaque attribut. Pydantic prend ces annotations et les transforme en règles strictes.
Créons un simple modèle User. Ce modèle s'attendra à un name (une chaîne de caractères) et un age (un entier). Si vous essayez de lui donner autre chose, Pydantic vous dira poliment — ou pas si poliment, selon votre point de vue — que ce n'est pas acceptable.
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
# Maintenant, vous avez un plan pour les données utilisateur.
Voyez ? Cela ressemble à une classe Python normale. La magie opère vraiment lorsque vous essayez de créer une instance.
4 Validation en action - Rattraper les éléments indésirables
Définir un modèle n'est que la moitié du plaisir. Le vrai spectacle commence lorsque vous lui donnez des données. Pydantic les assimilera, vérifiera qu'elles correspondent à vos types définis, et vous rendra un objet impeccable ou, eh bien, le rejettera avec une ValidationError.
Voyons notre modèle User gérer les données correctes et incorrectes. Nous commencerons par un utilisateur parfaitement valide, puis nous essaierons de faire passer un âge invalide — disons, une chaîne de caractères au lieu d'un entier. Pydantic ne se laissera pas berner.
from pydantic import BaseModel, ValidationError
class User(BaseModel):
name: str
age: int
# Ceci fonctionnera parfaitement
valid_user = User(name="Alice", age=30)
print(f"Utilisateur valide : {valid_user}")
# Sortie : Utilisateur valide : name='Alice' age=30
print("\n--- Essai avec des données invalides ---")
try:
invalid_user = User(name="Bob", age="vingt")
except ValidationError as e:
print(f"Erreur interceptée ! \n{e}")
# La sortie affichera une ValidationError indiquant que 'la valeur n'est pas un entier valide'
Remarquez comment Pydantic détecte non seulement l'inadéquation de type, mais vous donne également un message d'erreur plutôt utile. Cette boucle de rétroaction est précieuse pour le débogage et la création d'applications robustes.
5 Annotations de type - Le cadeau de Python à Pydantic
L'ensemble du système de validation de Pydantic repose sur les annotations de type de Python. Elles ne servent pas seulement à satisfaire votre IDE ; Pydantic les utilise activement pour faire respecter les schémas. Au-delà des types de base comme str et int, le module typing de Python offre une mine de conseils plus complexes.
Un conseil extrêmement utile est Optional. Lorsqu'un champ peut être présent ou absent, ou peut être None, Optional (du module typing) est votre allié. Il indique à Pydantic que, bien qu'une valeur soit attendue, None est également une entrée valide.
from pydantic import BaseModel
from typing import Optional # N'oubliez pas cette importation !
class Product(BaseModel):
name: str
price: float
description: Optional[str] # Ce champ peut être une chaîne de caractères ou None
# Produits valides
product1 = Product(name="Ordinateur portable", price=1200.50, description="Informatique puissante en déplacement.")
product2 = Product(name="Souris", price=25.00, description=None) # Explicitly None est acceptable
product3 = Product(name="Clavier", price=75.99) # Omettre cela donne aussi None par défaut
print(f"Produit 1 : {product1}")
print(f"Produit 2 : {product2}")
print(f"Produit 3 : {product3}")
Utiliser Optional[str] est du sucre syntaxique pour Union[str, None], ce qui rend vos intentions cristallines. C'est un petit détail, mais il évite beaucoup de confusion et d'erreurs inattendues liées à None.
6 Valeurs par défaut et champs optionnels - Rendre les choses flexibles
Parfois, un champ n'est pas strictement nécessaire, ou peut-être qu'il devrait simplement avoir une valeur par défaut sensée si aucune valeur n'est fournie. Pydantic s'intègre bien aux valeurs d'arguments par défaut de Python, vous permettant de les définir directement dans la définition de votre modèle. Cela rend vos modèles robustes aux données manquantes tout en gardant les choses prévisibles.
Nous mettrons à jour notre modèle Product pour inclure une quantité par défaut et une liste de tags optionnelle, qui par défaut est une liste vide. Cela signifie que vous n'avez pas toujours à fournir ces valeurs, ce qui est agréable pour une saisie de données moins verbeuse.
from pydantic import BaseModel
from typing import Optional, List # Ajouté List pour le typage des listes
class Product(BaseModel):
name: str
price: float
description: Optional[str] = None # Valeur par défaut de description à None
quantity: int = 1 # Quantité par défaut à 1 si non fournie
tags: List[str] = [] # Par défaut à une liste vide de chaînes de caractères
# Créer des produits, en omettant certains champs
product_default_quantity = Product(name="Casque", price=99.99)
product_with_tags = Product(name="Montre connectée", price=250.00, tags=["portable", "technologie"])
print(f"Produit avec quantité par défaut : {product_default_quantity}")
print(f"Produit avec tags : {product_with_tags}")
# Que se passe-t-il si nous fournissons une non-liste pour les tags ?
try:
bad_tags_product = Product(name="Tasse", price=10.0, tags="céramique")
except Exception as e:
print(f"\nErreur interceptée avec des tags incorrects : \n{e}")
Lors de la définition de valeurs par défaut pour des types mutables comme les listes ou les dictionnaires, utilisez toujours `List[str] = []` directement dans l'affectation, ou envisagez `Field(default_factory=list)` si vous vous inquiétez de l'état mutable partagé (bien que Pydantic gère souvent cela gracieusement en interne pour les affectations simples). Pydantic sait valider les types dans la liste également, prouvant que c'est plus qu'une vérification superficielle.
7 Modèles imbriqués - Quand les choses se compliquent
Les données du monde réel arrivent rarement dans des structures plates et simples. Souvent, vous aurez des objets contenant d'autres objets — un utilisateur peut avoir une adresse, qui elle-même a une rue, une ville et un code postal. Pydantic gère magnifiquement ces structures imbriquées en permettant d'intégrer un BaseModel dans un autre.
Affinerons notre modèle User. Nous introduirons d'abord un modèle Address, puis inclurons une instance de ce modèle Address comme champ dans notre User. Pydantic validera automatiquement le modèle imbriqué lorsque vous instancierez le parent.
from pydantic import BaseModel, ValidationError
class Address(BaseModel):
street: str
city: str
zip_code: str
class User(BaseModel):
name: str
age: int
address: Address # Ce champ attend un modèle Address !
# Un utilisateur valide avec une adresse imbriquée
user_with_address = User(
name="Charlie",
age=45,
address={
"street": "123 Main St",
"city": "Anytown",
"zip_code": "12345"
}
)
print(f"Utilisateur avec adresse : {user_with_address}")
# Que se passe-t-il si l'adresse imbriquée est invalide ?
try:
invalid_address_user = User(
name="Diana",
age=28,
address={
"street": "456 Oak Ave",
"city": "Otherville",
"zip_code": 98765 # Code postal en tant qu'entier au lieu de chaîne !
}
)
except ValidationError as e:
print(f"\nErreur interceptée pour une adresse imbriquée invalide : \n{e}")
Pydantic gère gracieusement la récursivité, garantissant que même les données profondément imbriquées respectent leurs types spécifiés. C'est un moyen fantastique de construire des structures de données complexes et auto-descriptives.
8 Listes et dictionnaires - Gestion des collections
Au-delà des modèles imbriqués uniques, les données se présentent souvent sous forme de collections — listes d'éléments ou dictionnaires associant des clés à des valeurs. Pydantic, en utilisant le module typing de Python, étend également ses capacités de validation à ces structures de données courantes.
Donnons à notre modèle User des amis (une liste d'autres modèles User, peut-être ?) et une liste de passe-temps. Nous ajouterons également un dictionnaire pour des métadonnées arbitraires, juste pour montrer comment Pydantic gère ces types flexibles.
from pydantic import BaseModel
from typing import List, Dict, Optional # Nécessaires pour les listes et les dictionnaires
# Réutilisation de notre modèle User pour l'imbrication
class User(BaseModel):
name: str
age: int
# Pas d'adresse pour simplifier cet exemple, mais elle pourrait être ici !
hobbies: List[str] = []
friends: List['User'] = [] # Référence avant pour les modèles auto-référentiels
metadata: Dict[str, str] = {} # Un dictionnaire de clés de chaîne à valeurs de chaîne
# Créer quelques utilisateurs
friend1 = User(name="Eve", age=25, hobbies=["lecture"])
friend2 = User(name="Frank", age=27, hobbies=["jeux", "programmation"])
# Un utilisateur principal avec une liste d'amis et de passe-temps
main_user = User(
name="Grace",
age=32,
hobbies=["randonnée", "photographie", "cuisine"],
friends=[friend1, friend2],
metadata={"status": "actif", "level": "senior"}
)
print(f"Utilisateur principal avec amis et passe-temps :\n{main_user.model_dump_json(indent=2)}")
# Que se passe-t-il si un ami n'est pas un modèle User ?
try:
bad_friends_user = User(
name="Harry",
age=40,
friends=[{"name": "Ami invalide", "age": "pas-un-entier"}] # Données d'ami invalides
)
except Exception as e:
print(f"\nErreur interceptée avec des données d'amis incorrectes : \n{e}")
Pydantic plonge en profondeur dans les collections, validant chaque élément selon son type spécifié. Pour les modèles auto-référentiels (comme un User ayant une liste d'amis User), vous pouvez utiliser une référence avant, une chaîne littérale du nom du modèle, comme 'User', que Pydantic résout plus tard. Il est assez astucieux de la manière dont il gère ces danses récursives.
9 Pydantic et FastAPI - Un mariage parfait
Si vous avez travaillé avec FastAPI, vous avez probablement rencontré Pydantic sans même vous en rendre compte. FastAPI utilise extensivement les modèles BaseModel de Pydantic pour définir la structure des corps de requête entrants, des paramètres de requête et des modèles de réponse sortants. Cette intégration étroite est l'une des superpuissances de FastAPI.
Lorsque vous définissez une route dans FastAPI et que vous utilisez un modèle Pydantic comme type de paramètre, FastAPI fait automatiquement plusieurs choses :
- Il analyse le corps de la requête JSON entrante dans votre modèle Pydantic.
- Il valide les données par rapport aux annotations de type et aux contraintes de votre modèle.
- Si la validation échoue, il renvoie automatiquement une réponse d'erreur claire (généralement une erreur 422 Unprocessable Entity).
- Il génère la documentation de l'API (OpenAPI/Swagger UI) sur la base de vos modèles Pydantic.
Vous déclarez votre structure de données une fois avec Pydantic, et FastAPI exploite cette déclaration pour la validation, la sérialisation et la documentation — un flux de travail vraiment efficace. Voici un petit aperçu, bien que la configuration de FastAPI soit un autre tutoriel à part entière :
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Item(BaseModel): # Un modèle Pydantic pour nos données d'article
name: str
description: str | None = None # Syntaxe Python 3.10+ pour Optional
price: float
tax: float | None = None
@app.post("/items/")
async def create_item(item: Item): # FastAPI attend automatiquement un modèle Pydantic 'Item'
return item.model_dump() # Convertit le modèle Pydantic en dictionnaire
Ce couplage étroit élimine une montagne de code répétitif que vous écririez autrement pour la validation et la transformation des données. C'est un peu un tour de magie, n'est-ce pas ?
10 Exportation de vos données - `model_dump` et `model_dump_json`
Une fois que vos données sont heureusement validées dans un modèle Pydantic, vous devez souvent les renvoyer dans le monde. Peut-être renvoyez-vous une réponse JSON depuis une API, enregistrez-vous dans une base de données ou faites simplement du débogage. Pydantic rend cette conversion en dictionnaires Python simples ou en chaînes JSON facile.
Pour Pydantic V2 (la version stable actuelle), vous utiliserez model_dump() pour obtenir une représentation de dictionnaire Python et model_dump_json() pour une chaîne JSON. Ces méthodes gèrent automatiquement toutes les imbrications et conversions de types, garantissant que vos données exportées ont l'air aussi bien qu'elles l'étaient à leur arrivée.
from pydantic import BaseModel
from typing import List
class Tag(BaseModel):
name: str
color: str
class Article(BaseModel):
title: str
content: str
tags: List[Tag] = []
is_published: bool = False
# Créer un article
article = Article(
title="La puissance de Pydantic",
content="Une plongée profonde dans la validation des données avec Pydantic.",
tags=[Tag(name="python", color="bleu"), Tag(name="fastapi", color="vert")],
is_published=True
)
# Convertir en un dictionnaire Python
article_dict = article.model_dump()
print(f"Article sous forme de dictionnaire :\n{article_dict}")
# Sortie : {'title': 'La puissance de Pydantic', 'content': 'Une plongée profonde dans la validation des données avec Pydantic.', 'tags': [{'name': 'python', 'color': 'bleu'}, {'name': 'fastapi', 'color': 'vert'}], 'is_published': True}
# Convertir en une chaîne JSON
article_json = article.model_dump_json(indent=2) # indent pour un affichage soigné
print(f"\nArticle sous forme de chaîne JSON :\n{article_json}")
Ces méthodes sont vos outils de prédilection pour faire interagir les objets Pydantic avec les systèmes externes. Elles garantissent une sortie cohérente, simplifiant considérablement votre logique de sérialisation.
11 Et ensuite ? - Au-delà des bases
Vous avez maintenant eu une bonne introduction à Pydantic, vu ses pouvoirs principaux en action, et même entrevu son meilleur ami, FastAPI. Ce que nous avons couvert ne fait qu'effleurer la surface, mais c'est plus qu'assez pour vous permettre de commencer à créer des modèles de données robustes et sûrs en matière de types.
À partir de là, vous pouvez explorer certaines des fonctionnalités plus avancées de Pydantic :
- Validateurs personnalisés : Utilisez
@field_validatorpour créer votre propre logique de validation pour des champs spécifiques, garantissant que les données respectent même les règles métier les plus obscures. - Paramètres Pydantic : Gérez facilement les paramètres de l'application et les variables d'environnement, en tirant parti de la validation de Pydantic pour la configuration.
- Types plus complexes : Explorez les
Enums, lesUUIDs, les objetsdatetime, et même les modèles génériques pour des structures de données véritablement flexibles. Fieldavec des contraintes supplémentaires : Ajoutez des règles de validation plus granulaires comme des longueurs minimales/maximales pour les chaînes, ou des comparaisons supérieur/inférieur pour les nombres.
Vous avez une solide compréhension des concepts fondamentaux de Pydantic. Ensuite, vous pouvez immédiatement commencer à refactoriser un projet existant pour utiliser les modèles Pydantic afin d'améliorer l'hygiène des données, ou plonger directement dans la création d'une application FastAPI, où Pydantic brille vraiment. Allez-y, essayez !
Explorez la documentation officielle de Pydantic pour une plongée plus approfondie.