Skip to content

Chapitre 3.3 : Modèles de données avec Pydantic

Temps d'étude : 50 minutes


1. Pydantic : Le "plan numérique" d'un vaisseau spatial

Imaginez que vous construisez un vaisseau spatial. Vous ne pouvez pas simplement souder des morceaux de métal au hasard. Vous avez besoin d'un plan détaillé qui définit :

  • Nom du vaisseau (type : chaîne, longueur max : 50 caractères)
  • Année de lancement (type : entier)
  • Présence d'un hyperpropulseur (type : oui/non)

Pydantic est une bibliothèque qui permet de créer de tels "plans numériques" pour vos données en Python. Dans FastAPI, elle remplit trois fonctions clés :

  1. Déclaration de structure : Décrit clairement de quels champs vos données sont composées.
  2. Validation des données : Vérifie automatiquement si les données entrantes correspondent au plan.
  3. Documentation : FastAPI utilise ces plans pour générer une documentation détaillée et interactive.

💡 Analogie spatiale : Un modèle Pydantic est le passeport technique d'un objet. Toute "cargaison" (données) arrivant à la station doit correspondre aux spécifications du passeport. Sinon, l'ordinateur de bord (Pydantic) la rejettera.


2. Création du premier plan : Le modèle Spaceship

Créons un modèle qui décrira notre engin spatial.

Étape 1 : Importation de BaseModel depuis Pydantic Pydantic est déjà installé avec fastapi[all]. Nous avons seulement besoin d'importer la classe de base pour nos modèles.

Ajoutez ceci en haut de main.py, à côté des autres importations :

# main.py
from fastapi import FastAPI
from pydantic import BaseModel

Étape 2 : Description du modèle Spaceship Créez une classe qui hérite de BaseModel. À l'intérieur de la classe, définissez les champs et leurs types en utilisant les annotations de type Python standard.

Ajoutez ce code dans main.py (peut être après les importations) :

class Spaceship(BaseModel):
    """
    Passeport technique (modèle) d'un vaisseau spatial.
    """
    name: str
    type: str
    launch_year: int
    status: str
Et voilà ! Vous venez de créer un "plan". Pydantic sait maintenant que tout objet de type Spaceship doit avoir quatre champs avec les types spécifiés.


3. Application du modèle : Amélioration de nos points d'API

Utilisons maintenant notre nouveau modèle pour rendre l'API "plus intelligente".

A. Modèle en tant que réponse (Response Model) Nous pouvons indiquer à FastAPI que notre point d'API doit retourner des données correspondant au modèle Spaceship. Cela garantit que la réponse aura toujours la bonne structure.

Modifiez le point d'API /spaceships/{ship_id} comme suit :

# main.py

# ... code avec db_spaceships et le modèle Spaceship ...

# Utilisation de `response_model` pour spécifier le "plan" de la réponse
@app.get("/spaceships/{ship_id}", response_model=Spaceship)
def get_spaceship(ship_id: int):
    """
    Retourne les données du vaisseau, correspondant au modèle Spaceship.
    """
    ship = db_spaceships.get(ship_id)
    return ship
- response_model=Spaceship : Nous disons à FastAPI : "La réponse de cette fonction doit correspondre à la structure Spaceship. Filtre tous les champs superflus et assure-toi que les types sont corrects."

Qu'est-ce que cela apporte ?

  • Filtrage des données : Si db_spaceships contenait des champs superflus (par exemple, "secret_code"), ils ne seraient pas inclus dans le JSON final.
  • Garantie de structure : Le client de l'API peut être sûr de toujours recevoir une réponse dans le format attendu.
  • Documentation : Dans /docs, un exemple précis de réponse (Example Value) sera désormais affiché.

B. Modèles pour les collections Et qu'en est-il du point d'API /spaceships qui retourne une liste de vaisseaux ? Pour cela, il faut utiliser list du module typing.

Modifiez les importations et le point d'API /spaceships :

# main.py en haut
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List  # <-- Importation de List

# ... code ...

# Indique que la réponse est une liste (List) d'objets de type Spaceship
@app.get("/spaceships", response_model=List[Spaceship])
def get_spaceships():
    """
    Retourne une liste de vaisseaux. Chaque élément de la liste
    est validé selon le modèle Spaceship.
    """
    # Pydantic ne peut pas fonctionner avec un dictionnaire dont les clés sont des ID.
    # Convertissons notre dictionnaire en une simple liste.
    return list(db_spaceships.values())

  • response_model=List[Spaceship] : Nous indiquons que la réponse sera une liste, où chaque élément est un objet correspondant au modèle Spaceship.
  • return list(db_spaceships.values()) : Changement important ! Pydantic s'attend à un objet itérable (une liste), et non à un dictionnaire où les clés sont des ID. Nous convertissons les valeurs de notre "simulateur de base de données" en une liste.

4. Vérification de l'API améliorée

Assurez-vous que le serveur uvicorn est lancé avec --reload.

  1. Vérifiez http://127.0.0.1:8000/spaceships : La réponse est maintenant un tableau JSON, et non un objet. C'est une structure plus correcte et standard pour les collections.
    [
      { "name": "Voyager-1", "type": "Sonde", ... },
      { "name": "Hubble Space Telescope", ... }
    ]
    
  2. Vérifiez http://127.0.0.1:8000/spaceships/1 : La réponse n'a pas changé, mais elle est maintenant garantie de correspondre au modèle.
  3. Consultez /docs : Dans la section "Schemas" en bas de page, votre modèle Spaceship est apparu. Et dans les exemples de réponses pour les points d'API, un beau schéma de données structuré est maintenant affiché.

5. Validation avancée : L'"ordinateur de bord" en action

Pydantic peut faire bien plus que simplement vérifier les types.

Ajoutons la validation à notre modèle Spaceship :

from pydantic import BaseModel, Field

class Spaceship(BaseModel):
    name: str = Field(..., min_length=3, max_length=50, description="Nom du vaisseau")
    type: str
    launch_year: int = Field(..., gt=1950, description="L'année de lancement doit être après 1950")
    status: str

  • Field(...) : Utilisé pour ajouter des règles de validation supplémentaires.
  • ... (Ellipsis) : Indique que le champ est obligatoire.
  • min_length, max_length : Contraintes pour la chaîne de caractères.
  • gt : "Greater Than" (plus grand que).

Bien que nous ne créions pas encore de nouveaux vaisseaux (ce sera dans le prochain chapitre), ces règles seront déjà reflétées dans la documentation et s'appliqueront lorsque nous implémenterons les requêtes POST.


Quiz pour la consolidation

1. Pydantic dans FastAPI est utilisé pour...

2. Pour créer un modèle de données, il faut hériter d'une classe de...

3. Le paramètre `response_model` dans le décorateur `@app.get` est nécessaire pour...

4. Comment indiquer qu'un point d'API retourne une *liste* d'objets de type `Item` ?

5. `Field(..., gt=0)` dans un modèle Pydantic signifie que le champ...


🚀 Résumé du chapitre :

Vous avez conçu des "plans numériques" pour les données de votre API. Maintenant, elle ne fonctionne pas seulement, elle fonctionne de manière prévisible et fiable.

  • 📝 Le modèle Spaceship a été créé à l'aide de Pydantic.
  • 🛡️ L'API valide et filtre désormais les données sortantes à l'aide de response_model.
  • 📊 La documentation est devenue beaucoup plus informative, affichant des schémas de données précis.

Les plans sont prêts et approuvés ! Dans le prochain chapitre, nous passerons de la lecture des données à leur création — nous implémenterons des opérations CRUD complètes pour notre flotte.

📌 Vérification :

  • Assurez-vous que le schéma du modèle Spaceship est apparu dans /docs.
  • Vérifiez que le point d'API /spaceships retourne désormais un tableau JSON ([...]), et non un objet ({...}).
  • Assurez-vous qu'il n'y a pas d'erreurs de syntaxe dans le code après l'ajout des modèles.

⚠️ En cas d'erreurs : - NameError: name 'BaseModel' is not defined: Vérifiez que vous avez importé BaseModel depuis pydantic. - NameError: name 'List' is not defined: Vérifiez que vous avez importé List depuis typing. - La réponse à /spaceships est vide ([]) : Assurez-vous d'avoir changé return db_spaceships en return list(db_spaceships.values()).