0% ont trouvé ce document utile (0 vote)
123 vues118 pages

API REST en Python Avec Flask

Transféré par

Hassam
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats ODT, PDF, TXT ou lisez en ligne sur Scribd
0% ont trouvé ce document utile (0 vote)
123 vues118 pages

API REST en Python Avec Flask

Transféré par

Hassam
Copyright
© © All Rights Reserved
Nous prenons très au sérieux les droits relatifs au contenu. Si vous pensez qu’il s’agit de votre contenu, signalez une atteinte au droit d’auteur ici.
Formats disponibles
Téléchargez aux formats ODT, PDF, TXT ou lisez en ligne sur Scribd

API REST en Python avec Flask, Connexion et

SQLAlchemy - Partie 1

Table des matières

Démo

Planification de la première partie

Mise en route

 Créer un environnement virtuel

 Ajouter des dépendances

 Lancer votre projet Flask

Ajout de votre premier point de terminaison d'API REST

 Créer le fichier de configuration de l'API

 Ajouter Connexion à l'application

 Retourner les données de votre point de terminaison de l'API

 Explorez la documentation de votre API

Construire l'API complète

 Travailler avec des composants

 Créer une nouvelle personne

 Traiter une personne

 Explorez la documentation complète de votre API


Conclusion

La plupart des applications Web modernes sont alimentées par une API REST
sous le capot. Ainsi, les développeurs peuvent séparer le code front end de la
logique dorsale, et les utilisateurs peuvent interagir avec l'interface de manière
dynamique. Dans cette série de didacticiels en trois parties, vous allez créer une
API REST avec le framework Web Flask.

Vous créerez une base avec un projet Flask de base, puis vous ajouterez des
points d'extrémité et les connecterez à une base de données SQLite. Vous
testerez votre API à l'aide de la documentation Swagger UI API que vous
construirez en cours de route.

Dans la première partie de cette série de tutoriels, vous apprendrez à :

 Construire un projet Flask de base avec une API REST

 Traiter les demandes HTTP avec Connexion

 Définir des points de terminaison d'API à l'aide de la spécification


OpenAPI

 Interagir avec votre API pour gérer les données

 Créer une documentation d'API avec Swagger UI

Après avoir terminé la première partie de cette série, vous passerez à la


deuxième partie, où vous apprendrez à utiliser une base de données appropriée
pour stocker vos données de façon permanente au lieu de vous fier au stockage
en mémoire.
Cette série de tutoriels est un guide pratique sur la façon de créer une API REST
avec Flask et d'interagir avec elle en utilisant des opérations CRUD (CRUD
operations). Si vous souhaitez rafraîchir vos connaissances sur le travail avec les
API, vous pouvez lire Python and REST APIs : Interacting With Web Services
( Python and REST APIs: Interacting With Web Services ).

Vous pouvez télécharger le code de la première partie de ce projet en cliquant


sur le lien ci-dessous :

Click here to download the free source code

Démo
Dans cette série de didacticiels en trois parties, vous allez construire une API
REST pour conserver la trace des notes des personnes qui peuvent vous rendre
visite tout au long de l'année. Dans ce tutoriel, vous créerez des personnes
comme la fée des dents, le lapin de Pâques et Knecht Ruprecht.

Idéalement, tu veux être en bons termes avec ces trois personnes. C'est pourquoi
vous leur enverrez des notes, afin d'augmenter vos chances d'obtenir des
cadeaux de valeur de leur part.

Vous pouvez interagir avec votre application en vous appuyant sur la


documentation de l'API. En cours de route, vous construirez un front end de
base qui reflète le contenu de votre base de données :
Dans la première partie de cette série, vous allez créer un projet Flask de base et
brancher vos premiers points de terminaison API. À la fin de cette partie, vous
serez en mesure de voir une liste de personnes dans le front-end et de gérer
chaque personne dans le back-end :

En tirant parti de l'interface utilisateur Swagger, vous créerez une documentation


pratique pour votre API en cours de route. Ainsi, vous aurez la possibilité de
tester le fonctionnement de votre API à chaque étape de ce tutoriel et d'obtenir
un aperçu utile de tous vos points de terminaison.

Planification de la première partie


En plus de construire les fondations du projet Flask, vous allez créer une API
REST qui donne accès à une collection de personnes et aux individus de cette
collection. Voici la conception de l'API pour la collection de personnes :

Action HTTP Verb URL Path Description


Read GET /api/people Read a collection of people.
Create POST /api/people Create a new person.
Read GET /api/people/<lname> Read a particular person.
Update PUT /api/people/<lname> Update an existing person.
Delete DELETE /api/people/<lname> Delete an existing person.

L'API REST que vous allez créer servira une structure de données de personnes
simple où les personnes sont associées au nom de famille et où toute mise à jour
est marquée par un nouvel horodatage.

L'ensemble de données avec lequel vous allez travailler ressemble à ceci :


PEOPLE = {
"Fairy": {
"fname": "Tooth",
"lname": "Fairy",
"timestamp": "2022-10-08 09:15:10",
},
"Ruprecht": {
"fname": "Knecht",
"lname": "Ruprecht",
"timestamp": "2022-10-08 09:15:13",
},
"Bunny": {
"fname": "Easter",
"lname": "Bunny",
"timestamp": "2022-10-08 09:15:27",
}
}
L'un des objectifs d'une API est de découpler les données de l'application qui les
utilise, ce qui permet de masquer les détails de la mise en œuvre des données.
Plus tard dans cette série de tutoriels, vous enregistrerez vos données dans une
base de données. Mais pour commencer, une structure de données en mémoire
convient parfaitement.

Pour commencer
Dans cette section, vous allez préparer l'environnement de développement pour
votre projet d'API REST Flask. Tout d'abord, vous allez créer un environnement
virtuel et installer toutes les dépendances dont vous avez besoin pour votre
projet.

Créer un environnement virtuel

Dans cette section, vous allez construire la structure de votre projet. Vous
pouvez nommer le dossier racine de votre projet comme vous le souhaitez. Par
exemple, vous pouvez le nommer rp_flask_api/. Créez le dossier et naviguez
dedans :

$ mkdir rp_flask_api
$ cd rp_flask_api

Dans ce cas, vous nommez le dossier racine de votre projet rp_flask_api/. Les
fichiers et dossiers que vous créerez au cours de cette série seront situés dans ce
dossier ou dans ses sous-dossiers.

Après avoir accédé au dossier du projet, il est bon de créer et d'activer un


environnement virtuel. De cette façon, vous installerez les dépendances du
projet non pas sur l'ensemble du système, mais uniquement dans
l'environnement virtuel de votre projet (virtual environment).

Sélectionnez votre système d'exploitation ci-dessous et utilisez la commande


spécifique à votre plate-forme pour configurer un environnement virtuel :

Windows
PS> python -m venv venv
PS> .\venv\Scripts\activate
(venv) PS>

Linux
$ python -m venv venv
$ source venv/Scripts/activate
(venv) $

Avec les commandes présentées ci-dessus, vous créez et activez un


environnement virtuel nommé venv en utilisant le module venv intégré de
Python. Les parenthèses (venv) devant l'invite indiquent que vous avez activé
l'environnement virtuel avec succès.

Ajouter les dépendances


Après avoir créé et activé votre environnement virtuel, il est temps d'installer
Flask avec pip (pip):

(venv) $ python -m pip install Flask==2.2.2

Le framework micro web Flask est la principale dépendance dont votre projet a
besoin. En plus de Flask, installez Connexion pour gérer les requêtes HTTP :

(venv) $ python -m pip install "connexion[swagger-


ui]==2.14.1"

Pour utiliser également la documentation API générée automatiquement, vous


installez Connexion (Connexion ) avec la prise en charge supplémentaire de
Swagger UI (Swagger UI). Plus tard dans ce tutoriel, vous en saurez plus sur les
paquets Python que vous venez d'installer (Python packages).

Initiez votre projet Flask


Le fichier principal de votre projet Flask sera app.py. Créez app.py dans
rp_flask_api/ et ajoutez le contenu suivant :

# app.py

from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")
def home():
return render_template("home.html")

if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, debug=True)

Vous importez le module Flask, donnant à l'application l'accès aux


fonctionnalités de Flask. Vous créez ensuite une instance d'application Flask
nommée app. Ensuite, vous connectez la route URL "/" à la fonction home() en
la décorant (decorating ) avec @app.route("/"). Cette fonction appelle la
fonction Flask render_template() pour récupérer le fichier home.html dans le
répertoire templates et le renvoyer au navigateur.

En bref, ce code fait fonctionner un serveur web de base et lui fait répondre avec
un modèle home.html, qui sera servi au navigateur lorsqu'il naviguera vers
l'URL "/".
Remarque : le serveur de développement de Flask utilise par défaut le port 5000.
Sur les nouvelles versions de macOS, ce port est déjà utilisé par le récepteur
AirPlay de macOS. Ci-dessus, vous avez changé le port de votre application
Flask avec port=8000. Si vous le souhaitez, vous pouvez modifier les
préférences du récepteur AirPlay sur votre Mac.

Flask attend home.html dans un répertoire de modèles nommé templates/. Créez

le répertoire templates/ et ajoutez home.html :

<!-- templates/home.html -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RP Flask REST API</title>
</head>
<body>
<h1>
Hello, World!
</h1>
</body>
</html>

Flask est livré avec le moteur de template Jinja (Jinja Templating Engine), qui
vous permet d'améliorer vos templates. Mais votre modèle home.html est un
fichier HTML ( HTML file) de base sans aucune fonctionnalité Jinja. Ce n'est
pas grave pour l'instant, car le but de home.html est de vérifier que votre projet
Flask répond comme prévu.
Avec l'environnement virtuel Python actif, vous pouvez exécuter votre
application avec cette ligne de commande dans le répertoire contenant le fichier
app.py :

(venv) $ python app.py


Lorsque vous exécutez app.py, un serveur web démarre sur le port 8000. Si vous
ouvrez un navigateur et naviguez vers http://localhost:8000, vous devriez voir

Hello, World ! affiché :

Félicitations, votre serveur web est en marche ! Vous étendrez le fichier


home.html plus tard pour travailler avec l'API REST que vous développez.

A présent, la structure de votre projet Flask devrait ressembler à ceci :

rp_flask_api/

├── templates/
│ └── home.html

└── app.py
Il s'agit d'une structure idéale pour démarrer tout projet Flask. Vous constaterez
que le code source vous sera utile lorsque vous travaillerez sur de futurs projets.
Vous pouvez le télécharger ici :

Dans les sections suivantes, vous allez développer le projet et ajouter vos
premiers points de terminaison d'API REST.

Ajout de votre premier point de terminaison d'API REST


Maintenant que vous disposez d'un serveur Web fonctionnel, vous pouvez
ajouter votre premier point de terminaison API REST. Pour ce faire, vous
utiliserez Connexion, que vous avez installé dans la section précédente.

Le module Connexion permet à un programme Python d'utiliser la spécification


OpenAPI avec Swagger. La spécification OpenAPI est un format de description
d'API pour les API REST et fournit de nombreuses fonctionnalités, notamment :

 La validation des données d'entrée et de sortie vers et depuis votre API.

 la configuration des points de terminaison de l'URL de l'API et des


paramètres attendus.

Lorsque vous utilisez OpenAPI avec Swagger, vous pouvez créer une interface
utilisateur (IU) pour explorer l'API. Tout cela peut se produire lorsque vous
créez un fichier de configuration auquel votre application Flask peut accéder.

Créer le fichier de configuration de l'API


Le fichier de configuration Swagger est un fichier YAML ou JSON contenant
vos définitions OpenAPI. Ce fichier contient toutes les informations nécessaires
pour configurer votre serveur afin de fournir une validation des paramètres
d'entrée, une validation des données de réponse de sortie et une définition des
points de terminaison URL.

Créez un fichier nommé swagger.yml et commencez à y ajouter des


métadonnées :

# swagger.yml

openapi: 3.0.0
info:
title: "RP Flask REST API"
description: "An API about people and notes"
version: "1.0.0"

Lorsque vous définissez une API, vous devez inclure la version de votre
définition OpenAPI. Vous utilisez le mot-clé openapi pour cela. La chaîne de
version est importante car certaines parties de la structure de l'OpenAPI peuvent
changer au fil du temps.

De plus, tout comme chaque nouvelle version de Python inclut de nouvelles


fonctionnalités (new features), des mots-clés peuvent être ajoutés ou dépréciés
dans la spécification OpenAPI.

Le mot-clé info commence la portée du bloc d'informations de l'API :

 title : Titre inclus dans le système d'interface utilisateur généré par


Connexion.
 description : Description de ce que l'API fournit ou concerne

 version : Valeur de la version de l'API

Ensuite, ajoutez servers et url, qui définissent le chemin racine de votre API :

# swagger.yml

# ...

servers:
- url: "/api"

En fournissant "/api" comme valeur de url, vous serez en mesure d'accéder à


tous vos chemins d'API relatifs à http://localhost:8000/api.

Vous définissez vos points de terminaison d'API dans un bloc de chemins :

# swagger.yml

# ...

paths:
/people:
get:
operationId: "people.read_all"
tags:
- "People"
summary: "Read the list of people"
responses:
"200":
description: "Successfully read people list"
Le bloc paths commence la configuration des chemins d'accès aux URL de l'API
:

 /people : L'URL relative de votre point de terminaison API

 get : La méthode HTTP à laquelle ce point de terminaison URL répondra.

Avec la définition de l'url dans les serveurs, cela crée le point de terminaison
GET /api/people auquel vous pouvez accéder à l'adresse
http://localhost:8000/api/people.

Le bloc get commence la configuration de l'unique point de terminaison URL


/api/people :

 operationId : La fonction Python qui répondra à la demande.

 tags : Les balises assignées à cet endpoint, qui vous permettent de


regrouper les opérations dans l'interface utilisateur.

 summary : Le texte d'affichage de l'interface utilisateur pour ce point de


terminaison.

 responses : Les codes d'état avec lesquels le point final répond.

operationId doit contenir une chaîne de caractères. Connexion utilisera


"people.read_all" pour trouver une fonction Python nommée read_all() dans un
module people de votre projet. Vous créerez le code Python correspondant plus
tard dans ce tutoriel.
Le bloc responses définit la configuration des codes d'état possibles. Ici, vous
définissez une réponse réussie pour le code d'état "200", contenant un texte de
description.

Vous pouvez trouver le contenu complet du fichier swagger.yml dans le


collapsible ci-dessous

swagger.yml Source Code

Ci-dessous, vous trouverez le code source complet de votre définition OpenAPI :


# swagger.yml

openapi: 3.0.0
info:
title: "RP Flask REST API"
description: "An API about people and notes"
version: "1.0.0"

servers:
- url: "/api"

paths :
/people:
get:
operationId: "people.read_all"
tags:
- "People"
summary: "Read the list of people"
responses:
"200":
description: "Successfully read people list"

Vous avez organisé ce fichier de manière hiérarchique. Chaque niveau


d'indentation représente un niveau de propriété, ou portée.
Par exemple, paths marque le début de l'endroit où sont définis tous les points de
terminaison de l'URL de l'API. La valeur /people mise en retrait sous that
représente le début de l'endroit où tous les points de terminaison de l'URL
/api/people seront définis. L'étendue get : en retrait sous /people contient les
définitions associées à une requête HTTP GET vers le point final de l'URL
/api/people. Ce schéma se poursuit pour l'ensemble de la configuration.

Le fichier swagger.yml est comme un schéma directeur pour votre API. Avec les
spécifications que vous incluez dans swagger.yml, vous définissez les données
que votre serveur Web peut attendre et la façon dont il doit répondre aux
demandes. Mais jusqu'à présent, votre projet Flask ne connaît pas votre fichier
swagger.yml. Lisez la suite pour utiliser Connexion afin de connecter votre
spécification OpenAPI à votre application Flask.

Ajouter Connexion à l'application


L'ajout d'un point de terminaison URL d'API REST à votre application Flask
avec Connexion se fait en deux étapes :

1. Ajouter un fichier de configuration API à votre projet.

2. Connectez votre application Flask avec le fichier de configuration.

Vous avez déjà ajouté un fichier de configuration nommé swagger.yml dans la


dernière section. Pour connecter le fichier de configuration API à votre
application Flask, vous devez faire référence à swagger.yml dans votre fichier
app.py :

# app.py
from flask import render_template # Remove: import Flask
import connexion
app = connexion. App(__name__, specification_dir="./")
app.add_api("swagger.yml")

@app.route("/")
def home():
return render_template("home.html")

if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000, debug=True)

L'instruction import Connexion ajoute le module au programme. L'étape


suivante consiste à créer l'instance d'application en utilisant Connexion plutôt
que Flask. En interne, l'application Flask est toujours créée, mais elle est
maintenant dotée de fonctionnalités supplémentaires.

Une partie de la création de l'instance d'application inclut le paramètre


specification_dir à la ligne 6. Il indique à Connexion dans quel répertoire
chercher son fichier de configuration. Dans ce cas, il s'agit du même répertoire
que celui à partir duquel vous exécutez app.py.

À la ligne 7, vous indiquez à l'instance d'application de lire le fichier


swagger.yml dans le répertoire de spécification et de configurer le système pour
fournir la fonctionnalité de Connexion.

Renvoyer les données de votre point de terminaison


personnel
Dans le fichier swagger.yml, vous avez configuré Connexion avec la valeur
operationId "people.read_all". Ainsi, lorsque l'API reçoit une requête HTTP
pour GET /api/people, votre application Flask appelle une fonction read_all()
dans un module people.

Pour que cela fonctionne, créez un fichier people.py avec une fonction read_all()
:

# people.py

3from datetime import datetime

5def get_timestamp():
return datetime.now().strftime(("%Y-%m-%d %H:%M:%S"))

8PEOPLE = {
"Fairy": {
"fname": "Tooth",
"lname": "Fairy",
"timestamp": get_timestamp(),
},
"Ruprecht": {
"fname": "Knecht",
"lname": "Ruprecht",
"timestamp": get_timestamp(),
},
"Bunny": {
"fname": "Easter",
"lname": "Bunny",
"timestamp": get_timestamp(),
}
}

def read_all():
return list(PEOPLE.values())
À la ligne 5, vous créez une fonction d'aide appelée get_timestamp() qui génère
une représentation en chaîne de l'horodatage actuel.

Vous définissez ensuite la structure de données du dictionnaire PEOPLE à la


ligne 8, qui est la donnée avec laquelle vous allez travailler dans cette partie de
la série de tutoriels.

Le dictionnaire PEOPLE se substitue à une véritable base de données. Comme


PEOPLE est une variable de module, son état persiste entre les appels de l'API
REST. Cependant, toutes les données que vous modifiez seront perdues lorsque
vous redémarrerez votre application Web. Ce n'est pas idéal, mais c'est suffisant
pour l'instant.

Ensuite, vous créez la fonction read_all() à la ligne 26. Votre serveur exécutera
read_all() lorsqu'il recevra une requête HTTP de type GET /api/people. La
valeur de retour de read_all() est une liste de dictionnaires contenant des
informations sur une personne.

L'exécution du code de votre serveur et la navigation de votre navigateur vers


http://localhost:8000/api/people afficheront la liste des personnes à l'écran :
Félicitations, vous avez créé votre premier point de terminaison d'API ! Avant
de continuer à développer votre API REST avec plusieurs points de terminaison,
prenez le temps d'explorer un peu plus l'API dans la section suivante.

Explorez la documentation de votre API


Actuellement, vous avez une API REST fonctionnant avec un seul point de
terminaison URL. Votre application Flask sait ce qu'il faut servir en fonction de
la spécification de votre API dans swagger.yml. De plus, Connexion utilise
swagger.yml pour créer la documentation de l'API pour vous.

Naviguez vers localhost:8000/api/ui pour voir votre documentation d'API en


action :
Il s'agit de l'interface initiale de Swagger. Elle affiche la liste des points de
terminaison URL pris en charge par votre point de terminaison
http://localhost:8000/api. Connexion construit cette liste automatiquement
lorsqu'il analyse le fichier swagger.yml.

Si vous cliquez sur le point de terminaison /people dans l'interface, celle-ci


s'élargira pour afficher plus d'informations sur votre API :

Cela affiche la structure de la réponse attendue, le type de contenu de cette


réponse et le texte de description que vous avez entré à propos du point de
terminaison dans le fichier swagger.yml. Chaque fois que le fichier de
configuration change, l'interface utilisateur Swagger change également.

Vous pouvez même essayer le point de terminaison en cliquant sur le bouton


"Try it out". Cette fonctionnalité peut être extrêmement utile lorsque votre API
se développe. La documentation Swagger UI sur l'API vous permet d'explorer et
d'expérimenter l'API sans avoir à écrire de code pour le faire.

L'utilisation d'OpenAPI avec l'interface utilisateur Swagger offre un moyen


simple et efficace de créer les points d'extrémité des URL d'API. Jusqu'à
présent, vous n'avez créé qu'un seul point de terminaison pour afficher tout le
monde. Dans la section suivante, vous ajouterez des points de terminaison
supplémentaires pour créer, mettre à jour et supprimer des personnes dans votre
collection.

Construire l'API complète

Jusqu'à présent, votre API REST Flask a un seul point de terminaison. Il est
maintenant temps de construire une API fournissant un accès CRUD complet à
votre structure de personnes. Comme vous vous en souvenez, la définition de
votre API ressemble à ceci :

Action HTTP Verb URL Path Description


Read GET /api/people Read a collection of people.
Create POST /api/people Create a new person.
Read GET /api/people/<lname> Read a particular person.
Update PUT /api/people/<lname> Update an existing person.
Delete DELETE /api/people/<lname> Delete an existing person.
Pour ce faire, vous étendrez les fichiers swagger.yml et people.py afin de
prendre entièrement en charge l'API définie ci-dessus.

Travailler avec des composants

Avant de définir de nouveaux chemins d'API dans swagger.yml, vous ajouterez


un nouveau bloc pour les composants (Components). Les composants sont des
blocs de construction dans votre spécification OpenAPI que vous pouvez
référencer à partir d'autres parties de votre spécification.

Ajoutez un bloc de composants avec des schémas pour une seule personne :

# swagger.yml

openapi: 3.0.0
info:
title: "RP Flask REST API"
description: "An API about people and notes"
version: "1.0.0"

servers:
- url: "/api"

components:
schemas:
Person:
type: "object"
required:
- lname
properties:
fname:
type: "string"
lname:
type: "string"
# ...

Pour éviter la duplication du code, vous créez un bloc composant. Pour l'instant,
vous ne sauvegardez que le modèle de données Personne dans le bloc schemas :

 type : Le type de données du schéma

 required : Les propriétés requises

Le tiret (-) devant - lname indique que required peut contenir une liste de
propriétés. Toute propriété que vous définissez comme requise doit également
exister dans properties, ce qui inclut les propriétés suivantes :

 fname : Le prénom d'une personne

 lname : Le nom de famille d'une personne

La clé de type définit la valeur associée à sa clé parent. Pour Person, toutes les
propriétés sont des chaînes de caractères. Vous représenterez ce schéma dans
votre code Python sous la forme d'un dictionnaire (dictionary ) plus tard dans ce
tutoriel.

Créer une nouvelle personne

Étendez vos points de terminaison API en ajoutant un nouveau bloc pour la


demande de post dans le bloc /people :
# swagger.yml

# ...

paths:
/people:
get:
# ...
post:
operationId: "people.create"
tags:
- People
summary: "Create a person"
requestBody:
description: "Person to create"
required: True
content:
application/json:
schema:
x-body-name: "person"
$ref: "#/components/schemas/Person"
responses:
"201":
description: "Successfully created person"

La structure de post ressemble au schéma existant de get. Une différence est que
vous envoyez également requestBody au serveur. Après tout, vous devez
indiquer à Flask les informations dont il a besoin pour créer une nouvelle
personne. Une autre différence est operationId, que vous définissez comme
people.create.

Dans le contenu, vous définissez application/json comme le format d'échange de


données de votre API.
Vous pouvez servir différents types de médias dans vos demandes et réponses
d'API. De nos jours, les API utilisent généralement JSON comme format
d'échange de données. C'est une bonne nouvelle pour vous, développeur Python,
car les objets JSON ressemblent beaucoup aux dictionnaires Python. Par
exemple :

{
"fname": "Tooth",
"lname": "Fairy"
}

Cet objet JSON ressemble au composant Personne que vous avez défini
précédemment dans swagger.yml et que vous référencez avec $ref dans schema.

Vous utilisez également un code d'état HTTP 201, qui est une réponse positive
indiquant la création d'une nouvelle ressource.

Remarque : si vous souhaitez en savoir plus sur les codes d'état HTTP, vous
pouvez consulter la documentation de Mozilla sur les codes d'état des réponses
http ( HTTP response status codes).

Avec people.create, vous dites à votre serveur de chercher une fonction create()
dans le module people. Ouvrez people.py et ajoutez create() au fichier :

# people.py

from datetime import datetime


from flask import abort

# ...
def create(person):
lname = person.get("lname")
fname = person.get("fname", "")

if lname and lname not in PEOPLE:


PEOPLE[lname] = {
"lname": lname,
"fname": fname,
"timestamp": get_timestamp(),
}
return PEOPLE[lname], 201
else:
abort(
406,
f"Person with last name {lname} already
exists",
)

A la ligne 4, vous importez la fonction abort() de Flask. L'utilisation de la


fonction abort() vous permet d'envoyer un message d'erreur à la ligne 20. Tu
lèves la réponse d'erreur lorsque le corps de la requête ne contient pas de nom de
famille ou lorsqu'une personne avec ce nom de famille existe déjà.

Remarque : le nom de famille d'une personne doit être unique, car vous utilisez
lname comme clé de dictionnaire de PEOPLE. Cela signifie que vous ne pouvez
pas avoir deux personnes avec le même nom de famille dans votre projet pour le
moment.

Si les données du corps de la requête sont valides, vous mettez à jour PEOPLE à
la ligne 13 et répondez avec le nouvel objet et un code HTTP 201 à la ligne 18.
Traiter une personne

Jusqu'à présent, vous êtes en mesure de créer une nouvelle personne et d'obtenir
une liste avec toutes vos personnes. Dans cette section, vous allez mettre à jour
swagger.yml et people.py pour travailler avec un nouveau chemin qui gère une
seule personne existante.

Ouvrez swagger.yml et ajoutez le code ci-dessous :

# swagger.yml

# ...

components:
schemas:
# ...
parameters:
lname:
name: "lname"
description: "Last name of the person to get"
in: path
required: True
schema:
type: "string"

paths:
/people:
# ...
/people/{lname}:
get:
operationId: "people.read_one"
tags:
- People
summary: "Read one person"
parameters:
- $ref: "#/components/parameters/lname"
responses:
"200":
description: "Successfully read person"

Comme pour le chemin /people, vous commencez par l'opération get pour le
chemin /people/{lname}. La sous-chaîne {lname} est un caractère de
remplacement pour le nom de famille, que vous devez transmettre en tant que
paramètre d'URL. Ainsi, par exemple, le chemin URL api/people/Ruprecht
contient Ruprecht comme nom de famille.

Remarque : Les paramètres de l'URL sont sensibles à la casse. Cela signifie que
vous devez taper un nom de famille comme Ruprecht avec un R majuscule.

Vous utiliserez également le paramètre lname dans d'autres opérations. Il est


donc logique de créer un composant pour ce paramètre et de le référencer si
nécessaire.

operationId pointe vers une fonction read_one() dans people.py, donc allez dans
ce fichier et créez la fonction manquante :

# people.py
# ...

def read_one(lname):
if lname in PEOPLE:
return PEOPLE[lname]
else:
abort(
404, f"Person with last name {lname} not
found"
)

Lorsque votre application Flask trouve le nom de famille fourni dans PEOPLE,
elle renvoie les données pour cette personne particulière. Sinon, le serveur
renvoie une erreur HTTP 404.

Pour mettre à jour une personne existante, mettez à jour swagger.yml avec ce
code :

# swagger.yml

# ...

paths:
/people:
# ...
/people/{lname}:
get:
# ...
put:
tags:
- People
operationId: "people.update"
summary: "Update a person"
parameters:
- $ref: "#/components/parameters/lname"
responses:
"200":
description: "Successfully updated person"
requestBody:
content:
application/json:
schema:
x-body-name: "person"
$ref: "#/components/schemas/Person"

Avec cette définition de l'opération put, votre serveur attend update() dans
people.py :

# people.py

20# ...

def update(lname, person):


if lname in PEOPLE:
PEOPLE[lname]["fname"] = person.get("fname",
PEOPLE[lname]["fname"])
PEOPLE[lname]["timestamp"] = get_timestamp()
return PEOPLE[lname]
else:
abort(
404,
f"Person with last name {lname} not found"
)

La fonction update() attend les arguments lname et person. Lorsqu'une personne


portant le nom de famille fourni existe, vous mettez à jour les valeurs
correspondantes dans PEOPLE avec les données de la personne.

Pour se débarrasser d'une personne dans votre ensemble de données, vous devez
utiliser une opération de suppression :

# swagger.yml
# ...

paths:
/people:
# ...
/people/{lname}:
get:
# ...
put:
# ...
delete:
tags:
- People
operationId: "people.delete"
summary: "Delete a person"
parameters:
- $ref: "#/components/parameters/lname"
responses:
"204":
description: "Successfully deleted person"

Ajoutez la fonction delete() correspondante à person.py :


# people.py

from flask import abort, make_response

# ...

def delete(lname):
if lname in PEOPLE:
del PEOPLE[lname]
return make_response(
f"{lname} successfully deleted", 200
)
else:
abort(
404,
f"Person with last name {lname} not found"
)

Si la personne que vous voulez supprimer existe dans votre ensemble de


données, alors vous supprimez l'élément de PEOPLE.

Les fichiers people.py et swagger.yml sont complets pour cette partie du tutoriel.
Vous pouvez télécharger les fichiers complets en cliquant sur le lien ci-dessous :

Avec tous les points de terminaison pour gérer les personnes en place, il est
temps d'essayer votre API. Puisque vous avez utilisé Connexion pour connecter
votre projet Flask avec Swagger, la documentation de votre API est prête pour
vous lorsque vous redémarrez votre serveur.

Explorez la documentation complète de votre API

Une fois que vous avez mis à jour les fichiers swagger.yml et people.py pour
compléter la fonctionnalité de l'API de personnes, le système d'interface
utilisateur Swagger sera mis à jour en conséquence et ressemblera à ceci :

Cette interface utilisateur vous permet de voir toute la documentation que vous
avez incluse dans le fichier swagger.yml et d'interagir avec tous les points
d'extrémité URL qui constituent la fonctionnalité CRUD de l'interface
utilisateur.

Malheureusement, les changements que vous effectuez ne persisteront pas


lorsque vous redémarrerez votre application Flask. C'est pourquoi vous allez
brancher une base de données appropriée à votre projet dans la prochaine partie
de cette série de tutoriels.
Conclusion
Dans cette partie de la série de tutoriels, vous avez créé une API REST complète
avec le framework web Flask de Python. Avec le module Connexion et quelques
travaux de configuration supplémentaires, une documentation utile et un
système interactif peuvent être mis en place. Cela fait de la création d'une API
REST une expérience très agréable.

Dans la première partie de cette série de tutoriels, vous avez appris à :

Construire un projet Flask de base avec une API REST.

Traiter les requêtes HTTP avec Connexion

Définir des points de terminaison d'API à l'aide de la spécification OpenAPI

Interagir avec votre API pour gérer les données

Créer une documentation d'API avec Swagger UI

Dans la deuxième partie de cette série, vous apprendrez à utiliser une base de
données appropriée pour stocker vos données de façon permanente au lieu de
vous fier au stockage en mémoire comme vous l'avez fait ici.
API REST en Python avec Flask, Connexion et
SQLAlchemy - Partie 2

Table des matières

Démo

Planification de la deuxième partie

Mise en route

 Récupérer les prérequis

 Ajouter de nouvelles dépendances

 Vérifiez votre projet Flask

Initialiser la base de données

 Inspecter la structure actuelle de vos données

 Conceptualisez votre table de base de données

 Construire votre base de données

 Interagir avec la base de données

Connecter la base de données SQLite avec votre projet Flask

 Configurer votre base de données

 Modélisation des données avec SQLAlchemy

 Sérialisez les données modélisées avec Marshmallow

 Faire un peu de nettoyage


Connecter la base de données avec votre API

 Lire depuis la base de données

 Ecrire dans la base de données

 Afficher les données dans votre interface

 Explorez la documentation de votre API

Conclusion

La plupart des applications Web modernes sont alimentées par une API REST
sous le capot. Ainsi, les développeurs peuvent séparer le code frontal de la
logique dorsale, et les utilisateurs peuvent interagir avec l'interface de manière
dynamique. Dans cette série de didacticiels en trois parties, vous construisez une
API REST à l'aide du framework Web Flask.

Vous avez créé une base avec un projet Flask de base et ajouté des points de
terminaison, que vous connecterez à une base de données SQLite. Vous testez
également votre API avec la documentation Swagger UI API que vous
construisez en cours de route.

Dans la première partie, vous avez utilisé Flask et Connexion pour créer une
API REST fournissant des opérations CRUD à une structure en mémoire
appelée PEOPLE. Ce faisant, vous avez appris comment le module Connexion
vous aide à construire une API REST agréable et une documentation interactive.

Dans la deuxième partie de cette série de tutoriels, vous apprendrez à.. :

 Écrire des commandes SQL en Python

 Configurer une base de données SQLite pour votre projet Flask

 Utiliser SQLAlchemy pour enregistrer des objets Python dans votre base
de données

 Exploiter la bibliothèque Marshmallow pour sérialiser les données

 Connecter votre API REST à votre base de données

Après avoir terminé la deuxième partie de cette série, vous passerez à la


troisième partie, où vous étendrez votre API REST avec la fonctionnalité d'ajout
de notes à une personne.

Vous pouvez télécharger le code de la deuxième partie de ce projet en cliquant


sur le lien ci-dessous :

Démo
Dans cette série de tutoriels en trois parties, vous allez créer une API REST pour
conserver la trace des notes des personnes susceptibles de vous rendre visite tout
au long de l'année. Vous allez créer des personnes comme la fée des dents, le
lapin de Pâques et Knecht Ruprecht.

Idéalement, vous voulez être en bons termes avec ces trois personnes. C'est
pourquoi vous leur enverrez des notes, afin d'augmenter vos chances d'obtenir
des cadeaux de valeur de leur part.
Vous pouvez interagir avec votre application en vous appuyant sur la
documentation de l'API. En cours de route, vous construisez également un
frontal de base qui reflète le contenu de votre base de données :

Dans la deuxième partie de cette série, vous allez améliorer le back-end de votre
application en ajoutant une base de données appropriée. De cette façon, vous
ferez persister vos données même lorsque vous redémarrerez votre application :

Grâce à la documentation de votre interface utilisateur Swagger, vous pourrez


interagir avec votre API REST et vous assurer que tout fonctionne comme
prévu.

Planification de la deuxième partie


Dans la première partie de cette série de tutoriels, vous avez travaillé avec un
dictionnaire PEOPLE pour stocker vos données. L'ensemble de données
ressemblait à ceci :

PEOPLE = {
"Fairy": {
"fname": "Tooth",
"lname": "Fairy",
"timestamp": "2022-10-08 09:15:10",
},
"Ruprecht": {
"fname": "Knecht",
"lname": "Ruprecht",
"timestamp": "2022-10-08 09:15:13",
},
"Bunny": {
"fname": "Easter",
"lname": "Bunny",
"timestamp": "2022-10-08 09:15:27",
}
}

Cette structure de données était pratique pour mettre votre projet en route.
Cependant, toutes les données que vous avez ajoutées avec votre API REST à
PEOPLE ont été perdues lorsque vous avez redémarré votre application.

Dans cette partie, vous allez traduire votre structure de données PEOPLE en une
table de base de données qui ressemblera à ceci :

id lname fname timestamp


1 Fairy Tooth 2022-10-08 09:15:10
2 Ruprecht Knecht 2022-10-08 09:15:13
3 Bunny Easter 2022-10-08 09:15:27

Ce tutoriel ne vous permettra pas de modifier les points d'extrémité de votre API
REST. Mais les changements que vous ferez dans le back-end seront
significatifs, et vous vous retrouverez avec une base de code beaucoup plus
polyvalente pour vous aider à faire évoluer votre projet Flask à l'avenir.

Pour commencer
Dans cette section, vous allez vérifier le projet d'API REST Flask sur lequel
vous travaillez. Vous voulez vous assurer qu'il est prêt pour les prochaines
étapes de cette série de didacticiels (Flask-Marshmallow).
Pour convertir des types de données complexes à partir de et vers des types de
données Python, vous avez besoin d'un sérialiseur. Pour ce tutoriel, vous
utiliserez Flask-Marshmallow. Flask-Marshmallow étend la librairie
Marshmallow et fournit des fonctionnalités supplémentaires lorsque vous
travaillez avec Flask (Marshmallow).

Récupérer les prérequis

Idéalement, vous avez suivi la première partie de cette série de tutoriels avant de
poursuivre avec la deuxième partie, que vous lisez en ce moment même. Sinon,
vous pouvez également télécharger le code source de la première partie en
cliquant sur le lien ci-dessous :

Si vous avez téléchargé le code source à partir du lien ci-dessus, assurez-vous de


suivre les instructions d'installation dans le fichier README.md fourni.

Avant de poursuivre le tutoriel, vérifiez que votre structure de dossiers


ressemble à ceci :

rp_flask_api/

├── templates/
│ └── home.html

├── app.py
├── people.py
└── swagger.yml

Une fois que vous avez mis en place la structure de dossiers de l'API REST de
Flask, vous pouvez poursuivre l'installation des dépendances dont vous aurez
besoin dans cette partie de la série de tutoriels.

Ajouter de nouvelles dépendances

Avant de continuer à travailler sur votre projet Flask, c'est une bonne idée de
créer et d'activer un environnement virtuel. De cette façon, vous installez toutes
les dépendances du projet non pas sur l'ensemble du système mais uniquement
dans l'environnement virtuel de votre projet.

Sélectionnez votre système d'exploitation ci-dessous et utilisez la commande


spécifique à votre plateforme pour configurer un environnement virtuel :

Windows
PS> python -m venv venv
PS> .\venv\Scripts\activate
(venv) PS>

Linux
$ python -m venv venv
$ source venv/bin/activate
(venv) $

Avec les commandes présentées ci-dessus, vous créez et activez un


environnement virtuel nommé venv en utilisant le module venv intégré de
Python. Les parenthèses (venv) devant l'invite indiquent que vous avez activé
l'environnement virtuel avec succès.

Remarque : si vous n'avez pas encore étudié la première partie de cette série de
tutoriels, assurez-vous de télécharger le code source en cliquant sur le lien ci-
dessous :

Ensuite, installez flask-marshmallow avec l'option sqlalchemy :

(venv) $ python -m pip install "flask-


marshmallow[sqlalchemy]==0.14.0"

Flask-Marshmallow installe également marshmallow, qui fournit une


fonctionnalité pour sérialiser et désérialiser les objets Python lorsqu'ils entrent et
sortent de votre API REST, qui est basée sur JSON ( JSON). Marshmallow
convertit les instances de classe Python en objets qui peuvent être convertis en
JSON (Python class instances ).

En utilisant l'option sqlalchemy, vous installez également des paquets qui aident
votre application Flask à tirer parti des pouvoirs de SQLAlchemy ( object-
relational model (ORM)).

SQLAlchemy fournit un modèle objet-relationnel (ORM), qui stocke chaque


objet Python dans une représentation de base de données des données de l'objet.
Cela peut vous aider à continuer à penser de manière pythonique et à ne pas
vous préoccuper de la manière dont les données de l'objet seront représentées
dans une base de données.
Vérifiez votre projet Flask
Après avoir suivi les étapes ci-dessus, vous pouvez vérifier que votre application
Flask fonctionne sans erreur. Exécutez la commande suivante dans le répertoire
contenant le fichier app.py :

(venv) $ python app.py

Lorsque vous exécutez cette application, un serveur web démarre sur le port
8000, qui est le port par défaut utilisé par Flask. Si vous ouvrez un navigateur et
naviguez vers http://localhost:8000

, vous devriez voir Hello, World ! affiché :

Parfait, votre application fonctionne parfaitement ! Il est maintenant temps


d'aller dans le back-end et de travailler avec une base de données appropriée.

Initialisation de la base de données


Actuellement, vous stockez les données de votre projet Flask dans un
dictionnaire. Stocker des données de cette façon n'est pas persistant. Cela
signifie que tout changement de données est perdu lorsque vous redémarrez
votre application Flask. En plus de cela, la structure de votre dictionnaire n'est
pas idéale.

Dans cette section, vous allez ajouter une base de données appropriée à votre
projet Flask pour corriger ces défauts.

Inspecter la structure actuelle de vos données

Actuellement, vous stockez vos données dans le dictionnaire PEOPLE de


people.py. La structure des données ressemble à ceci dans le code :

# people.py

# ...

PEOPLE = {
"Fairy": {
"fname": "Tooth",
"lname": "Fairy",
"timestamp": get_timestamp(),
},
"Ruprecht": {
"fname": "Knecht",
"lname": "Ruprecht",
"timestamp": get_timestamp(),
},
"Bunny": {
"fname": "Easter",
"lname": "Bunny",
"timestamp": get_timestamp(),
}
}

# ...

Les modifications que vous apporterez au programme déplaceront toutes les


données vers une table de base de données. Cela signifie que les données seront
enregistrées sur votre disque et existeront entre les exécutions du programme
app.py.

Conceptualisez votre table de base de données

Conceptuellement, vous pouvez considérer une table de base de données comme


un tableau à deux dimensions dont les lignes sont des enregistrements et les
colonnes des champs.

Les tables de base de données ont généralement une valeur entière auto-
incrémentée comme clé de recherche des lignes. C'est ce qu'on appelle la clé
primaire. Chaque enregistrement de la table possède une clé primaire dont la
valeur est unique pour l'ensemble de la table. Le fait de disposer d'une clé
primaire indépendante des données stockées dans la table vous donne la liberté
de modifier tout autre champ de la ligne.

Vous allez suivre la convention de la base de données qui consiste à nommer la


table au singulier, ainsi la table s'appellera person.
La traduction de votre structure PEOPLE ci-dessus en une table de base de
données nommée person ressemblera à ceci :

id lname fname timestamp


1 Fairy Tooth 2022-10-08 09:15:10
2 Ruprecht Knecht 2022-10-08 09:15:13
3 Bunny Easter 2022-10-08 09:15:27

Chaque colonne de la table a un nom de champ comme suit :

 id : Champ clé primaire pour chaque personne

 lname : Nom de famille de la personne

 fname : Prénom de la personne

 timestamp : Horodatage de la dernière modification

Avec ce concept de base de données en place, il est temps de construire la base


de données.

Créez votre base de données


Vous allez utiliser SQLite comme moteur de base de données pour stocker les
données de PEOPLE. SQLite (SQLite) est un système de gestion de base de
données relationnelle (SGBDR) largement utilisé qui n'a pas besoin d'un serveur
SQL pour fonctionner.

Contrairement aux autres moteurs de base de données SQL (other SQL database
engines), SQLite fonctionne avec un seul fichier pour maintenir toutes les
fonctionnalités de la base de données. Par conséquent, pour utiliser la base de
données, un programme doit simplement savoir comment lire et écrire dans un
fichier SQLite.
Le module sqlite3 ( sqlite3 ) intégré à Python vous permet d'interagir avec les
bases de données SQLite sans paquetage externe. Cela rend SQLite
particulièrement utile lors du lancement de nouveaux projets Python.

Démarrez un nouveau shell interactif Python pour créer la base de données


SQLite people.db (Python interactive shell): (winpty python sur git bash)

>>> import sqlite3


>>> conn = sqlite3.connect("people.db")
>>> columns = [
... "id INTEGER PRIMARY KEY",
... "lname VARCHAR UNIQUE",
... "fname VARCHAR",
... "timestamp DATETIME",
... ]
>>> create_table_cmd = f"CREATE TABLE person
({','.join(columns)})"
>>> conn.execute(create_table_cmd)
<sqlite3.Cursor object at 0x1063f4dc0>

Après avoir importé le module sqlite3 (sqlite3 module), vous pouvez créer une
nouvelle base de données avec. connect(). Si vous jetez un coup d'oeil à votre
système de fichiers après avoir défini la variable conn, vous remarquerez que
Python a immédiatement créé le fichier de base de données people.db.

Avec conn.execute(), vous exécutez la commande SQL pour créer une table
people avec les colonnes id, lname, fname et timestamp.
Notez que vous incluez une contrainte UNIQUE pour lname. C'est important car
vous utilisez le nom de famille dans votre API REST pour identifier une
personne. Par conséquent, votre base de données doit garantir l'unicité de lname
pour éviter les incohérences dans vos données.

Maintenant que votre base de données existe, vous pouvez y ajouter des données
:

>>> import sqlite3


>>> conn = sqlite3.connect("people.db")
>>> people = [
... "1, 'Fairy', 'Tooth', '2022-10-08 09:15:10'",
... "2, 'Ruprecht', 'Knecht', '2022-10-08 09:15:13'",

... "3, 'Bunny', 'Easter', '2022-10-08 09:15:27'",


... ]
>>> for person_data in people:
... insert_cmd = f"INSERT INTO person VALUES
({person_data})"
... conn.execute(insert_cmd)
...
<sqlite3.Cursor object at 0x104ac4dc0>
<sqlite3.Cursor object at 0x104ac4f40>
<sqlite3.Cursor object at 0x104ac4fc0>

>>> conn.commit()

Une fois que vous êtes connecté à la base de données people.db, vous déclarez
une transaction pour insérer les données people_data dans la table person. La
commande conn.execute() crée des objets sqlite3.Cursor en mémoire. Ce n'est
que lorsque vous exécutez conn.commit() que la transaction se réalise.
Interagir avec la base de données
Contrairement aux langages de programmation comme Python, SQL ne définit
pas comment obtenir les données. SQL décrit les données souhaitées et laisse le
comment au moteur de la base de données.

Une requête SQL permettant d'obtenir toutes les données de votre table de
personnes ressemblerait à ceci :

SELECT * FROM person;

Cette requête demande au moteur de la base de données de récupérer tous les


champs de la table person. Dans le code Python suivant, vous utilisez SQLite
pour exécuter la requête ci-dessus et afficher les données :

>>> import sqlite3


>>> conn = sqlite3.connect("people.db")
>>> cur = conn.cursor()
>>> cur.execute("SELECT * FROM person")
<sqlite3.Cursor object at 0x102357a40>

>>> people = cur.fetchall()


>>> for person in people:
... print(person)
...
(1, 'Fairy', 'Tooth', '2022-10-08 09:15:10')
(2, 'Ruprecht', 'Knecht', '2022-10-08 09:15:13')
(3, 'Bunny', 'Easter', '2022-10-08 09:15:27')

Le code ci-dessus fait ce qui suit :

 La ligne 1 importe le module sqlite3.

 La ligne 2 crée une connexion au fichier de la base de données.

 La ligne 3 crée un curseur à partir de la connexion.

 La ligne 4 utilise le curseur pour exécuter une requête SQL exprimée sous
forme de chaîne.

 La ligne 7 récupère tous les enregistrements renvoyés par la requête SQL


et les affecte à la variable people.

 Les lignes 8 et 9 itèrent sur les personnes et impriment les données de


chaque personne.

Dans le programme ci-dessus, l'instruction SQL est une chaîne transmise


directement à la base de données pour être exécutée. Dans ce cas, ce n'est peut-
être pas un gros problème car le SQL est une chaîne littérale complètement sous
le contrôle du programme. Toutefois, le cas d'utilisation de votre API REST
consistera à prendre les entrées de l'utilisateur à partir de l'application Web et à
les utiliser pour créer des requêtes SQL. Cela peut exposer votre application à
des attaques.

Développez la section ci-dessous pour apprendre comment :


Vous vous souviendrez que, dans la première partie de cette série de tutoriels, le
point de terminaison de l'API REST permettant d'obtenir une seule personne à
partir des données PEOPLE ressemblait à ceci :

GET /api/people/{lname} (get a single person)

Cela signifie que votre API attend une variable, lname, dans le chemin du point
de terminaison de l'URL qu'elle utilise pour trouver une seule personne. La
modification du code Python SQLite ci-dessus pour effectuer cette opération
ressemblerait à ceci :

lname = "Fairy"
cur.execute(f"SELECT * FROM person WHERE lname =
'{lname}'")

L'extrait de code ci-dessus fait ce qui suit :

 La ligne 1 définit la variable lname sur 'Fairy'. Cette valeur proviendrait


du chemin d'accès au point de terminaison de l'API REST.

 La ligne 2 utilise le formatage de chaîne Python pour créer une chaîne


SQL (string formatting ) et l'exécuter.

Pour simplifier les choses, le code ci-dessus attribue à la variable lname la


valeur d'une constante, mais en réalité, cette variable proviendrait du chemin
d'accès du point de terminaison de l'API et pourrait être n'importe quelle valeur
fournie par l'utilisateur. Le SQL généré par le formatage de la chaîne ressemble
à ceci :

SELECT * FROM person WHERE lname = 'Fairy'


Lorsque ce code SQL est exécuté par la base de données, il recherche dans la
table des personnes un enregistrement dont le nom de famille est égal à "Fairy".
C'est ce qui est prévu, mais tout programme qui accepte une entrée utilisateur est
également ouvert aux utilisateurs malveillants. Le programme ci-dessus, dans
lequel la variable lname est définie par l'entrée de l'utilisateur, vous expose à ce
que l'on appelle une attaque par injection SQL (SQL injection attack). Vous
pouvez voir une telle attaque sous le nom de "Little Bobby Tables" ( Little
Bobby Tables:):

Image: xkcd.com
Par exemple, imaginez qu'un utilisateur malveillant appelle votre API REST de
cette manière :

GET /api/people/Fairy';DROP TABLE person;

La requête API REST ci-dessus définit la variable lname sur 'Fairy';DROP


TABLE person;', ce qui, dans le code ci-dessus, génère cette instruction SQL :

SELECT * FROM person WHERE lname = 'Fairy';DROP TABLE


person;

L'instruction SQL ci-dessus est valide, et lorsqu'elle est exécutée par la base de
données, elle trouve un enregistrement où lname correspond à 'Fairy'. Ensuite,
elle trouvera le caractère de délimitation de l'instruction SQL ; et ira de l'avant et
abandonnera la table entière. Cela aurait pour effet de détruire votre application.

Vous pouvez protéger votre programme en désinfectant toutes les données que
vous recevez des utilisateurs de votre application. Dans ce contexte, la
désinfection des données signifie que votre programme doit examiner les
données fournies par l'utilisateur pour s'assurer qu'elles ne contiennent rien de
dangereux pour le programme. Cette opération peut être délicate à réaliser
correctement et doit être effectuée partout où les données utilisateur
interagissent avec la base de données.

Il serait bien mieux que ce que vous obteniez en retour pour la personne soit un
objet Python, où chacun des champs est un attribut de l'objet. De cette façon,
vous vous assurez que les objets contiennent les types de valeurs attendus et non
des commandes malveillantes.

Lorsque vous interagissez avec une base de données dans votre code Python,
vous pouvez réfléchir à deux fois avant d'écrire des commandes SQL pures.
Comme vous l'avez appris plus haut, écrire du SQL peut non seulement sembler
peu pratique, mais aussi poser des problèmes de sécurité. Si vous ne voulez pas
trop vous soucier de l'interaction avec la base de données, un paquetage comme
SQLAlchemy peut vous aider.

Connecter la base de données SQLite avec votre


projet Flask
Dans cette section, vous utiliserez SQLAlchemy pour vous aider à communiquer
avec votre base de données et à connecter people.db à votre application Flask.

SQLAlchemy gère de nombreuses interactions spécifiques à des bases de


données particulières et vous permet de vous concentrer sur les modèles de
données ainsi que sur la façon de les utiliser. SQLAlchemy aseptisera les
données de l'utilisateur pour vous avant de créer des instructions SQL. C'est un
autre avantage important et une raison d'utiliser SQLAlchemy lorsque vous
travaillez avec des bases de données.

Dans cette section, vous allez également créer deux modules Python, config.py
et models.py :

1. config.py permet d'importer et de configurer les modules nécessaires dans


le programme. Cela inclut Flask, Connexion, SQLAlchemy, et
Marshmallow.

2. models.py est le module dans lequel vous allez créer les définitions de
classe de SQLAlchemy et Marshmallow.

À la fin de cette section, vous serez en mesure de supprimer l'ancienne structure


de données PEOPLE et de travailler avec la base de données connectée.

Configurez votre base de données


Le module config.py est, comme son nom l'indique, l'endroit où toutes vos
informations de configuration sont créées et initialisées. Dans ce fichier, vous
allez configurer Flask, Connexion, SQLAlchemy, et Marshmallow.

Créez config.py dans votre dossier de projet rp_flask_api/ :

# config.py
2
3import pathlib
4import connexion
5from flask_sqlalchemy import SQLAlchemy
6from flask_marshmallow import Marshmallow
7
8basedir = pathlib.Path(__file__).parent.resolve()
9connex_app = connexion.App(__name__,
specification_dir=basedir)
10
11app = connex_app.app
12app.config["SQLALCHEMY_DATABASE_URI"] =
f"sqlite:///{basedir / 'people.db'}"
13app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False
14
15db = SQLAlchemy(app)
ma = Marshmallow(app)

Voici ce que fait le code ci-dessus :

 Les lignes 3 à 6 importent la pathlib intégrée ainsi que les bibliothèques


tierces connexion, SQLAlchemy et Marshmallow.

 La ligne 8 crée la variable basedir qui pointe vers le répertoire dans lequel
le programme est exécuté.
 La ligne 9 utilise la variable basedir pour créer l'instance de l'application
Connexion et lui donner le chemin d'accès au répertoire qui contient votre
fichier de spécification.

 La ligne 11 crée une variable, app, qui est l'instance Flask initialisée par
Connexion.

 La ligne 12 indique à SQLAlchemy d'utiliser SQLite comme base de


données et un fichier nommé people.db dans le répertoire courant comme
fichier de base de données.

 La ligne 13 désactive le système d'événements de SQLAlchemy. Le


système d'événements génère des événements qui sont utiles dans les
programmes pilotés par événements, mais il ajoute une surcharge
significative. Comme vous ne créez pas de programme piloté par des
événements, vous désactivez cette fonction.

 La ligne 15 initialise SQLAlchemy en transmettant les informations de


configuration de l'application à SQLAlchemy et en affectant le résultat à
une variable db.

 La ligne 16 initialise Marshmallow et lui permet de travailler avec les


composants SQLAlchemy attachés à l'application.
Si vous souhaitez en savoir plus sur les configurations de SQLAlchemy que
vous pouvez mettre en œuvre ici, vous pouvez consulter la documentation des
clés de configuration de Flask-SQLALchemy (configuration keys ).

Modélisation des données avec SQLAlchemy


SQLAlchemy est un grand projet qui fournit de nombreuses fonctionnalités pour
travailler avec des bases de données en utilisant Python. L'une des
fonctionnalités qu'il fournit est un mappeur objet-relationnel (ORM). Cet ORM
vous permet d'interagir avec la table de la base de données de la personne d'une
manière plus Python en faisant correspondre une ligne de champs de la table de
la base de données à un objet Python.

Créez un fichier models.py avec une définition de classe SQLAlchemy pour les
données de la table de la base de données des personnes :

# models.py

from datetime import datetime


from config import db

class Person(db.Model):
__tablename__ = "person"
id = db.Column(db.Integer, primary_key=True)
lname = db.Column(db.String(32), unique=True)
fname = db.Column(db.String(32))
timestamp = db.Column(
db.DateTime, default=datetime.utcnow,
onupdate=datetime.utcnow
)

Voici ce que fait le code ci-dessus :


 La ligne 3 importe l'objet datetime du module datetime fourni avec
Python ( the datetime module). Cela vous donne un moyen de créer un
horodatage dans la classe Person aux lignes 11 à 13.

 La ligne 4 importe db, une instance de SQLAlchemy que vous avez


définie dans le module config.py. Cela permet à models.py d'accéder aux
attributs et méthodes de SQLAlchemy.

 La ligne 6 définit la classe Person. L'héritage de db.Model donne à Person


les caractéristiques SQLAlchemy pour se connecter à la base de données
et accéder à ses tables.

 La ligne 7 relie la définition de la classe à la table de la base de données


Person.

 La ligne 8 déclare la colonne id contenant un nombre entier agissant


comme clé primaire pour la table.

 La ligne 9 définit le champ last name avec une valeur de type chaîne. Ce
champ doit être unique car vous utilisez lname comme identifiant d'une
personne dans une URL d'API REST.

 La ligne 10 définit le champ prénom avec une valeur de type chaîne.

 Les lignes 11 à 13 définissent un champ d'horodatage avec une valeur de


type date.

Le paramètre default=datetime.utcnow attribue par défaut à l'horodatage la


valeur utcnow actuelle lorsqu'un enregistrement est créé. Le paramètre
onupdate=datetime.utcnow met à jour l'horodatage avec la valeur utcnow
actuelle lorsque l'enregistrement est mis à jour. Pour en savoir plus sur les
horodatages UTC, développez la section repliable ci-dessous :
Vous vous demandez peut-être pourquoi l'horodatage de la classe ci-dessus est
mis à jour par la méthode datetime.utcnow(), qui renvoie un UTC, ou temps
universel coordonné. Il s'agit d'une façon de normaliser la source de votre
horodatage.

La source, ou temps zéro, est une ligne allant du pôle nord au pôle sud de la
Terre en passant par le Royaume-Uni. Il s'agit du fuseau horaire zéro à partir
duquel tous les autres fuseaux horaires sont décalés. En utilisant cette source
comme temps zéro, vos horodateurs sont décalés par rapport à ce point de
référence standard.

Si votre application est accessible depuis différents fuseaux horaires, vous


disposez d'un moyen de calculer la date et l'heure. Tout ce dont vous avez besoin
est un horodatage UTC et le fuseau horaire de destination.

Si vous deviez utiliser des fuseaux horaires locaux comme source d'horodatage,
vous ne pourriez pas effectuer de calculs de date et d'heure sans informations sur
le décalage d'un fuseau horaire local par rapport au temps zéro. Sans les
informations sur la source de l'horodatage, vous ne pourriez pas effectuer de
comparaisons de date et d'heure, ni aucun calcul.

Travailler avec un horodatage basé sur l'UTC est une bonne norme à suivre.
Voici un site d'outils à utiliser pour mieux comprendre ces horodatages ( tool
kit).
L'utilisation de SQLAlchemy vous permet de penser en termes d'objets ayant un
comportement plutôt que de traiter du SQL brut. Cela devient encore plus
bénéfique lorsque les tables de votre base de données deviennent plus grandes et
les interactions plus complexes.

Sérialiser les données modélisées avec Marshmallow

Il est très pratique de travailler avec les données modélisées de SQLAlchemy


dans vos programmes. Cependant, l'API REST fonctionne avec des données
JSON, et ici vous pouvez rencontrer un problème avec le modèle SQLAlchemy.

Comme SQLAlchemy renvoie des données sous forme d'instances de classe


Python, Connexion ne peut pas sérialiser ces instances de classe en données au
format JSON.

Remarque : dans ce contexte, la sérialisation signifie la conversion d'objets


Python, qui peuvent contenir d'autres objets Python et des types de données
complexes, en structures de données plus simples pouvant être analysées en
types de données JSON (JSON data types), qui sont énumérés ici :

 string : Un type de chaîne de caractères

 number (nombre) : Les nombres pris en charge par Python (entiers,


flottants, longs).

 object (objet) : Un objet JSON, qui est à peu près équivalent à un


dictionnaire Python.
 array : Équivalent approximatif d'une liste Python

 booléen : Représenté en JSON comme vrai ou faux, mais en Python


comme Vrai ou Faux

 null : Essentiellement aucun en Python (None in Python)

À titre d'exemple, votre classe Person contient un timestamp, qui est une classe
DateTime de Python. Il n'y a pas de définition de DateTime dans JSON, donc
l'horodatage doit être converti en chaîne de caractères pour exister dans une
structure JSON.

Vous utilisez une base de données pour stocker des données persistantes. Avec
SQLAlchemy, vous pouvez aisément communiquer avec votre base de données
depuis votre programme Python. Cependant, vous devez résoudre deux
problèmes :

1. Votre API REST fonctionne avec JSON au lieu d'objets Python.

2. Vous devez vous assurer que les données que vous ajoutez à la base de
données sont valides.

C'est là que le module Marshmallow entre en jeu !

Marshmallow vous aide à créer une classe PersonSchema, qui ressemble à la


classe Person de SQLAlchemy que vous venez de créer. La classe
PersonSchema définit la manière dont les attributs d'une classe seront convertis
dans des formats conviviaux JSON. Marshmallow s'assure également que tous
les attributs sont présents et contiennent le type de données attendu.
Voici la définition de la classe Marshmallow pour les données de votre table
Personne :

# models.py

from datetime import datetime


from config import db, ma

class Person(db.Model):
__tablename__ = "person"
id = db.Column(db.Integer, primary_key=True)
lname = db.Column(db.String(32), unique=True)
fname = db.Column(db.String(32))
timestamp = db.Column(
db.DateTime, default=datetime.utcnow,
onupdate=datetime.utcnow
)

class PersonSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Person
load_instance = True
sqla_session = db.session

person_schema = PersonSchema()
people_schema = PersonSchema(many=True)

Vous importez ma de config.py pour permettre à PersonSchema d'hériter de


ma.SQLAlchemyAutoSchema. Pour trouver un modèle SQLAlchemy et une
session SQLALchemy, SQLAlchemyAutoSchema recherche puis utilise cette
classe Meta interne.
Pour PersonSchema, le modèle est Person, et sqla_session est db.session. C'est
ainsi que Marshmallow trouve des attributs dans la classe Person et apprend les
types de ces attributs afin de savoir comment les sérialiser et les désérialiser.

Avec load_instance, vous êtes en mesure de désérialiser des données JSON et de


charger des instances de modèle Personne à partir de celles-ci. Enfin, vous
instanciez deux schémas, person_schema et people_schema, que vous utiliserez
plus tard.

Faites le ménage
Il est maintenant temps de se débarrasser de l'ancienne structure de données
PEOPLE. Cela permettra de s'assurer que toutes les modifications que vous
apportez aux données des personnes sont effectuées sur la base de données
plutôt que sur le dictionnaire obsolète PEOPLE.

Ouvrez people.py et débarrassez-vous des imports, fonctions et structures de


données dont vous n'avez plus besoin, et utilisez de nouveaux imports pour
ajouter db et les données de models.py :

# people.py

# Remove: from datetime import datetime


from flask import make_response, abort

from config import db


from models import Person, people_schema, person_schema
# Remove: get_timestamp():
# Remove: PEOPLE

# ...

Vous supprimez l'importation de datetime, la fonction get_timestamp() et le


dictionnaire PEOPLE. En échange, vous ajoutez des objets de la configuration et
des modèles que vous utiliserez à partir de maintenant.

Au moment où vous avez supprimé le dictionnaire PEOPLE, votre éditeur de


code Python (Python code editor) s'est peut-être plaint de la variable PEOPLE
non définie dans votre code. Dans la section suivante, vous remplacerez toutes
les références à PEOPLE par des requêtes de base de données et vous rendrez
votre éditeur Python heureux.

Connexion de la base de données avec votre API

Votre base de données est connectée à votre projet Flask mais pas encore à l'API
REST. Potentiellement, vous pourriez utiliser le shell interactif Python pour
ajouter plus de personnes à votre base de données. Mais il sera beaucoup plus
amusant d'améliorer votre API REST et d'utiliser les points de terminaison
existants pour ajouter des données !
Dans cette section, vous allez connecter votre API à la base de données, afin
d'utiliser vos points de terminaison existants avec la base de données pour gérer
les personnes. Si vous voulez récapituler la façon dont vous avez construit les
points de terminaison de l'API, vous pouvez vous rendre dans la première partie
de cette série de tutoriels.

Voici à quoi ressemble votre API REST Flask pour le moment :

Action HTTP Verb URL Path Description


Read GET /api/people Read a collection of people.
Create POST /api/people Create a new person.
Read GET /api/people/<lname> Read a particular person.
Update PUT /api/people/<lname> Update an existing person.
Delete DELETE /api/people/<lname> Delete an existing person.

Ensuite, vous allez mettre à jour les fonctions existantes connectées aux points
de terminaison listés ci-dessus afin qu'elles puissent travailler avec la base de
données people.db.

Lire la base de données


Tout d'abord, ajustez les fonctions dans people.py qui lisent les
données de la base de données sans rien écrire dans la base de
données. Commencez par read_all() :

# people.py

# ...

def read_all():
people = Person.query.all()
return people_schema.dump(people)
# ...

La fonction read_all() répond au point de terminaison de l'API REST GET


/api/people et renvoie tous les enregistrements de la table de la base de données
des personnes.

Vous utilisez people_schema qui est une instance de la classe PersonSchema de


Marshmallow créée avec le paramètre many=True. Avec ce paramètre, vous
indiquez à PersonSchema qu'il doit s'attendre à un interable à sérialiser. Ceci est
important car la variable people contient une liste d'éléments de la base de
données.

Enfin, vous sérialisez vos objets Python avec .dump() et renvoyez les données
de toutes les personnes comme réponse à l'appel API REST.

L'autre fonction de people.py qui ne reçoit que des données est read_one() :

# people.py

# ...

def read_one(lname):
person = Person.query.filter(Person.lname ==
lname).one_or_none()

if person is not None:


return person_schema.dump(person)
else:
abort(404, f"Person with last name {lname} not
found")

# ...

La fonction read_one() (one_or_none()) reçoit un paramètre lname à partir du


chemin d'accès de l'URL REST, indiquant que l'utilisateur recherche une
personne spécifique.

Vous utilisez lname dans la méthode .filter() de la requête. Plutôt que d'utiliser
la méthode .all(), vous utilisez la méthode .one_or_none() pour obtenir une
personne ou renvoyer None si aucune correspondance n'est trouvée.

Si une personne est trouvée, alors person contient un objet Person et vous
retournez l'objet sérialisé. Sinon, vous appelez abort() avec une erreur.

Écrire dans la base de données

Une autre modification de people.py consiste à créer une nouvelle personne dans
la base de données. Cela vous donne l'occasion d'utiliser le PersonSchema de
Marshmallow pour désérialiser une structure JSON envoyée avec la requête
HTTP pour créer un objet Person de SQLAlchemy. Voici une partie du module
people.py mis à jour montrant le gestionnaire pour le point de terminaison URL
REST POST /api/people :
# people.py

# ...

def create(person):
lname = person.get("lname")
existing_person = Person.query.filter(Person.lname ==
lname).one_or_none()

if existing_person is None:
new_person = person_schema.load(person,
session=db.session)
db.session.add(new_person)
db.session.commit()
return person_schema.dump(new_person), 201
else:
abort(406, f"Person with last name {lname} already
exists")

# ...
Au lieu de recevoir seulement un nom de famille comme dans read_one(),
create() reçoit un objet person. Cet objet doit contenir lname, qui ne doit pas
déjà exister dans la base de données. La valeur lname est votre identifiant pour
votre personne, donc vous ne pouvez pas avoir une personne avec le même nom
de famille plusieurs fois dans votre base de données.

Si le nom de famille est unique, vous désérialisez l'objet person en tant que
new_person et l'ajoutez à db.session. Lorsque vous livrez new_person à la base
de données, votre moteur de base de données attribue une nouvelle valeur de clé
primaire et un horodatage basé sur UTC à l'objet. Plus tard, vous verrez le jeu de
données créé dans la réponse de l'API.
Ajustez update() et delete() de la même manière que vous avez ajusté les autres
fonctions :

# people.py

# ...

def update(lname, person):


existing_person = Person.query.filter(Person.lname ==
lname).one_or_none()

if existing_person:
update_person = person_schema.load(person,
session=db.session)
existing_person.fname = update_person.fname
db.session.merge(existing_person)
db.session.commit()
return person_schema.dump(existing_person), 201
else:
abort(404, f"Person with last name {lname} not
found")

def delete(lname):
existing_person = Person.query.filter(Person.lname ==
lname).one_or_none()

if existing_person:
db.session.delete(existing_person)
db.session.commit()
return make_response(f"{lname} successfully
deleted", 200)
else:
abort(404, f"Person with last name {lname} not
found")
Avec tous ces changements en place, il est temps de mettre à jour votre code
frontal et d'utiliser Swagger UI pour vérifier si votre base de données fonctionne
comme prévu.

Afficher les données dans votre front-end

Maintenant que vous avez ajouté la configuration SQLite et défini votre modèle
Person, votre projet Flask contient toutes les informations nécessaires pour
travailler avec votre base de données. Avant de pouvoir afficher les données
dans le front-end, vous devez faire quelques ajustements à app.py :

# app.py
2
3from flask import render_template
4# Remove: import connexion
5import config
6from models import Person
7
8app = config.connex_app
9app.add_api(config.basedir / "swagger.yml")
10
[email protected]("/")
12def home():
13 people = Person.query.all()
14 return render_template("home.html", people=people)
15
16if __name__ == "__main__":
17 app.run(host="0.0.0.0", port=8000, debug=True)
Vous travaillez maintenant avec config.py et models.py. Vous supprimez donc
l'importation à la ligne 4 et ajoutez les importations pour config à la ligne 5 et
Person à la ligne 6.

Le module config fournit l'application Flask à la sauce Connexion pour vous.


Par conséquent, vous ne créez plus une nouvelle application Flask dans app.py,
mais vous faites référence à config.connex_app à la ligne 8.

À la ligne 13, vous interrogez le modèle Person pour obtenir toutes les données
de la table person et les transmettre à render_template() à la ligne 14.

Pour afficher les données des personnes dans le front-end, vous devez ajuster le
modèle home.html :

!-- templates/home.html -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RP Flask REST API</title>
</head>
<body>
<h1>
Hello, People!
</h1>
<ul>
{% for person in people %}
<li>{{ person.fname }} {{ person.lname }}</li>
{% endfor %}
</ul>
</body>
</html>

Vous pouvez exécuter votre application avec cette commande dans le répertoire
contenant le fichier app.py :

(venv) $ python app.py

Lorsque vous exécutez cette application, un serveur Web démarre sur le port
8000, qui est le port que vous avez défini dans app.py. Si vous ouvrez un
navigateur et naviguez vers http://localhost:8000, vous verrez les données de
votre base de données :

Génial ! Votre page d'accueil répertorie les trois personnes qui se trouvent
actuellement dans votre base de données. Enfin, vous pouvez utiliser Swagger
UI pour créer, mettre à jour et supprimer des personnes et voir les changements
se refléter sur la page d'accueil.
Explorez la documentation de votre API

Avec les changements ci-dessus en place, votre base de données est maintenant
fonctionnelle et persiste les données même lorsque vous redémarrez votre
application :

Vous pouvez tirer parti de votre API pour ajouter, mettre à jour et supprimer des
personnes. Grâce aux modifications que vous avez apportées au front-end, vous
êtes en mesure de voir toutes les personnes qui sont actuellement stockées dans
votre base de données.

Lorsque vous redémarrez votre application Flask, vous ne réinitialisez plus les
données. Puisque vous avez maintenant une base de données attachée à votre
projet Flask, vos données sont sauvegardées.

Conclusion
Félicitations, vous avez abordé de nombreux sujets dans ce tutoriel et ajouté des
outils utiles à votre arsenal !

Dans la deuxième partie de cette série de tutoriels, vous avez appris à :

 Écrire des commandes SQL en Python

 Configurer une base de données SQLite pour votre projet Flask


 Utiliser SQLAlchemy pour enregistrer des objets Python dans votre base
de données.

 Exploiter la bibliothèque Marshmallow pour sérialiser les données

 Connecter votre API REST à votre base de données

Les compétences que vous avez acquises sont certainement plus complexes que
l'API REST de la première partie, mais cette étape vous a donné des outils
puissants à utiliser pour créer des applications plus complexes. Leur utilisation
vous donnera une longueur d'avance pour créer vos propres applications Web
soutenues par une base de données.

Pour consulter le code de la deuxième partie de cette série de tutoriels, cliquez


ci-dessous :

Code source : Cliquez ici pour télécharger le code source gratuit que vous
utiliserez pour continuer à construire une API REST avec le framework web
Flask.

Dans la prochaine partie de cette série, vous allez étendre votre API REST afin
de pouvoir créer, lire, mettre à jour et supprimer des notes. Les notes seront
stockées dans une nouvelle table de base de données. Chaque note sera liée à
une personne, vous ajouterez donc des relations entre les notes et les personnes
dans votre base de données.
La troisième partie sera la dernière de cette série de tutoriels. À la fin, vous
disposerez d'une API REST Flask à part entière avec des tables de base de
données associées en arrière-plan.

API REST en Python avec Flask, Connexion et


SQLAlchemy - Partie 3

Table des matières


Démo

Planification de la troisième partie

Commencer à travailler

 Vérifiez votre projet Flask

 Inspecter l'ensemble de données

 Établir des relations avec les personnes

Étendre votre base de données

 Créer des modèles SQLAlchemy

 Alimenter la base de données

Afficher les personnes avec leurs notes

 Afficher les notes dans le front-end


 Répondre avec des notes

 Créer un schéma de notes

Gérer les notes avec votre API REST

 Lire une seule note

 Mettre à jour et supprimer une note

 Créer une note pour une personne

 Explorer la documentation de votre API

Conclusion
La plupart des applications Web modernes sont alimentées par une API REST
sous le capot. Ainsi, les développeurs peuvent séparer le code frontal de la
logique dorsale, et les utilisateurs peuvent interagir avec l'interface de manière
dynamique. Dans cette série de didacticiels en trois parties, vous construisez une
API REST à l'aide du framework Web Flask.

Vous avez créé une base avec un projet Flask de base et ajouté des points de
terminaison, que vous avez connectés à une base de données SQLite. Vous
testez également votre API à l'aide de la documentation Swagger UI API que
vous construisez en cours de route.

Dans la troisième partie de cette série de tutoriels, vous apprendrez à :

 Travailler avec plusieurs tables dans une base de données

 Créer des champs "un vers plusieurs" dans votre base de données

 Gérez les relations avec SQLAlchemy

 Exploiter les schémas imbriqués avec Marshmallow

 Afficher les objets liés dans le front-end


Vous pouvez télécharger le code de la troisième partie de ce projet en cliquant
sur le lien ci-dessous :

Démo
Dans cette série de tutoriels en trois parties, vous construisez une API REST
pour conserver la trace des notes des personnes qui peuvent vous rendre visite
tout au long de l'année. Vous allez créer des personnes comme la fée des dents
(Tooth Fairy,), le lapin de Pâques (Easter Bunny) et Knecht Ruprecht (Knecht
Ruprecht).

Idéalement, vous voulez être en bons termes avec ces trois personnes. C'est
pourquoi vous leur enverrez des notes, afin d'augmenter vos chances d'obtenir
des cadeaux de valeur de leur part.

Dans ce tutoriel, vous élargirez encore votre ceinture d'outils de programmation.


Vous apprendrez à créer des structures de données hiérarchiques représentées
sous forme de relations un à plusieurs (one-to-many ) par SQLAlchemy. En
outre, vous allez également étendre l'API REST que vous avez déjà construite
pour créer, lire, mettre à jour et supprimer les notes d'une personne :
Il est temps de terminer cette série de tutoriels en trois parties en créant des
relations entre les personnes et les notes !

Planification de la troisième partie


Dans la première partie de cette série, vous avez créé votre API REST. Dans la
deuxième partie, vous avez connecté votre API REST à une base de données.
Ainsi, votre application Flask peut apporter des modifications aux données
existantes et créer de nouvelles données qui persistent même lorsque vous
redémarrez votre serveur d'applications.

Jusqu'à présent, vous avez ajouté la possibilité d'enregistrer les changements


effectués via l'API REST dans une base de données en utilisant SQLAlchemy et
vous avez appris à sérialiser ces données pour l'API REST en utilisant
Marshmallow.

Actuellement, la base de données people.db ne contient que des données sur les
personnes. Dans cette partie de la série, vous allez ajouter une nouvelle table
pour stocker les notes. Pour relier les notes à une personne, vous allez créer des
relations entre les entrées de la table person et la table note de votre base de
données.
Vous allez amorcer people.db avec un script build_database.py qui contient les
données nécessaires sur les personnes et les notes. Voici un extrait de l'ensemble
de données avec lequel vous allez travailler :

PEOPLE_NOTES = [
{
"lname": "Fairy",
"fname": "Tooth",
"notes": [
("I brush my teeth after each meal.", "2022-
01-06 17:10:24"),
("The other day a friend said I have big
teeth.", "2022-03-05 22:17:54"),
("Do you pay per gram?", "2022-03-05
22:18:10"),
],
},
# ...
]
Vous apprendrez comment ajuster votre base de données SQLite pour mettre en
œuvre les relations. Ensuite, vous serez en mesure de traduire le dictionnaire
PEOPLE_NOTES en données conformes à la structure de votre base de
données.

Enfin, vous afficherez le contenu de votre base de données sur la page d'accueil
de votre application et utiliserez votre API REST Flask pour ajouter, mettre à
jour et supprimer les notes que vous rédigez pour les personnes.
Pour commencer
Idéalement, vous avez suivi la première partie et la deuxième partie de cette
série de tutoriels avant de poursuivre avec la troisième partie, que vous lisez en
ce moment. Sinon, vous pouvez également télécharger le code source de la
deuxième partie en cliquant sur le lien ci-dessous :

Si vous avez téléchargé le code source à partir du lien ci-dessus, assurez-vous de


suivre les instructions d'installation dans le fichier README.md fourni.

Avant de poursuivre le tutoriel, vérifiez que votre structure de dossiers


ressemble à ceci :

rp_flask_api/

├── templates/
│ └── home.html

├── app.py
├── config.py
├── models.py
├── people.py
└── swagger.yml

Une fois que vous avez mis en place la structure de dossiers de l'API REST de
Flask, vous pouvez vérifier si votre projet Flask fonctionne comme prévu.

Vérifiez votre projet de flask

Avant de continuer à travailler sur votre projet Flask, c'est une bonne idée de
créer et d'activer un environnement virtuel (virtual environment). De cette façon,
vous installez toutes les dépendances du projet non pas sur l'ensemble du
système mais uniquement dans l'environnement virtuel de votre projet.

Sélectionnez votre système d'exploitation ci-dessous et utilisez la commande


spécifique à votre plateforme pour configurer un environnement virtuel :

Windows PowerShell
PS> python -m venv venv
PS> .\venv\Scripts\activate
(venv) PS>

shell
$ python -m venv venv
$ source venv/bin/activate
(venv) $

Avec les commandes présentées ci-dessus, vous créez et activez un


environnement virtuel nommé venv en utilisant le module venv intégré de
Python. Les parenthèses (venv) devant l'invite indiquent que vous avez activé
avec succès l'environnement virtuel.

Note : Si vous n'avez pas encore travaillé sur la deuxième partie de cette série de
tutoriels, assurez-vous de télécharger le code source en cliquant sur le lien ci-
dessous :

Code source : Cliquez ici pour télécharger le code source gratuit que vous
utiliserez pour continuer à construire une API REST avec le framework web
Flask.

Avant de continuer, installez les dépendances en suivant les instructions


énumérées dans le fichier README.md fourni.

Vous pouvez maintenant vérifier que votre application Flask fonctionne sans
erreur. Exécutez la commande suivante dans le répertoire contenant le fichier
app.py :

(venv) $ python app.py

Lorsque vous exécutez cette application, un serveur Web démarre sur le port
8000. Si vous ouvrez un navigateur et naviguez vers http://localhost:8000, vous
devriez voir une page avec le titre Hello, People ! affiché :
Parfait, votre application fonctionne parfaitement ! Il est maintenant temps de
penser à la nouvelle structure de la base de données.

Inspecter l'ensemble de données


Avant de commencer à planifier la façon dont vous voulez ajuster votre base de
données, il est bon de jeter un coup d'œil aux données que votre base contient
actuellement et au jeu de données avec lequel vous allez travailler.

La table person de votre base de données people.db ressemble actuellement à


ceci :

id lname fname timestamp


1 Fairy Tooth 2022-10-08 09:15:10
2 Ruprecht Knecht 2022-10-08 09:15:13
3 Bunny Easter 2022-10-08 09:15:27
Vous allez commencer à étendre votre base de données avec
une liste PEOPLE_NOTES :

PEOPLE_NOTES = [
{
"lname": "Fairy",
"fname": "Tooth",
"notes": [
("I brush my teeth after each meal.", "2022-
01-06 17:10:24"),
("The other day a friend said, I have big
teeth.", "2022-03-05 22:17:54"),
("Do you pay per gram?", "2022-03-05
22:18:10"),
],
},
{
"lname": "Ruprecht",
"fname": "Knecht",
"notes": [
("I swear, I'll do better this year.", "2022-
01-01 09:15:03"),
("Really! Only good deeds from now on!",
"2022-02-06 13:09:21"),
],
},
{
"lname": "Bunny",
"fname": "Easter",
"notes": [
("Please keep the current inflation rate in
mind!", "2022-01-07 22:47:54"),
("No need to hide the eggs this time.", "2022-
04-06 13:03:17"),
],
},
]
Notez que les valeurs de lname dans PEOPLE_NOTES correspondent au
contenu de votre colonne lname dans la table person de votre base de données
people.db.

Dans l'ensemble de données ci-dessus, chaque personne comprend une clé


appelée notes, qui est associée à une liste contenant des tuples de données.
Chaque tuple de la liste des notes représente une note unique contenant le
contenu et un horodatage.

Chaque personne est associée à plusieurs notes, et chaque note est associée à une
seule personne. Cette hiérarchie de données est connue sous le nom de relation
un-à-plusieurs (one-to-many), où un seul objet parent est lié à plusieurs objets
enfants. Vous verrez plus loin dans ce tutoriel comment cette relation est gérée
dans la base de données avec SQLAlchemy.

Établir des relations avec les gens


id person_id content timestamp
1 1 I brush my teeth after each meal. 2022-01-06 17:10:24
2 1 The other day a friend said, I have big teeth. 2022-03-05 22:17:54
3 1 Do you pay per gram? 2022-03-05 22:18:10
4 2 I swear, I’ll do better this year. 2022-01-01 09:15:03
5 2 Really! Only good deeds from now on! 2022-02-06 13:09:21
6 3 Please keep the current inflation rate in mind! 2022-01-07 22:47:54
7 3 No need to hide the eggs this time. 2022-04-06 13:03:17
Remarquez que, comme la table des personnes, la table des notes possède un
identifiant unique appelé id, qui est la clé primaire (foreign key) de la table des
notes. La colonne person_id crée la relation avec la table person.

Alors que id est la clé primaire de la table, person_id est ce que l'on appelle une
clé étrangère. La clé étrangère donne à chaque entrée de la table des notes la clé
primaire de l'enregistrement de la personne à laquelle elle est associée. Grâce à
cela, SQLAlchemy peut rassembler toutes les notes associées à chaque personne
en reliant la clé primaire person.id à la clé étrangère note.person_id, créant ainsi
une relation.

En divisant l'ensemble de données en deux tables et en introduisant le concept


de clé étrangère, vous rendrez les données un peu plus complexes à appréhender.
Mais vous résoudrez les inconvénients d'une représentation en table unique.

Le plus grand avantage des tables liées est qu'il n'y a pas de données
redondantes dans la base de données. Il n'y a qu'une seule entrée pour chaque
personne que vous voulez stocker dans la base de données.

Si le lapin de Pâques veut toujours changer les noms, il suffit de modifier une
seule ligne dans la table des personnes, et tout ce qui est lié à cette ligne
profitera immédiatement du changement.

En outre, la dénomination des colonnes est plus cohérente et plus significative.


Comme les données relatives aux personnes et aux notes existent dans des tables
distinctes, l'horodatage de création ou de mise à jour peut être nommé de
manière cohérente dans les deux tables, car il n'y a pas de conflit de noms entre
les tables.

Mais assez parlé de théorie ! Dans la section suivante, vous allez créer les
modèles qui représentent les relations entre les tables de la base de données que
vous avez imaginées.

Extension de votre base de données


Dans cette section, vous allez étendre votre base de données. Vous allez
modifier la structure de données People dans models.py pour donner à chaque
personne une liste de notes qui lui sont associées. Enfin, vous allez alimenter la
base de données avec quelques données initiales.

Créer des modèles SQLAlchemy


# models.py

from datetime import datetime


from config import db, ma

class Person(db.Model):
__tablename__ = "person"
person_id = db.Column(db.Integer, primary_key=True)
lname = db.Column(db.String(32), unique=True)
fname = db.Column(db.String(32))
timestamp = db.Column(
db.DateTime, default=datetime.utcnow,
onupdate=datetime.utcnow
)
notes = db.relationship(
Note,
backref="person",
cascade="all, delete, delete-orphan",
single_parent=True,
order_by="desc(Note.timestamp)"
)

# ...

Aux lignes 14 à 20, vous créez un nouvel attribut dans la classe Personne
appelé .notes. Ce nouvel attribut .notes est défini dans les lignes de code
suivantes :

 Ligne 14 : comme vous l'avez fait pour les autres attributs de la classe,
vous créez ici un nouvel attribut appelé .notes et le rendez égal à une
instance d'un objet appelé db.relationship. Cet objet crée la relation que
vous ajoutez à la classe Person, et il est créé avec tous les paramètres
définis dans les lignes qui suivent.

 Ligne 15 : le paramètre Note définit la classe SQLAlchemy à laquelle la


classe Personne sera liée. La classe Note n'est pas encore définie, donc
elle ne fonctionnera pas pour le moment. Parfois, il peut être plus facile de
faire référence aux classes sous forme de chaînes de caractères pour éviter
les problèmes liés à la classe définie en premier. Par exemple, vous
pouvez utiliser "Note" au lieu de Note ici.

 Ligne 16 : le paramètre backref="person" crée ce que l'on appelle une


référence inversée dans les objets Note. Chaque instance de Note contient
un attribut appelé .person. L'attribut .person fait référence à l'objet parent
auquel une instance particulière de Note est associée. Avoir une référence
à l'objet parent (Person dans ce cas) dans l'enfant peut être très utile si
votre code itère sur les notes et doit inclure des informations sur le parent.
 Ligne 17 : le paramètre cascade="all, delete, delete-orphan" détermine
comment traiter les instances de Note lorsque des modifications sont
apportées à l'instance de Personne parente. Par exemple, lorsqu'un objet
Personne est supprimé, SQLAlchemy crée le SQL nécessaire pour
supprimer l'objet Personne de la base de données. Ce paramètre indique à
SQLAlchemy de supprimer également toutes les instances de note qui lui
sont associées. Vous pouvez en savoir plus sur ces options dans la
documentation de SQLAlchemy.

 Ligne 18 : Le paramètre single_parent=True est requis si delete-orphan


fait partie du paramètre de cascade précédent. Il indique à SQLAlchemy
de ne pas autoriser l'existence d'une instance de Note orpheline (c'est-à-
dire une Note sans objet Personne parent), car chaque Note a un seul
parent.

 Ligne 19 : Le paramètre order_by="desc(Note.timestamp)" indique à


SQLAlchemy comment trier les instances de Note associées à un objet
Personne. Lorsqu'un objet Personne est récupéré, la liste d'attributs des
notes contient par défaut des objets Note dans un ordre inconnu. La
fonction SQLAlchemy desc() triera les notes dans l'ordre décroissant du
plus récent au plus ancien, plutôt que dans l'ordre croissant par défaut.

Maintenant que votre modèle Person possède le nouvel attribut. notes, et que
celui-ci représente la relation un-à-plusieurs avec les objets Note, vous devez
définir un modèle SQLAlchemy pour un objet Note. Puisque vous faites
référence à Note à partir de Person, ajoutez la nouvelle classe Note juste avant la
définition de la classe Person :
# models.py
2
3from datetime import datetime
4from config import db, ma
5
6class Note(db.Model):
7 __tablename__ = "note"
8 id = db.Column(db.Integer, primary_key=True)
9 person_id = db.Column(db.Integer,
db.ForeignKey("person.id"))
10 content = db.Column(db.String, nullable=False)
11 timestamp = db.Column(
12 db.DateTime, default=datetime.utcnow,
onupdate=datetime.utcnow
13 )
14
15class Person(db.Model):
16 # ...
17
18# ...

 La classe Note définit les attributs qui composent une note, comme vous
l'avez appris dans votre exemple de table de base de données de notes ci-
dessus. Avec ce code, vous définissez les attributs :

 La ligne 6 crée la classe Note, héritant de db.Model, exactement comme


vous l'avez fait précédemment en créant la classe Person.

 La ligne 7 indique à la classe la table de base de données à utiliser pour


stocker les objets Note.
 La ligne 8 crée l'attribut .id, le définissant comme une valeur entière et
comme la clé primaire de l'objet Note.

 La ligne 9 crée l'attribut .person_id et le définit comme clé étrangère,


reliant la classe Note à la classe Person à l'aide de la clé
primaire .person.id. C'est grâce à cet attribut et à l'attribut Person.notes
que SQLAlchemy sait quoi faire lorsqu'il interagit avec les objets Person
et Note.

 La ligne 10 crée l'attribut .content, qui contient le texte réel de la note. Le


paramètre nullable=False indique que les nouvelles notes doivent contenir
du contenu.

 Les lignes 11 à 13 créent l'attribut .timestamp, et exactement comme dans


la classe Person, cet attribut contient l'heure de création ou de mise à jour
d'une instance particulière de Note.

Maintenant que vous avez mis à jour la classe Personne et créé le modèle de
Note, passez à la mise à jour de la base de données.

Alimenter la base de données

Maintenant que vous avez mis à jour Person et créé le modèle Note, vous allez
les utiliser pour reconstruire la base de données people.db. Pour ce faire, créez
un script Python d'aide nommé build_database.py :
# build_database.py

from datetime import datetime


from config import app, db
from models import Person, Note

PEOPLE_NOTES = [
{
"lname": "Fairy",
"fname": "Tooth",
"notes": [
("I brush my teeth after each meal.", "2022-
01-06 17:10:24"),
("The other day a friend said, I have big
teeth.", "2022-03-05 22:17:54"),
("Do you pay per gram?", "2022-03-05
22:18:10"),
],
},
{
"lname": "Ruprecht",
"fname": "Knecht",
"notes": [
("I swear, I'll do better this year.", "2022-
01-01 09:15:03"),
("Really! Only good deeds from now on!",
"2022-02-06 13:09:21"),
],
},
{
"lname": "Bunny",
"fname": "Easter",
"notes": [
("Please keep the current inflation rate in
mind!", "2022-01-07 22:47:54"),
("No need to hide the eggs this time.", "2022-
04-06 13:03:17"),
],
},
]

with app.app_context():
db.drop_all()
db.create_all()
for data in PEOPLE_NOTES:
new_person = Person(lname=data.get("lname"),
fname=data.get("fname"))
for content, timestamp in data.get("notes", []):
new_person.notes.append(
Note(
content=content,
timestamp=datetime.strptime(timestamp,
"%Y-%m-%d %H:%M:%S"),
)
)
db.session.add(new_person)
db.session.commit()
Dans le code ci-dessus, vous alimentez la base de données de votre projet avec
le contenu de PEOPLE_NOTES. Vous utilisez db dans votre module de
configuration afin que Python sache comment traiter les données et les
transmettre aux tables et cellules correspondantes de la base de données.

Note : Lorsque vous exécutez build_database.py, vous allez recréer


people.db. Toutes les données existantes dans people.db seront perdues.

L'exécution du programme build_database.py à partir de la ligne de commande


recréera la base de données avec les nouveaux ajouts, la rendant ainsi prête à
être utilisée avec l'application Web :

(venv) $ python build_database.py


Une fois que votre projet contient une base de données fraîche, vous pouvez
ajuster votre projet pour afficher les notes dans le front-end.

Afficher les personnes avec leurs notes

Maintenant que votre base de données contient des données avec lesquelles vous
pouvez travailler, vous pouvez commencer à afficher les données à la fois dans
le front-end et dans votre API REST.

Afficher les notes dans le front End


Dans la section précédente, vous avez créé la relation entre une personne et ses
notes en ajoutant un attribut .notes à la classe Personne.

Mettez à jour le fichier home.html dans votre dossier templates/ pour accéder
aux notes d'une personne :

<!-- templates/home.html -->

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RP Flask REST API</title>
</head>
<body>
<h1>
Hello, People!
</h1>
{% for person in people %}
<h2>{{ person.fname }} {{ person.lname }}</h2>
<ul>
{% for note in person.notes %}
<li>
{{ note.content }}
</li>
{% endfor %}
</ul>
{% endfor %}
</body>
</html>

Dans le code ci-dessus, vous accédez à l’attribut. notes de chaque personne.


Ensuite, vous parcourez en boucle toutes les notes d'une personne particulière
pour accéder au contenu d'une note.

Rendez-vous sur le site http://localhost:8000 pour vérifier si votre modèle a le


rendu attendu :
Parfait, vous pouvez voir les notes de chaque personne listées dans votre front-
end. Cela signifie que Flask réussit à connecter Personne et Notes sous le capot
et vous fournit un objet Personne avec lequel vous pouvez travailler de manière
pratique.

Répondre avec des notes


Ensuite, vérifiez le point de terminaison /api/people de votre API à l'adresse

http://localhost:8000/api/people :
Vous recevez la collection de personnes sans aucune erreur. Cependant, il n'y a
pas de notes dans les données que vous recevez.

Pour étudier le problème, jetez un coup d'oeil à read_all() dans people.py :

# people.py
2
3# ...
4
5def read_all():
6 people = Person.query.all()
7 person_schema = PersonSchema(many=True)
8 return person_schema.dump(people)
9
10# ...

La méthode .dump() de la ligne 8 fonctionne avec ce qu'elle reçoit et ne filtre


aucune donnée. Le problème peut donc provenir de la définition de people à la
ligne 6 ou de person_schema à la ligne 7.
L'appel de requête à la base de données pour remplir les personnes est
exactement le même que celui de app.py.

Person.query.all()

Cet appel a fonctionné avec succès dans le front-end pour montrer les notes pour
chaque personne. Cela montre que PersonSchema est le coupable le plus
probable.

Par défaut, un schéma Marshmallow ne traverse pas les objets de base de


données liés. Vous devez demander explicitement à un schéma d'inclure des
relations.

Ouvrez models.py et mettez à jour PersonSchema :


# models.py

# ...

class PersonSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Person
load_instance = True
sqla_session = db.session
include_relationships = True

Avec include_relationships dans la classe Meta de PersonSchema, vous indiquez


à Marshmallow d'ajouter tout objet connexe au schéma de la personne.
Cependant, le résultat n'est toujours pas celui escompté :
La réponse à http://localhost:8000/api/people contient maintenant les notes de
chaque personne. Mais au lieu de montrer toutes les données qu'une note
contient, l'objet notes ne contient qu'une liste de clés primaires.

Créer un schéma Notes


# models.py

# ...

class Note(db.Model):
# ...

class NoteSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Note
load_instance = True
sqla_session = db.session
include_fk = True
class Person(db.Model):
# ...

class PersonSchema(ma.SQLAlchemyAutoSchema):
# ...

note_schema = NoteSchema()
# ...

Vous faites référence à Note à partir de NoteSchema, vous devez donc placer
NoteSchema sous la définition de votre classe Note pour éviter les erreurs. Vous
instanciez également NoteSchema pour créer un objet auquel vous ferez
référence ultérieurement.

Comme votre modèle Note contient une clé étrangère, vous devez définir
include_fk sur True. Sinon, Marshmallow ne reconnaîtrait pas person_id
pendant le processus de sérialisation.

Une fois le NoteSchema en place, vous pouvez le référencer dans le


PeopleSchema :

# models.py

from datetime import datetime


from marshmallow_sqlalchemy import fields

from config import db, ma

# ...
class PersonSchema(ma.SQLAlchemyAutoSchema):
class Meta:
model = Person
load_instance = True
sqla_session = db.session
include_relationships = True

notes = fields.Nested(NoteSchema, many=True)

Après avoir importé des champs de marshmallow_sqlalchemy, vous pouvez


faire référence à l'objet Note associé par son NoteSchema. Pour éviter de
rencontrer des erreurs, vérifiez que vous avez défini NoteSchema avant
PeopleSchema.

Bien que vous travailliez avec SQLAlchemyAutoSchema, vous devez créer


explicitement le champ Note dans PersonSchema. Sinon, Marshmallow ne reçoit
pas toutes les informations dont il a besoin pour travailler avec les données
Notes. Par exemple, il ne saura pas que vous attendez une liste d'objets en
utilisant l'argument many.

Une fois les modifications apportées, vérifiez le point de terminaison de votre


API à l'adresse http://localhost:8000/api/people :
Parfait, votre fonction read_all() renvoie non seulement toutes les personnes,
mais aussi toutes les notes qui sont attachées à chaque personne !

Dans la section suivante, vous allez étendre votre API REST Flask pour créer,
lire, mettre à jour et supprimer une seule note.

Gérer les notes avec votre API REST

Vous avez mis à jour les modèles SQLAlchemy et les avez utilisés pour lire la
base de données people.db. Vos notes sont disponibles en tant que schéma
imbriqué dans People. Vous recevez la liste des notes lorsque vous demandez
une collection de personnes ou une personne particulière :
Action HTTP Verb URL Path Description
Read GET /api/people Read a collection of people.
Read GET /api/people/<lname> Read a particular person.

Bien que vous puissiez lire les notes sur les points de terminaison indiqués dans
le tableau ci-dessus, il n'y a actuellement aucun moyen de lire une seule note ou
de gérer toutes les notes dans votre API REST.

Remarque : les paramètres de l'URL sont sensibles à la casse. Par


exemple, vous devez visiter http://localhost:8000/api/people/Ruprecht
avec un R majuscule dans le nom de famille Ruprecht.

Vous pouvez passer à la première partie pour récapituler comment vous avez
construit les points de terminaison existants de votre API REST. Dans cette
section du tutoriel, vous allez ajouter des points d'accès supplémentaires pour
fournir des fonctionnalités de création, de lecture, de mise à jour et de
suppression des notes :

Action HTTP Verb URL Path Description


Create POST /api/notes URL to create a new note
Read GET /api/notes/<note_id> URL to read a single note
Update PUT api/notes/<note_id> URL to update a single note
Delete DELETE api/notes/<note_id> URL to delete a single note

Vous allez commencer par ajouter la fonctionnalité de lecture d'une seule note.
Pour ce faire, vous allez ajuster votre fichier de configuration Swagger qui
contient vos définitions d'API.
Lire une seule note

Actuellement, vous êtes en mesure de recevoir toutes les notes d'une personne
lorsque vous demandez des données sur cette personne en particulier. Pour
obtenir des informations sur une note, vous ajouterez un autre point de
terminaison.

Avant d'ajouter le point de terminaison, mettez à jour votre configuration


Swagger en créant un composant paramètre note_id dans le fichier
swagger.yml :

# swagger.yml

# ...

components:
schemas:
# ...

parameters:
lname:
# ...
note_id:
name: "note_id"
description: "ID of the note"
in: path
required: true
schema:
type: "integer"
# ...
Le note_id dans les paramètres fera partie de vos endpoints pour identifier quelle
note vous voulez traiter.
Continuez à éditer swagger.yml et ajoutez les données pour le point final pour
lire une seule note :

# swagger.yml

# ...

paths:
/people:
# ...
/people/{lname}:
# ...
/notes/{note_id}:
get:
operationId: "notes.read_one"
tags:
- Notes
summary: "Read one note"
parameters:
- $ref: "#/components/parameters/note_id"
responses:
"200":
description: "Successfully read one note"

La structure de /notes/{note_id} est similaire à celle de /people/{lname}. Vous


commencez par l'opération get pour le chemin /notes/{note_id}. La sous-chaîne
{note_id} est un caractère de remplacement pour l'ID d'une note que vous devez
transmettre en tant que paramètre d'URL. Ainsi, par exemple, l'URL
http://localhost:8000/api/notes/1 vous donnera les données pour la note avec la
clé primaire 1.
L'operationId pointe vers notes.read_one. Cela signifie que votre API attend une
fonction read_one() dans un fichier notes.py. Allez-y, créez notes.py et ajoutez
read_one() :

# notes.py

from flask import abort, make_response

from config import db


from models import Note, note_schema

def read_one(note_id):
note = Note.query.get(note_id)

if note is not None:


return note_schema.dump(note)
else:
abort(
404, f"Note with ID {note_id} not found"
)

Bien que vous n'utilisiez pas encore make_response() et db, vous pouvez aller de
l'avant et les ajouter à vos importations. Vous les utiliserez plus tard lorsque
vous écrirez dans la base de données.

Pour l'instant, vous ne lisez la base de données qu'avec le paramètre note_id du


chemin de l'URL REST. Vous utilisez note_id dans la méthode .get() de la
requête pour obtenir la note avec la clé primaire de l'entier note_id.
Si une note est trouvée, alors note contient un objet Note et vous retournez
l'objet sérialisé. Allez-y et essayez-le en visitant
http://localhost:8000/api/notes/1 dans votre navigateur :

Parfait, la réponse API avec l'ensemble de données de la note se présente


exactement comme prévu ! Ensuite, vous allez utiliser le même point de
terminaison pour mettre à jour et supprimer une note.

Mettre à jour et supprimer une note

Cette fois, vous commencez par créer les fonctions dans notes.py, avant de créer
les opérations dans swagger.yml.

Ajoutez update() et delete() à notes.py :

# notes.py

# ...

def update(note_id, note):


existing_note = Note.query.get(note_id)

if existing_note:
update_note = note_schema.load(note,
session=db.session)
existing_note.content = update_note.content
db.session.merge(existing_note)
db.session.commit()
return note_schema.dump(existing_note), 201
else:
abort(404, f"Note with ID {note_id} not found")

def delete(note_id):
existing_note = Note.query.get(note_id)

if existing_note:
db.session.delete(existing_note)
db.session.commit()
return make_response(f"{note_id} successfully
deleted", 204)
else:
abort(404, f"Note with ID {note_id} not found")

Lorsque vous comparez update() et delete(), vous constatez qu'elles partagent


une structure similaire. Les deux fonctions recherchent une note existante et
travaillent avec une session de base de données.

Pour que la fonction update() fonctionne, vous devez également accepter un


objet note comme argument, qui contient l'attribut .content que vous pouvez
mettre à jour.

En revanche, vous devez seulement connaître l'ID de la note dont vous voulez
vous débarrasser lorsque vous appelez delete().
Ensuite, créez deux opérations dans swagger.yml qui font référence à
notes.update et notes.delete :

# swagger.yml

# ...

paths:
/people:
# ...
/people/{lname}:
# ...
/notes/{note_id}:
get:
# ...
put:
tags:
- Notes
operationId: "notes.update"
summary: "Update a note"
parameters:
- $ref: "#/components/parameters/note_id"
responses:
"200":
description: "Successfully updated note"
requestBody:
content:
application/json:
schema:
x-body-name: "note"
type: "object"
properties:
content:
type: "string"
delete:
tags:
- Notes
operationId: "notes.delete"
summary: "Delete a note"
parameters:
- $ref: "#/components/parameters/note_id"
responses:
"204":
description: "Successfully deleted note"

Encore une fois, la structure de put et delete est similaire. La principale


différence est que vous devez fournir un requestBody qui contient les données
de la note pour mettre à jour l'objet de la base de données.

Vous avez maintenant créé les points de terminaison pour travailler avec des
notes existantes. Ensuite, vous allez ajouter le point final pour créer une note.
Créer une note pour une personne
Jusqu'à présent, vous pouvez lire, mettre à jour et supprimer une seule note. Ce
sont des actions que vous pouvez effectuer sur des notes existantes. Il est
maintenant temps d'ajouter la fonctionnalité à votre API REST pour créer
également une nouvelle note.

Ajoutez create() à notes.py :

# notes.py

from flask import make_response, abort

from config import db


from models import Note, Person, note_schema

# ...

def create(note):
person_id = note.get("person_id")
person = Person.query.get(person_id)

if person:
new_note = note_schema.load(note,
session=db.session)
person.notes.append(new_note)
db.session.commit()
return note_schema.dump(new_note), 201
else:
abort(
404,
f"Person not found for ID: {person_id}"
)
Une note a toujours besoin d'une personne à qui elle appartient. C'est pourquoi
vous devez travailler avec le modèle Person lorsque vous créez une nouvelle
note.

Tout d'abord, vous recherchez le propriétaire de la note en utilisant person_id,


que vous fournissez avec l'argument notes de create(). Si cette personne existe
dans la base de données, vous ajoutez la nouvelle note à person.notes.

Bien que vous travailliez dans ce cas avec la table de la base de données des
personnes, SQLAlchemy se chargera d'ajouter la note à la table note.

Pour accéder à notes.create avec votre API, passez à swagger.yml et ajoutez un


autre point de terminaison

# swagger.yml

# ...

paths:
/people:
# ...
/people/{lname}:
# ...
/notes:
post:
operationId: "notes.create"
tags:
- Notes
summary: "Create a note associated with a person"
requestBody:
description: "Note to create"
required: True
content:
application/json:
schema:
x-body-name: "note"
type: "object"
properties:
person_id:
type: "integer"
content:
type: "string"
responses:
"201":
description: "Successfully created a note"
/notes/{note_id}:
# ...

Vous ajoutez le point de terminaison /notes juste avant le point de terminaison


/notes/{noted_id}. De cette façon, vous ordonnez vos points de terminaison de
notes du général au spécifique. Cet ordre vous aide à naviguer dans votre fichier
swagger.yml lorsque votre API s'agrandit.

Avec les données du bloc de schéma, vous fournissez à Marshmallow les


informations sur la façon de sérialiser une note dans votre API. Si vous
comparez ce schéma de note au modèle de note dans models.py, vous
remarquerez que les noms person_id et content correspondent. Il en va de même
pour les types de champs.

Vous pouvez également remarquer que tous les champs du modèle Note ne sont
pas présents dans le schéma du composant. Ce n'est pas grave, car vous
n'utiliserez ce schéma que pour enregistrer de nouvelles notes. Pour chaque note,
l'identifiant et l'horodatage seront définis automatiquement.
Maintenant que tous les points de terminaison pour la gestion des notes sont en
place, il est temps de jeter un coup d'oeil à la documentation de votre API.

Explorez la documentation de votre API

Une fois les modifications ci-dessus mises en place, vous


pouvez exploiter votre API pour ajouter, mettre à jour et
supprimer des notes. Visitez votre interface utilisateur
Swagger à l'adresse http://localhost:8000/api/ui et explorez
vos points de terminaison API :

Génial, les points de terminaison de votre API REST Flask


fonctionnent ! Tous les changements que vous effectuez avec
votre API apparaissent sur votre front-end, aussi.
Conclusion

Dans ce tutoriel, vous avez ajusté votre base de données SQLite pour mettre en
œuvre des relations. Ensuite, vous avez traduit le dictionnaire PEOPLE_NOTES
en données conformes à la structure de votre base de données, et vous avez
transformé votre API REST Flask en une application Web de prise de notes.

Dans la troisième partie de cette série de tutoriels, vous avez appris à :

 Travailler avec plusieurs tables dans une base de données.

 créer des champs un-à-plusieurs dans votre base de données

 gérer les relations avec SQLAlchemy

 exploiter les schémas imbriqués avec Marshmallow

 Afficher les objets liés dans le front-end

Savoir comment construire et utiliser les relations dans une base de données
vous donne un outil puissant pour résoudre de nombreux problèmes difficiles. Il
existe d'autres relations que l'exemple one-to-many de ce tutoriel. Les autres
relations courantes sont de type un à un, plusieurs à plusieurs et plusieurs à un.
Toutes ont leur place dans votre ceinture d'outils, et SQLAlchemy peut vous
aider à les aborder toutes !

Vous avez construit avec succès une API REST pour garder la trace des notes
des personnes qui peuvent vous rendre visite tout au long de l'année. Votre base
de données contient des personnes comme la fée des dents, le lapin de Pâques et
Knecht Ruprecht. En ajoutant des notes, vous pouvez garder une trace de vos
bonnes actions et espérer recevoir des cadeaux de valeur de leur part.

Pour revoir votre code, cliquez sur le lien ci-dessous :

Code source : Cliquez ici pour télécharger le code source gratuit que vous
utiliserez pour finir de construire une API REST avec le framework web Flask.

Avez-vous ajouté une personne ou une note spéciale à votre projet d'API REST
Flask ? Faites-le savoir à la communauté Real Python dans les commentaires ci-
dessous.

Vous aimerez peut-être aussi