Cours
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 :
def calculate_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.15
elif is_premium_member:
base_discount = 0.10
else:
base_discount = 0.05
return purchase_amount * base_discount
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)
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 :
def create_user(data):
# Manual validation nightmare
if not isinstance(data.get('age'), int):
raise ValueError("Age must be an integer")
if data['age'] < 0 or data['age'] > 150:
raise ValueError("Age must be between 0 and 150")
if not isinstance(data.get('email'), str) or '@' not in data['email']:
raise ValueError("Invalid email format")
if not isinstance(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'])
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
class User(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": "john@example.com",
"is_active": "true" # String gets converted to bool
}
user = User(**user_data)
print(user.age) # 25 (as integer)
print(user.model_dump()) # Clean dictionary output
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()
class UserCreate(BaseModel):
name: str
email: EmailStr
age: int
@app.post("/users/")
async def create_user(user: UserCreate):
# FastAPI automatically validates the request body
# and generates API docs from your Pydantic model
return {"message": f"Created user {user.name}"}
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
@dataclass
class UserDataclass:
name: str
age: int
# Pydantic: validation, serialization, framework integration
class UserPydantic(BaseModel):
name: str
age: int
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 Windows
Maintenant, installez Pydantic avec la commande suivante :
pip install pydantic
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 :
pip install "pydantic[email]"
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
class User(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": "alice@example.com",
"age": 28
}
user = User(**clean_data)
print(f"User created: {user.name}, Age: {user.age}")
print(f"Model output: {user.model_dump()}")
Sortie :
User created: Alice Johnson, Age: 28
Model output: {'name': 'Alice Johnson', 'email': 'alice@example.com', 'age': 28, 'is_active': True, 'created_at': None}
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 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. - Définitions des champs: Chaque ligne de la classe définit un champ avec son type attendu. La syntaxe
name: str
indique à Pydantic quename
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 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. - 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
pourcreated_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": "bob@company.com",
"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'>
Voyons maintenant la conversion automatique des types de Pydantic en action :
# Messy data that still works
messy_data = {
"name": "Bob Smith",
"email": "bob@company.com",
"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'>
Sortie :
Age type: <class 'int'>
Is active type: <class 'bool'>
Comme vous pouvez le constater, les champs age
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)
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]
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
@dataclass
class ProductDataclass:
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")
Les modèles pydantiques ajoutent la validation, la sérialisation et l'intégration du cadre :
from pydantic import BaseModel, Field
class ProductPydantic(BaseModel):
name: str = Field(min_length=1)
price: float = Field(gt=0) # Must be greater than 0
in_stock: bool
# Automatic validation prevents bad data
try:
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
)
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 fonction Field()
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
class Product(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 messages
try:
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")
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 syntaxe Field(...)
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 coercion
class FlexibleOrder(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 matters
class StrictOrder(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)
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
class Address(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"
class Customer(BaseModel):
name: str = Field(min_length=1)
email: EmailStr
shipping_address: Address
billing_address: Optional[Address] = None
class OrderItem(BaseModel):
product_id: int = Field(gt=0)
quantity: int = Field(gt=0, le=100)
unit_price: Decimal = Field(gt=0)
class Order(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": "john@example.com",
"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")
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
class UserCreate(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}$')
class UserUpdate(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())}")
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
class UserRegistration(BaseModel):
username: str = Field(min_length=3)
email: EmailStr
password: str
subscription_tier: str = Field(pattern=r'^(free|pro|enterprise)$')
@field_validator('password')
@classmethod
def validate_password_complexity(cls, password, info):
tier = info.data.get('subscription_tier', 'free')
if len(password) < 8:
raise ValueError('Password must be at least 8 characters')
if tier == 'enterprise' and not re.search(r'[A-Z]', password):
raise ValueError('Enterprise accounts require uppercase letters')
return password
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
class EventRegistration(BaseModel):
start_date: datetime
end_date: datetime
max_attendees: int = Field(gt=0)
current_attendees: int = Field(ge=0)
@model_validator(mode='after')
def validate_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 self
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()
class UserCreate(BaseModel):
username: str = Field(min_length=3)
email: EmailStr
password: str = Field(min_length=8)
class UserResponse(BaseModel):
id: int
username: str
email: EmailStr
created_at: datetime
@app.post("/users/", response_model=UserResponse)
async def create_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)
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 :
class UserUpdate(BaseModel):
username: Optional[str] = Field(None, min_length=3)
email: Optional[EmailStr] = None
@app.patch("/users/{user_id}")
async def update_user(user_id: int, user_update: UserUpdate):
# Only update provided fields
update_data = user_update.model_dump(exclude_unset=True)
# Your database update logic here
return {"message": f"Updated user {user_id}"}
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 site BaseSettings
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 file
DATABASE_URL=postgresql://user:password@localhost:5432/myapp
SECRET_KEY=your-secret-key-here
DEBUG=false
ALLOWED_HOSTS=localhost,127.0.0.1,yourdomain.com
Définissez ensuite votre modèle de paramétrage :
from pydantic import BaseSettings, Field
from typing import List
class AppSettings(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"])
class Config:
env_file = ".env"
case_sensitive = False
# Load settings automatically from environment and .env file
settings = AppSettings()
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ètre case_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 :
class DatabaseSettings(BaseSettings):
url: str = Field(env="DATABASE_URL")
max_connections: int = Field(default=5, env="DB_MAX_CONNECTIONS")
class AppSettings(BaseSettings):
secret_key: str
debug: bool = False
database: DatabaseSettings = DatabaseSettings()
class Config:
env_file = ".env"
settings = AppSettings()
# Access nested configuration
db_url = settings.database.url
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é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 :
- Documentation officielle :
- Documentation Pydantic- Référence complète avec des exemples et des bonnes pratiques
- Guide de migration Pydantic- Indispensable pour passer d'une version à l'autre
- Documentation FastAPI- Découvrez comment Pydantic alimente la documentation automatique des API.
- Ressources d'apprentissage DataCamp associées :
- Validation de modèles en Python- Plongée dans les techniques de validation avec scikit-learn
- Introduction à FastAPI- Construire des API de production avec l'intégration Pydantic
- Introduction aux API en Python- Travailler avec des API externes et la validation de données JSON
- Python intermédiaire- Maîtrisez les bases de Python qui complètent l'utilisation de Python.
- Programmation orientée objet en Python- Construire les bases de la compréhension des modèles pydantiques
FAQ
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.

Je suis un créateur de contenu en science des données avec plus de 2 ans d'expérience et l'un des plus grands followings sur Medium. J'aime écrire des articles détaillés sur l'IA et la ML dans un style un peu sarcastıc, car il faut bien faire quelque chose pour les rendre un peu moins ennuyeux. J'ai produit plus de 130 articles et un cours DataCamp, et un autre est en cours d'élaboration. Mon contenu a été vu par plus de 5 millions de personnes, dont 20 000 sont devenues des adeptes sur Medium et LinkedIn.