Pydantic est la bibliothèque de validation de données la plus populaire de Python, qui permet de transformer les indications de type en règles de validation à l'exécution.
Au lieu d'écrire des dizaines de if isinstance() et des fonctions de validation personnalisées, vous définissez votre structure de données une seule fois en utilisant la syntaxe familière de Python.
Pydantic se charge du reste : validation des données entrantes, conversion des types le cas échéant et fourniture de messages d'erreur clairs en cas d'échec de la validation.
Considérez cette simple fonction Python avec des indications de type et une documentation appropriée :
defcalculate_user_discount(age:int, is_premium_member:bool, purchase_amount:float)->float:"""Calculate discount percentage based on user profile and purchase amount."""if age >=65:
base_discount =0.15elif is_premium_member:
base_discount =0.10else:
base_discount =0.05return purchase_amount * base_discountPropulsé par
En regardant sa signature, vous savez exactement quel type de données il accepte et produit à l'autre bout. Mais pouvez-vous en dire autant de l'utilisateur final ? Après tout, ces indications de type ne servent qu'à des fins de documentation - si quelqu'un transmet un type de données incorrect, l'interpréteur de Python n'est jamais inquiété :
# This runs without error, even though types are completely wrong!
discount = calculate_user_discount(True,1,5)print(discount)# Output: 0.5 (True becomes 1, 1 is truthy, so 5 * 0.10)Propulsé par
Il s'agit d'un exemple classique du typage dynamique de Python. Elle offre aux programmeurs un développement rapide et une grande liberté, mais au prix de l'introduction de problèmes de validation de type qui font souvent surface en production.
Dans ce tutoriel, je vais vous expliquer comment utiliser Pydantic pour des applications réelles. Nous commencerons par votre premier modèle de données et progresserons jusqu'aux modèles avancés utilisés dans les systèmes de production. Vous découvrirez comment valider des structures de données imbriquées, écrire des validateurs personnalisés pour des règles métier complexes, et intégrer Pydantic avec des frameworks populaires comme FastAPI et SQLAlchemy.
Qu'est-ce que Pydantic ?
Vous jouez essentiellement à la roulette des données lorsque vous créez des applications qui interagissent avec le monde extérieur. Les utilisateurs peuvent soumettre un numéro de téléphone sous forme de chaîne de caractères au lieu d'un nombre entier, une API peut renvoyer des valeurs nulles là où vous attendez des nombres, ou un fichier de configuration peut contenir des fautes de frappe qui interrompent votre application à 3 heures du matin.
Les approches traditionnelles de Python pour résoudre ce problème deviennent rapidement lourdes. Vous finissez par écrire un code de validation comme celui-ci :
defcreate_user(data):# Manual validation nightmareifnotisinstance(data.get('age'),int):raise ValueError("Age must be an integer")if data['age']<0or data['age']>150:raise ValueError("Age must be between 0 and 150")ifnotisinstance(data.get('email'),str)or'@'notin data['email']:raise ValueError("Invalid email format")ifnotisinstance(data.get('is_active'),bool):raise ValueError("is_active must be a boolean")# Finally create the user...return User(data['age'], data['email'], data['is_active'])Propulsé par
Multipliez cela par chaque structure de données de votre application et vous passerez plus de temps à écrire du code de validation que de la logique d'entreprise.
Pydantic résout ce problème en combinant trois concepts puissants : les indications de type, la validation en cours d'exécution et la sérialisation automatique. Au lieu de procéder à des vérifications manuelles, vous définissez votre structure de données une seule fois en utilisant la syntaxe d'annotation de type de Python, et Python se charge automatiquement de toute la validation :
from pydantic import BaseModel, EmailStr
from typing import Optional
classUser(BaseModel):
age:int
email: EmailStr
is_active:bool=True
nickname: Optional[str]=None# Pydantic automatically validates and converts data
user_data ={"age":"25",# String gets converted to int"email":"[email protected]","is_active":"true"# String gets converted to bool}
user = User(**user_data)print(user.age)# 25 (as integer)print(user.model_dump())# Clean dictionary outputPropulsé par
Caractéristiques de Pydantic
L'approche de Pydantic vous offre plusieurs avantages. La performance est le premier avantage que vous remarquerez - la logique de validation de base de Python est écrite en Rust, ce qui la rend plus rapide que la validation Python écrite à la main dans la plupart des cas. Votre application peut traiter des milliers de demandes sans que la validation ne devienne un goulot d'étranglement.
Pydantic permet également l'intégration avec des frameworks modernes. FastAPI, l'un des frameworks web Python à la croissance la plus rapide, utilise des modèles Python pour générer automatiquement la documentation de l'API, valider les corps des requêtes et sérialiser les réponses. Lorsque vous définissez un modèle Pydantic, vous bénéficiez gratuitement de la génération de schémas OpenAPI:
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()classUserCreate(BaseModel):
name:str
email: EmailStr
age:int@app.post("/users/")asyncdefcreate_user(user: UserCreate):# FastAPI automatically validates the request body# and generates API docs from your Pydantic modelreturn{"message":f"Created user {user.name}"}Propulsé par
La génération de schémas JSON se fait automatiquement avec chaque modèle Pydantic. Cela signifie que vos structures de données s'auto-documentent et que vous pouvez générer des bibliothèques de clients, des règles de validation pour les applications frontales ou des schémas de base de données à partir de la même source de vérité.
Pydantique vs. Classes de données vs. Guimauve
Mais quand devriez-vous choisir Pydantic plutôt que d'autres solutions ? Si vous comparez Pydantic et les classes de données, la décision se résume aux besoins de validation. Le site@dataclass de Python est parfait pour les conteneurs de données simples où vous faites confiance à l'entrée, mais Python excelle lorsque vous avez besoin de validation, de sérialisation et d'intégration avec des frameworks web :
from dataclasses import dataclass
from pydantic import BaseModel
# Dataclass: fast, simple, no validation@dataclassclassUserDataclass:
name:str
age:int# Pydantic: validation, serialization, framework integrationclassUserPydantic(BaseModel):
name:str
age:intPropulsé par
Si l'on compare Python et Marshmallow, l'approche de type hint de Python semble plus naturelle pour les développeurs Python, tandis que Marshmallow utilise des classes de schéma. Pydantic offre également de meilleures performances et une intégration plus facile avec les frameworks asynchrones modernes.
Pydantic fonctionne mieux lorsque vous construisez des API, traitez des données externes, gérez la configuration, ou tout autre scénario où l'échec de la validation des données doit être détecté tôt plutôt que de causer des bogues mystérieux plus tard. Il convertit les erreurs d'exécution en messages de validation clairs et exploitables qui aident les développeurs et les utilisateurs à comprendre ce qui n'a pas fonctionné.
Démarrer avec Pydantic
Nous allons voir comment utiliser Pydantic dans vos projets, de l'installation à la création de vos premiers modèles de données.
Installation et configuration
L'installation de Pydantic est simple, mais quelques écueils courants peuvent faire trébucher les nouveaux venus. Je vous guiderai à travers le processus d'installation approprié et vous aiderai à éviter les erreurs qui entraînent un débogage inutile.
Tout d'abord, créez un environnement virtuel pour isoler les dépendances de votre projet. Cela permet d'éviter les conflits de version qui peuvent entraîner de mystérieuses erreurs d'importation par la suite :
# Create and activate virtual environment (works in zsh and bash)
python -m venv pydantic_env
source pydantic_env/bin/activate # On macOS/Linux# pydantic_env\Scripts\activate # On WindowsPropulsé par
Maintenant, installez Pydantic avec la commande suivante :
Pour la fonctionnalité de validation des courriels, dont vous aurez probablement besoin dans des applications réelles (et dans ce tutoriel), installez les suppléments pour les courriels :
Les guillemets autour de pydantic[email] sont importants dans presque tous les terminaux. Sans eux, vous risquez d'obtenir un message d'erreur "aucune correspondance trouvée".
Note: Si vous rencontrez ModuleNotFoundError: No module named 'pydantic', vérifiez les points suivants :
Mauvais environnement Python: Assurez-vous que votre environnement virtuel est activé
Plusieurs versions de Python: Vérifiez que vous utilisez la même version de Python que celle qui a permis d'installer Python.
Configuration IDE: Il se peut que votre éditeur de code utilise un interprète Python différent
Avertissement de dénomination critique: Ne nommez jamais votre fichier Python pydantic.py. Cela crée une importation circulaire qui cassera votre code avec des messages d'erreur déroutants. Python essaiera d'importer votre fichier au lieu de la bibliothèque Python actuelle.
Votre premier modèle pydantique
Construisons un modèle utilisateur simple pour voir Pydantic en action. Au lieu de commencer par des exemples abstraits, nous allons résoudre un problème réel : valider les données d'enregistrement d'un utilisateur à partir d'un formulaire web.
from pydantic import BaseModel, EmailStr
from typing import Optional
from datetime import datetime
classUser(BaseModel):
name:str
email: EmailStr
age:int
is_active:bool=True
created_at: datetime =None# Test with clean data
clean_data ={"name":"Alice Johnson","email":"[email protected]","age":28}
user = User(**clean_data)print(f"User created: {user.name}, Age: {user.age}")print(f"Model output: {user.model_dump()}")Propulsé par
Sortie :
User created: Alice Johnson, Age: 28
Model output: {'name': 'Alice Johnson', 'email': '[email protected]', 'age': 28, 'is_active': True, 'created_at': None}Propulsé par
Décortiquons ce qui se passe dans cette définition de modèle :
Création d'un modèle pydantique : Votre classe User hérite de BaseModel, ce qui lui confère toutes les capacités de validation et de sérialisation de Pydantic. Cet héritage transforme une classe Python ordinaire en un outil de validation de données.
Définitions des champs: Chaque ligne de la classe définit un champ avec son type attendu. La syntaxename: str indique à Pydantic que name doit être une chaîne de caractères, age: int signifie que l'âge doit être un nombre entier, et ainsi de suite.
EmailStr expliqué: EmailStr est un type spécial de Pydantic qui valide automatiquement les adresses électroniques. Il provient du paquet pydantic[email] que vous avez installé précédemment et utilise des expressions régulières pour s'assurer que le format de l'e-mail est valide. Si quelqu'un passe "not-an-email", Pydantic lèvera une erreur de validation.
Valeurs par défaut: Les champs tels que is_active: bool = True ont des valeurs par défaut. Si vous ne fournissez pas ces champs lors de la création d'un utilisateur, Pydantic utilise les valeurs par défaut. Le champ= None pour created_at est facultatif.
Instanciation du modèle: Lorsque vous appelez User(**clean_data),** décompresse votre dictionnaire et transmet chaque paire clé-valeur en tant qu'argument de mot-clé au constructeur du modèle.
Voyons maintenant la conversion automatique des types de Pydantic en action :
# Messy data that still works
messy_data ={"name":"Bob Smith","email":"[email protected]","age":"35",# String instead of int"is_active":"true"# String instead of bool}
user = User(**messy_data)print(f"Age type: {type(user.age)}")# <class 'int'>print(f"Is active type: {type(user.is_active)}")# <class 'bool'>Propulsé par
Voyons maintenant la conversion automatique des types de Pydantic en action :
# Messy data that still works
messy_data ={"name":"Bob Smith","email":"[email protected]","age":"35",# String instead of int"is_active":"true"# String instead of bool}
user = User(**messy_data)print(f"Age type: {type(user.age)}")# <class 'int'>print(f"Is active type: {type(user.is_active)}")# <class 'bool'>Propulsé par
Sortie :
Age type: <class'int'>
Is active type: <class'bool'>Propulsé par
Comme vous pouvez le constater, les champsage et is_inactive sont automatiquement convertis dans leur format par la validation.
En cas d'échec de la validation, Pydantic fournit des messages d'erreur clairs :
from pydantic import ValidationError
try:
invalid_user = User(
name="",# Empty string
email="not-an-email",# Invalid email
age=-5# Negative age)except ValidationError as e:print(e)Propulsé par
Sortie :
# Shows exactly which fields failed and why
1 validation error for User
email
value is not a valid email address: An email address must have an @-sign. [type=value_error, input_value='not-an-email', input_type=str]Propulsé par
BaseModel vs. classes de données
Comprendre quand utiliser BaseModel de Python ou @dataclass de Python vous aide à choisir le bon outil pour chaque situation.
Les classes de données Python sont parfaites pour les conteneurs de données simples dont vous contrôlez l'entrée :
from dataclasses import dataclass
@dataclassclassProductDataclass:
name:str
price:float
in_stock:bool# Fast, simple, but no validation
product = ProductDataclass("Laptop",999.99,True)# This also works, even though types are wrong:
broken_product = ProductDataclass(123,"expensive","maybe")Propulsé par
Les modèles pydantiques ajoutent la validation, la sérialisation et l'intégration du cadre :
from pydantic import BaseModel, Field
classProductPydantic(BaseModel):
name:str= Field(min_length=1)
price:float= Field(gt=0)# Must be greater than 0
in_stock:bool# Automatic validation prevents bad datatry:
product = ProductPydantic(name="", price=-10, in_stock="maybe")except ValidationError as e:print("Validation caught the errors!")# Valid data works perfectly
good_product = ProductPydantic(
name="Laptop",
price="999.99",# String converted to float
in_stock=True)Propulsé par
Quand choisir chaque approche ?
Utilisez les classes de données pour les structures de données internes, les objets de configuration ou lorsque les performances sont critiques et que vous faites confiance à vos sources de données.
Utilisez Pydantic pour les points d'extrémité d'API, les entrées utilisateur, l'analyse de données externes, ou lorsque vous avez besoin d'une sérialisation JSON.
Pydantic ajoute une certaine surcharge par rapport aux classes de données, mais ce coût est généralement négligeable par rapport aux bogues qu'il permet d'éviter et au temps de développement qu'il permet d'économiser. Pour les applications web, l'intégration automatique avec des frameworks comme FastAPI fait de Pydantic un choix évident.
Les fonctions de validation et de sérialisation deviennent de plus en plus utiles au fur et à mesure que votre application se développe. En commençant par les modèles Pydantic, vous disposez d'une base solide qui s'adapte à vos besoins.
Construire des modèles de données avec Pydantic
Maintenant que vous avez compris les principes de base, abordons les défis auxquels vous serez confronté lors de la création d'applications prêtes à être produites.
Validation du champ et contraintes
Prenons l'exemple d'une API de catalogue de produits dans laquelle les données de prix proviennent de plusieurs fournisseurs appliquant des normes de formatage différentes. Certains envoient des prix sous forme de chaînes, d'autres sous forme de flotteurs et, occasionnellement, quelqu'un envoie un prix négatif qui fait planter votre système de facturation.
La fonctionField() de Pydantic transforme les indications de type de base en règles de validation sophistiquées qui protègent votre application :
from pydantic import BaseModel, Field
from decimal import Decimal
from typing import Optional
classProduct(BaseModel):
name:str= Field(min_length=1, max_length=100)
price: Decimal = Field(gt=0, le=10000)# Greater than 0, less than or equal to 10,000
description: Optional[str]= Field(None, max_length=500)
category:str= Field(..., pattern=r'^[A-Za-z\s]+$')# Only letters and spaces
stock_quantity:int= Field(ge=0)# Greater than or equal to 0
is_available:bool=True# This works - all constraints satisfied
valid_product = Product(
name="Wireless Headphones",
price="199.99",# String converted to Decimal
description="High-quality wireless headphones",
category="Electronics",
stock_quantity=50)# This fails with clear error messagestry:
invalid_product = Product(
name="",# Too short
price=-50,# Negative price
category="Electronics123",# Contains numbers
stock_quantity=-5# Negative stock)except ValidationError as e:print(f"Validation errors: {len(e.errors())} issues found")Propulsé par
Chaque paramètre de Field() a une fonction spécifique : min_length et max_length empêchent les violations du schéma de la base de données, gt et le créent des limites à la logique d'entreprise et pattern valide les données formatées à l'aide d'expressions régulières. La syntaxeField(...) avec des points de suspension marque les champs obligatoires, tandis que Field(None, ...) crée des champs facultatifs avec des règles de validation.
Coercition de type contre validation stricte
Par défaut, Pydantic convertit les types compatibles plutôt que de les rejeter. Cette flexibilité fonctionne bien pour les entrées utilisateur, mais certains scénarios exigent une correspondance exacte des types :
from pydantic import BaseModel, Field, ValidationError
# Default: lenient type coercionclassFlexibleOrder(BaseModel):
order_id:int
total_amount:float
is_paid:bool# These all work due to automatic conversion
flexible_order = FlexibleOrder(
order_id="12345",# String to int
total_amount="99.99",# String to float
is_paid="true"# String to bool)# Strict validation when precision mattersclassStrictOrder(BaseModel):
model_config ={"str_strip_whitespace":True,"validate_assignment":True}
order_id:int= Field(strict=True)
total_amount:float= Field(strict=True)
is_paid:bool= Field(strict=True)Propulsé par
Le dictionnaire model_config contrôle le comportement de validation dans l'ensemble de votre modèle. L'option str_strip_whitespace nettoie automatiquement les chaînes de caractères saisies, tandis que l'option validate_assignment garantit que les modifications apportées aux champs après la création du modèle déclenchent toujours la validation. Les champs individuels peuvent remplacer ces paramètres à l'aide de Field(strict=True) pour les situations nécessitant une correspondance exacte des types, comme les calculs financiers ou les données scientifiques.
Modèles imbriqués et données complexes
Les applications réelles gèrent des structures de données complexes et interconnectées. Une commande de commerce électronique contient des informations sur le client, des adresses de livraison et plusieurs produits, chacun nécessitant sa propre validation :
from typing import List
from datetime import datetime
classAddress(BaseModel):
street:str= Field(min_length=5)
city:str= Field(min_length=2)
postal_code:str= Field(pattern=r'^\d{5}(-\d{4})?$')
country:str="USA"classCustomer(BaseModel):
name:str= Field(min_length=1)
email: EmailStr
shipping_address: Address
billing_address: Optional[Address]=NoneclassOrderItem(BaseModel):
product_id:int= Field(gt=0)
quantity:int= Field(gt=0, le=100)
unit_price: Decimal = Field(gt=0)classOrder(BaseModel):
order_id:str= Field(pattern=r'^ORD-\d{6}$')
customer: Customer
items: List[OrderItem]= Field(min_items=1)
order_date: datetime = Field(default_factory=datetime.now)# Complex nested data validation
order_data ={"order_id":"ORD-123456","customer":{"name":"John Doe","email":"[email protected]","shipping_address":{"street":"123 Main Street","city":"Anytown","postal_code":"12345"}},"items":[{"product_id":1,"quantity":2,"unit_price":"29.99"},{"product_id":2,"quantity":1,"unit_price":"149.99"}]}
order = Order(**order_data)print(f"Order validated with {len(order.items)} items")Propulsé par
Pydantic valide les structures imbriquées de manière récursive - le champ Customer devient un objet Customer complet, qui valide son propre champ Address. La syntaxe List[OrderItem] valide chaque élément de la liste en tant que OrderItem, tandis que Field(min_items=1) empêche les commandes vides d'atteindre votre système d'inventaire. L'utilisation de default_factory=datetime.now permet de créer des horodatages uniques pour chaque commande.
Champs facultatifs et gestion de l'absence de champs
Les besoins en données varient selon les opérations. La création par l'utilisateur exige des informations complètes, tandis que les mises à jour doivent accepter des changements partiels :
from typing import Optional
classUserCreate(BaseModel):
name:str= Field(min_length=1)
email: EmailStr
age:int= Field(ge=13, le=120)
phone: Optional[str]= Field(None, pattern=r'^\+?1?\d{9,15}$')classUserUpdate(BaseModel):
name: Optional[str]= Field(None, min_length=1)
email: Optional[EmailStr]=None
age: Optional[int]= Field(None, ge=13, le=120)
phone: Optional[str]= Field(None, pattern=r'^\+?1?\d{9,15}$')# PATCH request with partial data
update_data ={"name":"Jane Smith","age":30}
user_update = UserUpdate(**update_data)# Serialize only provided fields
patch_data = user_update.model_dump(exclude_none=True)print(f"Fields to update: {list(patch_data.keys())}")Propulsé par
Sérialisation convertit les objets Pydantic en dictionnaires ou en chaînes JSON pour le stockage ou la transmission. La méthode model_dump() se charge de cette conversion, exclude_none=True supprimant les champs non fournis. Ce modèle fonctionne parfaitement pour les requêtes PATCH dans lesquelles les clients n'envoient que les champs qu'ils souhaitent modifier, ce qui permet d'éviter les écrasements accidentels de données dans votre base de données.
Cette base vous prépare à relever le prochain défi : mettre en œuvre une logique de validation personnalisée qui tient compte des règles de gestion propres à votre application.
Validation personnalisée et intégration dans le monde réel
La création d'applications de production implique la manipulation de données qui ne correspondent pas aux modèles de contrôle de type standard.
Pensez à un formulaire d'enregistrement d'utilisateur dont les exigences en matière de mot de passe varient en fonction des plans d'abonnement, ou à une API qui reçoit des données d'adresses de plusieurs pays avec différents formats de codes postaux. Ces scénarios requièrent une logique de validation personnalisée qui intègre vos règles commerciales spécifiques tout en s'intégrant harmonieusement aux cadres web et aux systèmes de configuration.
Cette section vous montre comment mettre en œuvre des modèles de validation personnalisés pratiques, intégrer les modèles Pydantic avec FastAPI pour la documentation automatique de l'API, et gérer les paramètres de l'application par le biais de variables d'environnement en utilisant l' approche du fichier.env sur laquelle s'appuient la plupart des applications de production.
Valideurs de champs et validation de modèles
Lorsque la logique métier détermine la validité des données, le décorateur @field_validator de Pydantic transforme vos fonctions de validation en une partie du modèle lui-même. Prenons l'exemple d'un système d'enregistrement des utilisateurs dans lequel les différents niveaux d'abonnement ont des exigences différentes en matière de mot de passe :
from pydantic import BaseModel, field_validator, Field
import re
classUserRegistration(BaseModel):
username:str= Field(min_length=3)
email: EmailStr
password:str
subscription_tier:str= Field(pattern=r'^(free|pro|enterprise)$')@field_validator('password')@classmethoddefvalidate_password_complexity(cls, password, info):
tier = info.data.get('subscription_tier','free')iflen(password)<8:raise ValueError('Password must be at least 8 characters')if tier =='enterprise'andnot re.search(r'[A-Z]', password):raise ValueError('Enterprise accounts require uppercase letters')return passwordPropulsé par
Le décorateur @field_validator vous permet d'accéder aux valeurs d'autres champs par l'intermédiaire du paramètre info.data, ce qui permet d'appliquer des règles de validation dépendant de plusieurs champs. Le validateur s'exécute après la vérification de base des types, de sorte que vous pouvez supposer que subscription_tier est l'une des valeurs autorisées.
Pour la validation de plusieurs champs, le décorateur@model_validator s'exécute après la validation de tous les champs individuels :
from datetime import datetime
from pydantic import model_validator
classEventRegistration(BaseModel):
start_date: datetime
end_date: datetime
max_attendees:int= Field(gt=0)
current_attendees:int= Field(ge=0)@model_validator(mode='after')defvalidate_event_constraints(self):if self.end_date <= self.start_date:raise ValueError('Event end date must be after start date')if self.current_attendees > self.max_attendees:raise ValueError('Current attendees cannot exceed maximum')return selfPropulsé par
Le paramètre mode='after' garantit que le validateur reçoit une instance de modèle entièrement construite, ce qui est parfait pour la logique métier qui nécessite l'accès à tous les champs validés. Le validateur doit renvoyer self pour indiquer que la validation est réussie.
Intégration FastAPI
L'intégration de FastAPI avec Pydantic crée une validation automatique des requêtes et une documentation de l'API. Le modèle clé consiste à créer des modèles distincts pour les différentes opérations, ce qui vous permet de contrôler les données qui circulent dans chaque direction :
from fastapi import FastAPI
from typing import Optional
from datetime import datetime
app = FastAPI()classUserCreate(BaseModel):
username:str= Field(min_length=3)
email: EmailStr
password:str= Field(min_length=8)classUserResponse(BaseModel):id:int
username:str
email: EmailStr
created_at: datetime
@app.post("/users/", response_model=UserResponse)asyncdefcreate_user(user: UserCreate):# FastAPI automatically validates the request body
new_user ={"id":1,"username": user.username,"email": user.email,"created_at": datetime.now()}return UserResponse(**new_user)Propulsé par
La séparation entre les modèles d'entrée et de sortie présente plusieurs avantages. Les modèles d'entrée peuvent inclure des règles de validation et des champs obligatoires, tandis que les modèles de sortie contrôlent exactement les données envoyées aux clients. FastAPI génère automatiquement de la documentation OpenAPI à partir de vos modèles Pydantic, créant ainsi des documents API interactifs que les développeurs peuvent utiliser pour tester les points de terminaison.
Pour les opérations de mise à jour, vous pouvez créer des modèles dont tous les champs sont facultatifs :
classUserUpdate(BaseModel):
username: Optional[str]= Field(None, min_length=3)
email: Optional[EmailStr]=None@app.patch("/users/{user_id}")asyncdefupdate_user(user_id:int, user_update: UserUpdate):# Only update provided fields
update_data = user_update.model_dump(exclude_unset=True)# Your database update logic herereturn{"message":f"Updated user {user_id}"}Propulsé par
Le paramètre exclude_unset=True dans les opérations PATCH garantit que vous ne mettez à jour que les champs qui ont été explicitement fournis, évitant ainsi les écrasements accidentels. Ce modèle fonctionne parfaitement pour les API REST où les clients envoient des mises à jour partielles.
Gestion de la configuration avec les variables d'environnement
Les applications de production ont besoin d'une gestion de la configuration sécurisée et facile à déployer. Le siteBaseSettings de Pydantic, combiné aux fichiers.env, permet une configuration sûre qui fonctionne dans les environnements de développement, d'essai et de production.
Tout d'abord, créez un fichier.env à la racine de votre projet :
# .env fileDATABASE_URL=postgresql://user:password@localhost:5432/myapp
SECRET_KEY=your-secret-key-here
DEBUG=false
ALLOWED_HOSTS=localhost,127.0.0.1,yourdomain.comPropulsé par
Définissez ensuite votre modèle de paramétrage :
from pydantic import BaseSettings, Field
from typing import List
classAppSettings(BaseSettings):
database_url:str= Field(description="Database connection URL")
secret_key:str= Field(description="Secret key for JWT tokens")
debug:bool= Field(default=False)
allowed_hosts: List[str]= Field(default=["localhost"])classConfig:
env_file =".env"
case_sensitive =False# Load settings automatically from environment and .env file
settings = AppSettings()Propulsé par
La classe BaseSettings lit automatiquement les variables d'environnement, les fichiers .env et les arguments de la ligne de commande. Les variables d'environnement ont la priorité sur les valeurs du fichier .env, ce qui permet de remplacer facilement les paramètres dans différents environnements de déploiement. Le paramètrecase_sensitive = False permet d'attribuer des noms flexibles aux variables d'environnement.
Pour les applications complexes, vous pouvez organiser les paramètres en groupes logiques :
Cette approche imbriquée permet de conserver les paramètres connexes ensemble tout en maintenant une séparation claire entre les différents composants de votre application. Chaque groupe de paramètres peut avoir son propre préfixe de variable d'environnement et ses propres règles de validation.
L'approche du fichier .env fonctionne avec des plateformes de déploiement comme Heroku, AWS et Docker, où les variables d'environnement sont le moyen standard de configurer les applications. Votre application bénéficie d'une sécurité de type et d'une validation tout en suivant les modèles de configuration cloud-native que les équipes d'exploitation attendent.
Ces modèles constituent la base de la construction d'applications faciles à maintenir qui gèrent la complexité du monde réel. Le système de validation de Pydantic s'adapte à vos exigences spécifiques tout en fournissant des messages d'erreur clairs et une documentation automatique qui aide toute votre équipe à comprendre les structures de données attendues par votre application.
Conclusion
Vous avez maintenant vu comment Pydantic peut vous éviter le travail fastidieux d'écrire le code de validation à la main. Au lieu d'encombrer vos fonctions avec des vérificationsisinstance() et une gestion personnalisée des erreurs, vous pouvez définir votre structure de données une seule fois et laisser Pydantic s'occuper du reste.
N'oubliez pas de lire la section FAQ ci-dessous pour des questions plus spécifiques sur Pydantic. Pour une documentation complète et des modèles avancés, voici quelques ressources que vous pouvez utiliser :
La validation Pydantic ralentit-elle mon application ?⌄
Pydantic V2 est construit avec Rust sous le capot, ce qui le rend très rapide. Pour la plupart des applications, la surcharge de validation est minime par rapport aux bogues qu'elle permet d'éviter. Vous pouvez utiliser model_construct() pour ignorer la validation des sources de données fiables lorsque les performances sont critiques.
Quand dois-je utiliser Python au lieu des classes de données Python ?⌄
Utilisez Pydantic lorsque vous avez besoin d'une validation, que vous travaillez avec des sources de données externes ou que vous créez des API. Utilisez les classes de données pour les structures de données internes simples pour lesquelles vous avez confiance en l'entrée. Pydantic excelle lorsque les données proviennent des utilisateurs, des API ou des fichiers de configuration.
Comment gérer les erreurs de validation de Pydantic dans les applications de production ?⌄
Attrapez les exceptions ValidationError et extrayez la liste .errors() pour obtenir des informations structurées sur les erreurs. Chaque erreur contient les champs loc, msg et type qui vous aident à fournir des messages d'erreur conviviaux ou à enregistrer des informations de débogage détaillées.
Puis-je ajouter Pydantic à un code existant sans tout casser ?⌄
Oui ! Commencez par envelopper les points d'entrée de données externes (requêtes API, analyse de fichiers) avec des modèles Pydantic. Vous pouvez convertir progressivement les structures de données internes. Pydantic fonctionne avec le code existant et ne nécessite pas de modifier l'ensemble de votre base de code en une seule fois.
Quelle est l'erreur la plus fréquente lorsque l'on commence avec Pydantic ?⌄
Ne pas comprendre la coercition de type. Pydantic convertit par défaut les types compatibles (comme la chaîne "123" en entier 123). Si vous avez besoin d'une vérification stricte des types, utilisez Field(strict=True) ou définissez un mode strict dans la configuration de votre modèle pour empêcher les conversions automatiques.
Apprenez à utiliser JSON en Python, notamment la sérialisation, la désérialisation, le formatage, l'optimisation des performances, la gestion des API, ainsi que les limites et les alternatives de JSON.
Initiez-vous aux structures de données de Python : apprenez-en plus sur les types de données et les structures de données primitives et non primitives, telles que les chaînes de caractères, les listes, les piles, etc.
Découvrez les fonctions lambda Python, leur utilité et quand les utiliser. Comprend des exemples pratiques et des bonnes pratiques pour une mise en œuvre efficace.
Découvrez les fonctions et méthodes des listes Python. Veuillez suivre les exemples de code pour list() et d'autres fonctions et méthodes Python dès maintenant.
Pydantic est la bibliothèque de validation de données la plus populaire de Python, qui permet de transformer les indications de type en règles de validation à l'exécution.
Au lieu d'écrire des dizaines de if
isinstance()et des fonctions de validation personnalisées, vous définissez votre structure de données une seule fois en utilisant la syntaxe familière de Python.Pydantic se charge du reste : validation des données entrantes, conversion des types le cas échéant et fourniture de messages d'erreur clairs en cas d'échec de la validation.
Considérez cette simple fonction Python avec des indications de type et une documentation appropriée :
En regardant sa signature, vous savez exactement quel type de données il accepte et produit à l'autre bout. Mais pouvez-vous en dire autant de l'utilisateur final ? Après tout, ces indications de type ne servent qu'à des fins de documentation - si quelqu'un transmet un type de données incorrect, l'interpréteur de Python n'est jamais inquiété :
Il s'agit d'un exemple classique du typage dynamique de Python. Elle offre aux programmeurs un développement rapide et une grande liberté, mais au prix de l'introduction de problèmes de validation de type qui font souvent surface en production.
Dans ce tutoriel, je vais vous expliquer comment utiliser Pydantic pour des applications réelles. Nous commencerons par votre premier modèle de données et progresserons jusqu'aux modèles avancés utilisés dans les systèmes de production. Vous découvrirez comment valider des structures de données imbriquées, écrire des validateurs personnalisés pour des règles métier complexes, et intégrer Pydantic avec des frameworks populaires comme FastAPI et SQLAlchemy.
Qu'est-ce que Pydantic ?
Vous jouez essentiellement à la roulette des données lorsque vous créez des applications qui interagissent avec le monde extérieur. Les utilisateurs peuvent soumettre un numéro de téléphone sous forme de chaîne de caractères au lieu d'un nombre entier, une API peut renvoyer des valeurs nulles là où vous attendez des nombres, ou un fichier de configuration peut contenir des fautes de frappe qui interrompent votre application à 3 heures du matin.
Les approches traditionnelles de Python pour résoudre ce problème deviennent rapidement lourdes. Vous finissez par écrire un code de validation comme celui-ci :
Multipliez cela par chaque structure de données de votre application et vous passerez plus de temps à écrire du code de validation que de la logique d'entreprise.
Pydantic résout ce problème en combinant trois concepts puissants : les indications de type, la validation en cours d'exécution et la sérialisation automatique. Au lieu de procéder à des vérifications manuelles, vous définissez votre structure de données une seule fois en utilisant la syntaxe d'annotation de type de Python, et Python se charge automatiquement de toute la validation :
Caractéristiques de Pydantic
L'approche de Pydantic vous offre plusieurs avantages. La performance est le premier avantage que vous remarquerez - la logique de validation de base de Python est écrite en Rust, ce qui la rend plus rapide que la validation Python écrite à la main dans la plupart des cas. Votre application peut traiter des milliers de demandes sans que la validation ne devienne un goulot d'étranglement.
Pydantic permet également l'intégration avec des frameworks modernes. FastAPI, l'un des frameworks web Python à la croissance la plus rapide, utilise des modèles Python pour générer automatiquement la documentation de l'API, valider les corps des requêtes et sérialiser les réponses. Lorsque vous définissez un modèle Pydantic, vous bénéficiez gratuitement de la génération de schémas OpenAPI:
La génération de schémas JSON se fait automatiquement avec chaque modèle Pydantic. Cela signifie que vos structures de données s'auto-documentent et que vous pouvez générer des bibliothèques de clients, des règles de validation pour les applications frontales ou des schémas de base de données à partir de la même source de vérité.
Pydantique vs. Classes de données vs. Guimauve
Mais quand devriez-vous choisir Pydantic plutôt que d'autres solutions ? Si vous comparez Pydantic et les classes de données, la décision se résume aux besoins de validation. Le site
@dataclassde Python est parfait pour les conteneurs de données simples où vous faites confiance à l'entrée, mais Python excelle lorsque vous avez besoin de validation, de sérialisation et d'intégration avec des frameworks web :Si l'on compare Python et Marshmallow, l'approche de type hint de Python semble plus naturelle pour les développeurs Python, tandis que Marshmallow utilise des classes de schéma. Pydantic offre également de meilleures performances et une intégration plus facile avec les frameworks asynchrones modernes.
Pydantic fonctionne mieux lorsque vous construisez des API, traitez des données externes, gérez la configuration, ou tout autre scénario où l'échec de la validation des données doit être détecté tôt plutôt que de causer des bogues mystérieux plus tard. Il convertit les erreurs d'exécution en messages de validation clairs et exploitables qui aident les développeurs et les utilisateurs à comprendre ce qui n'a pas fonctionné.
Démarrer avec Pydantic
Nous allons voir comment utiliser Pydantic dans vos projets, de l'installation à la création de vos premiers modèles de données.
Installation et configuration
L'installation de Pydantic est simple, mais quelques écueils courants peuvent faire trébucher les nouveaux venus. Je vous guiderai à travers le processus d'installation approprié et vous aiderai à éviter les erreurs qui entraînent un débogage inutile.
Tout d'abord, créez un environnement virtuel pour isoler les dépendances de votre projet. Cela permet d'éviter les conflits de version qui peuvent entraîner de mystérieuses erreurs d'importation par la suite :
Maintenant, installez Pydantic avec la commande suivante :
Pour la fonctionnalité de validation des courriels, dont vous aurez probablement besoin dans des applications réelles (et dans ce tutoriel), installez les suppléments pour les courriels :
Les guillemets autour de
pydantic[email]sont importants dans presque tous les terminaux. Sans eux, vous risquez d'obtenir un message d'erreur "aucune correspondance trouvée".Note: Si vous rencontrez
ModuleNotFoundError: No module named 'pydantic', vérifiez les points suivants :Avertissement de dénomination critique: Ne nommez jamais votre fichier Python
pydantic.py. Cela crée une importation circulaire qui cassera votre code avec des messages d'erreur déroutants. Python essaiera d'importer votre fichier au lieu de la bibliothèque Python actuelle.Votre premier modèle pydantique
Construisons un modèle utilisateur simple pour voir Pydantic en action. Au lieu de commencer par des exemples abstraits, nous allons résoudre un problème réel : valider les données d'enregistrement d'un utilisateur à partir d'un formulaire web.
Sortie :
Décortiquons ce qui se passe dans cette définition de modèle :
Userhérite deBaseModel, ce qui lui confère toutes les capacités de validation et de sérialisation de Pydantic. Cet héritage transforme une classe Python ordinaire en un outil de validation de données.name: strindique à Pydantic quenamedoit être une chaîne de caractères,age: intsignifie que l'âge doit être un nombre entier, et ainsi de suite.EmailStrest un type spécial de Pydantic qui valide automatiquement les adresses électroniques. Il provient du paquetpydantic[email]que vous avez installé précédemment et utilise des expressions régulières pour s'assurer que le format de l'e-mail est valide. Si quelqu'un passe "not-an-email", Pydantic lèvera une erreur de validation.is_active: bool = Trueont des valeurs par défaut. Si vous ne fournissez pas ces champs lors de la création d'un utilisateur, Pydantic utilise les valeurs par défaut. Le champ= Nonepourcreated_atest facultatif.User(**clean_data),**décompresse votre dictionnaire et transmet chaque paire clé-valeur en tant qu'argument de mot-clé au constructeur du modèle.Voyons maintenant la conversion automatique des types de Pydantic en action :
Voyons maintenant la conversion automatique des types de Pydantic en action :
Sortie :
Comme vous pouvez le constater, les champs
ageetis_inactivesont automatiquement convertis dans leur format par la validation.En cas d'échec de la validation, Pydantic fournit des messages d'erreur clairs :
Sortie :
BaseModel vs. classes de données
Comprendre quand utiliser
BaseModelde Python ou@dataclass de Python vous aide à choisir le bon outil pour chaque situation.Les classes de données Python sont parfaites pour les conteneurs de données simples dont vous contrôlez l'entrée :
Les modèles pydantiques ajoutent la validation, la sérialisation et l'intégration du cadre :
Quand choisir chaque approche ?
Pydantic ajoute une certaine surcharge par rapport aux classes de données, mais ce coût est généralement négligeable par rapport aux bogues qu'il permet d'éviter et au temps de développement qu'il permet d'économiser. Pour les applications web, l'intégration automatique avec des frameworks comme FastAPI fait de Pydantic un choix évident.
Les fonctions de validation et de sérialisation deviennent de plus en plus utiles au fur et à mesure que votre application se développe. En commençant par les modèles Pydantic, vous disposez d'une base solide qui s'adapte à vos besoins.
Construire des modèles de données avec Pydantic
Maintenant que vous avez compris les principes de base, abordons les défis auxquels vous serez confronté lors de la création d'applications prêtes à être produites.
Validation du champ et contraintes
Prenons l'exemple d'une API de catalogue de produits dans laquelle les données de prix proviennent de plusieurs fournisseurs appliquant des normes de formatage différentes. Certains envoient des prix sous forme de chaînes, d'autres sous forme de flotteurs et, occasionnellement, quelqu'un envoie un prix négatif qui fait planter votre système de facturation.
La fonction
Field()de Pydantic transforme les indications de type de base en règles de validation sophistiquées qui protègent votre application :Chaque paramètre de
Field()a une fonction spécifique :min_lengthetmax_lengthempêchent les violations du schéma de la base de données,gtetlecréent des limites à la logique d'entreprise etpatternvalide les données formatées à l'aide d'expressions régulières. La syntaxeField(...)avec des points de suspension marque les champs obligatoires, tandis queField(None, ...)crée des champs facultatifs avec des règles de validation.Coercition de type contre validation stricte
Par défaut, Pydantic convertit les types compatibles plutôt que de les rejeter. Cette flexibilité fonctionne bien pour les entrées utilisateur, mais certains scénarios exigent une correspondance exacte des types :
Le dictionnaire
model_configcontrôle le comportement de validation dans l'ensemble de votre modèle. L'optionstr_strip_whitespacenettoie automatiquement les chaînes de caractères saisies, tandis que l'optionvalidate_assignmentgarantit que les modifications apportées aux champs après la création du modèle déclenchent toujours la validation. Les champs individuels peuvent remplacer ces paramètres à l'aide deField(strict=True)pour les situations nécessitant une correspondance exacte des types, comme les calculs financiers ou les données scientifiques.Modèles imbriqués et données complexes
Les applications réelles gèrent des structures de données complexes et interconnectées. Une commande de commerce électronique contient des informations sur le client, des adresses de livraison et plusieurs produits, chacun nécessitant sa propre validation :
Pydantic valide les structures imbriquées de manière récursive - le champ
Customerdevient un objetCustomercomplet, qui valide son propre champAddress. La syntaxeList[OrderItem]valide chaque élément de la liste en tant queOrderItem, tandis queField(min_items=1)empêche les commandes vides d'atteindre votre système d'inventaire. L'utilisation dedefault_factory=datetime.nowpermet de créer des horodatages uniques pour chaque commande.Champs facultatifs et gestion de l'absence de champs
Les besoins en données varient selon les opérations. La création par l'utilisateur exige des informations complètes, tandis que les mises à jour doivent accepter des changements partiels :
Sérialisation convertit les objets Pydantic en dictionnaires ou en chaînes JSON pour le stockage ou la transmission. La méthode
model_dump()se charge de cette conversion,exclude_none=Truesupprimant les champs non fournis. Ce modèle fonctionne parfaitement pour les requêtes PATCH dans lesquelles les clients n'envoient que les champs qu'ils souhaitent modifier, ce qui permet d'éviter les écrasements accidentels de données dans votre base de données.Cette base vous prépare à relever le prochain défi : mettre en œuvre une logique de validation personnalisée qui tient compte des règles de gestion propres à votre application.
Validation personnalisée et intégration dans le monde réel
La création d'applications de production implique la manipulation de données qui ne correspondent pas aux modèles de contrôle de type standard.
Pensez à un formulaire d'enregistrement d'utilisateur dont les exigences en matière de mot de passe varient en fonction des plans d'abonnement, ou à une API qui reçoit des données d'adresses de plusieurs pays avec différents formats de codes postaux. Ces scénarios requièrent une logique de validation personnalisée qui intègre vos règles commerciales spécifiques tout en s'intégrant harmonieusement aux cadres web et aux systèmes de configuration.
Cette section vous montre comment mettre en œuvre des modèles de validation personnalisés pratiques, intégrer les modèles Pydantic avec FastAPI pour la documentation automatique de l'API, et gérer les paramètres de l'application par le biais de variables d'environnement en utilisant l' approche du fichier
.envsur laquelle s'appuient la plupart des applications de production.Valideurs de champs et validation de modèles
Lorsque la logique métier détermine la validité des données, le décorateur
@field_validatorde Pydantic transforme vos fonctions de validation en une partie du modèle lui-même. Prenons l'exemple d'un système d'enregistrement des utilisateurs dans lequel les différents niveaux d'abonnement ont des exigences différentes en matière de mot de passe :Le décorateur
@field_validatorvous permet d'accéder aux valeurs d'autres champs par l'intermédiaire du paramètreinfo.data, ce qui permet d'appliquer des règles de validation dépendant de plusieurs champs. Le validateur s'exécute après la vérification de base des types, de sorte que vous pouvez supposer quesubscription_tierest l'une des valeurs autorisées.Pour la validation de plusieurs champs, le décorateur
@model_validators'exécute après la validation de tous les champs individuels :Le paramètre
mode='after'garantit que le validateur reçoit une instance de modèle entièrement construite, ce qui est parfait pour la logique métier qui nécessite l'accès à tous les champs validés. Le validateur doit renvoyerselfpour indiquer que la validation est réussie.Intégration FastAPI
L'intégration de FastAPI avec Pydantic crée une validation automatique des requêtes et une documentation de l'API. Le modèle clé consiste à créer des modèles distincts pour les différentes opérations, ce qui vous permet de contrôler les données qui circulent dans chaque direction :
La séparation entre les modèles d'entrée et de sortie présente plusieurs avantages. Les modèles d'entrée peuvent inclure des règles de validation et des champs obligatoires, tandis que les modèles de sortie contrôlent exactement les données envoyées aux clients. FastAPI génère automatiquement de la documentation OpenAPI à partir de vos modèles Pydantic, créant ainsi des documents API interactifs que les développeurs peuvent utiliser pour tester les points de terminaison.
Pour les opérations de mise à jour, vous pouvez créer des modèles dont tous les champs sont facultatifs :
Le paramètre
exclude_unset=Truedans les opérations PATCH garantit que vous ne mettez à jour que les champs qui ont été explicitement fournis, évitant ainsi les écrasements accidentels. Ce modèle fonctionne parfaitement pour les API REST où les clients envoient des mises à jour partielles.Gestion de la configuration avec les variables d'environnement
Les applications de production ont besoin d'une gestion de la configuration sécurisée et facile à déployer. Le site
BaseSettingsde Pydantic, combiné aux fichiers.env, permet une configuration sûre qui fonctionne dans les environnements de développement, d'essai et de production.Tout d'abord, créez un fichier
.envà la racine de votre projet :Définissez ensuite votre modèle de paramétrage :
La classe
BaseSettingslit automatiquement les variables d'environnement, les fichiers.envet les arguments de la ligne de commande. Les variables d'environnement ont la priorité sur les valeurs du fichier.env, ce qui permet de remplacer facilement les paramètres dans différents environnements de déploiement. Le paramètrecase_sensitive = Falsepermet d'attribuer des noms flexibles aux variables d'environnement.Pour les applications complexes, vous pouvez organiser les paramètres en groupes logiques :
Cette approche imbriquée permet de conserver les paramètres connexes ensemble tout en maintenant une séparation claire entre les différents composants de votre application. Chaque groupe de paramètres peut avoir son propre préfixe de variable d'environnement et ses propres règles de validation.
L'approche du fichier
.envfonctionne avec des plateformes de déploiement comme Heroku, AWS et Docker, où les variables d'environnement sont le moyen standard de configurer les applications. Votre application bénéficie d'une sécurité de type et d'une validation tout en suivant les modèles de configuration cloud-native que les équipes d'exploitation attendent.Ces modèles constituent la base de la construction d'applications faciles à maintenir qui gèrent la complexité du monde réel. Le système de validation de Pydantic s'adapte à vos exigences spécifiques tout en fournissant des messages d'erreur clairs et une documentation automatique qui aide toute votre équipe à comprendre les structures de données attendues par votre application.
Conclusion
Vous avez maintenant vu comment Pydantic peut vous éviter le travail fastidieux d'écrire le code de validation à la main. Au lieu d'encombrer vos fonctions avec des vérifications
isinstance()et une gestion personnalisée des erreurs, vous pouvez définir votre structure de données une seule fois et laisser Pydantic s'occuper du reste.N'oubliez pas de lire la section FAQ ci-dessous pour des questions plus spécifiques sur Pydantic. Pour une documentation complète et des modèles avancés, voici quelques ressources que vous pouvez utiliser :