257 lines
9.7 KiB
Markdown
257 lines
9.7 KiB
Markdown
# TP : Galerie d'images (flask)
|
||
|
||
## Informations générales
|
||
|
||
**Cours** : Python Avancé > Web > Flask \
|
||
**Objectifs pédagogiques** :
|
||
- Python avancé : framework Flask pour le développement web
|
||
- Outils modernes (poetry, PyCharm)
|
||
- Bonnes pratiques de l'entreprise
|
||
|
||
---
|
||
|
||
## Prérequis
|
||
|
||
### Installation et configuration de l’environnement
|
||
|
||
Installer les dépendances avec `poetry install` depuis un terminal PyCharm.
|
||
|
||
### Connaissances préalables
|
||
|
||
- Connaissances de base en programmation
|
||
|
||
---
|
||
|
||
## Énoncé
|
||
|
||
### Contexte
|
||
|
||
L'objectif de ce TP Bonus est de développer une application web simple mais robuste avec le micro-framework Flask.
|
||
|
||
Vous êtes invité à lire les premières sections de la [documentation officielle de Flask](https://flask.palletsprojects.com/en/stable/quickstart/) pour vous familiariser avec les concepts de base, avant de commencer le TP.
|
||
|
||
Cette application web affichera une galerie d'images à partir de fichiers stockés localement sur le serveur.
|
||
|
||
Les images étant fournies par le serveur avec différentes largeurs, cela permettra au client d'optimiser la performance réseau en fonction de la taille de l'image réelle à afficher côté client (navigateur).
|
||
|
||
Pour vous concentrer sur la logique métier et le fonctionnement de Flask, une partie significative du code vous est déjà fournie :
|
||
|
||
* Les **templates HTML** (`layout.html`, `gallery.html`, `404.html`) qui gèrent l'affichage.
|
||
* Les **fichiers statiques** (CSS Bootstrap) pour le design, ainsi que les images statiques.
|
||
* Un module utilitaire `utils.py` contenant une fonction `build_image_structure` qui analyse et structure les données du répertoire contenant les images.
|
||
* Une suite de **tests automatisés** (`test_app.py`) qui vous permettra de valider votre travail au fur et à mesure.
|
||
|
||
Votre mission est de compléter le fichier `src/app/views.py` pour faire le lien entre les requêtes des utilisateurs, les données des images et les templates HTML.
|
||
|
||
### Objectifs pédagogiques
|
||
|
||
À la fin de ce TP, vous saurez :
|
||
|
||
* Créer et organiser des routes (vues) dans une application Flask à l'aide de Blueprints.
|
||
* Faire le rendu de templates HTML en leur transmettant des données dynamiques (`render_template`).
|
||
* Gérer les erreurs de manière élégante (`try...except`) pour rendre une application plus robuste.
|
||
* Créer un premier endpoint d'API simple qui retourne des données au format JSON.
|
||
* Utiliser une suite de tests (Pytest) pour guider et valider votre développement (approche TDD-lite).
|
||
* Lancer et déboguer une application Flask localement.
|
||
|
||
### Mise en place
|
||
|
||
Le projet utilise `poetry` pour la gestion des dépendances.
|
||
|
||
La structure du projet est la suivante :
|
||
|
||
```
|
||
.
|
||
├── poetry.lock
|
||
├── pyproject.toml
|
||
├── src
|
||
│ └── app
|
||
│ ├── __init__.py # Point d'entrée de l'application
|
||
│ ├── static/ # Fichiers CSS, et images
|
||
│ ├── templates/ # Fichiers HTML (templates Jinja2)
|
||
│ ├── utils.py # Fonctions utilitaires (fourni)
|
||
│ └── views.py # Le fichier à compléter !
|
||
└── tests
|
||
└── test_views.py # Tests pour valider votre code (fourni)
|
||
```
|
||
|
||
### Travail à réaliser
|
||
|
||
Ouvrez le fichier `src/app/views.py`. Il est actuellement presque vide. Vous allez le compléter étape par étape.
|
||
|
||
#### Étape 1 : afficher la galerie d'images
|
||
|
||
Votre premier objectif est de créer la vue principale qui affiche la galerie.
|
||
|
||
**Tâche :** Créez une fonction `index()` qui répond à la route `/` (la racine du site).
|
||
|
||
**Logique à implémenter dans la fonction `index()` :**
|
||
|
||
1. Construisez le chemin vers le dossier contenant les images. Ce dossier se trouve dans `static/images`. Utilisez `current_app.static_folder` de Flask et la bibliothèque `pathlib` pour un code propre et portable.
|
||
|
||
2. Appelez la fonction `build_image_structure()` (importée depuis `.utils`) en lui passant le chemin du dossier des images.
|
||
|
||
3. **Rendez votre code robuste !** Encadrez l'appel à cette fonction dans un bloc `try...except`. En cas d'erreur (par exemple, si le dossier n'existe pas ou si un autre problème survient), vous devez éviter que l'application ne plante.
|
||
|
||
* En cas de succès, vous passez les données des images au template.
|
||
|
||
* Si aucune image n'est trouvée par la fonction, prévoyez un message d'erreur à afficher.
|
||
|
||
* En cas d'exception, logguez l'erreur et préparez un message d'erreur générique pour l'utilisateur.
|
||
|
||
4. Faites le rendu du template `gallery.html` en utilisant `render_template()`. Vous devez lui passer deux variables :
|
||
|
||
* `structured_images`: Le dictionnaire d'images retourné par `build_image_structure`.
|
||
|
||
* `error_message`: Un message d'erreur (ou `None` si tout va bien).
|
||
|
||
**Diagramme de flux (Étape 1) :**
|
||
|
||
```mermaid
|
||
graph TD
|
||
subgraph Client[Navigateur]
|
||
A[Utilisateur]
|
||
end
|
||
subgraph Serveur[Serveur Flask]
|
||
B["Route /"]
|
||
C["views.py: index()"]
|
||
D["utils.py: build_image_structure()"]
|
||
E["templates/gallery.html"]
|
||
F[Réponse HTML]
|
||
end
|
||
subgraph SystemeFichiers[Disque Serveur]
|
||
G["static/images"]
|
||
end
|
||
|
||
A -- "1. Requête GET /" --> B
|
||
B -- "2. Déclenche" --> C
|
||
C -- "3. Construit chemin et appelle" --> D
|
||
D -- "4. Lit le dossier" --> G
|
||
G -- "5. Retourne la structure" --> D
|
||
D -- "6. Retourne dict_images" --> C
|
||
C -- "7. Injecte données dans" --> E
|
||
E -- "8. Génère" --> F
|
||
F -- "9. Envoie" --> A
|
||
```
|
||
|
||
**Logique de gestion d'erreur (commune aux étapes 1 et 4) :**
|
||
|
||
```mermaid
|
||
graph TD
|
||
A[Début de la vue] --> B{try}
|
||
B -- "Succès" --> C["Appel: build_image_structure()"]
|
||
C --> D{Images trouvées?}
|
||
D -- "Oui" --> E[Préparer réponse 200 OK - HTML ou JSON]
|
||
D -- "Non" --> F[Préparer réponse - HTML avec erreur ou JSON 404]
|
||
B -- "Échec" --> G[except Exception as e]
|
||
G --> H[Logguer l'erreur e]
|
||
H --> I[Préparer réponse - HTML avec erreur ou JSON 500]
|
||
E --> Z[Fin]
|
||
F --> Z[Fin]
|
||
I --> Z[Fin]
|
||
```
|
||
|
||
#### Étape 2 : valider avec les tests
|
||
|
||
Avant même de regarder le résultat dans votre navigateur, vérifiez que votre code est correct en lançant la suite de tests.
|
||
|
||
```bash
|
||
poetry run pytest -p no:warnings
|
||
```
|
||
|
||
Analysez le résultat. Certains tests qui échouaient au début devraient maintenant passer. L'objectif est de faire passer tous les tests relatifs à la galerie.
|
||
|
||
#### Étape 3 : lancer le serveur et constater le résultat
|
||
|
||
Une fois les tests au vert, lancez le serveur de développement Flask.
|
||
|
||
1. Assurez-vous d'être dans le bon dossier :
|
||
|
||
```bash
|
||
cd src
|
||
```
|
||
|
||
2. Lancez le serveur en mode "debug" (ce mode recharge automatiquement le serveur à chaque modification de votre code) :
|
||
|
||
```bash
|
||
flask run --debug
|
||
```
|
||
|
||
3. Ouvrez votre navigateur à l'adresse [http://127.0.0.1:5000](http://127.0.0.1:5000) et admirez votre galerie !
|
||
|
||
#### Étape 4 : créer un endpoint d'API JSON
|
||
|
||
Votre second objectif est de fournir les mêmes données via une API, au format JSON.
|
||
|
||
**Tâche :** Créez une fonction `api_images()` qui répond à la route `/api/images`.
|
||
|
||
**Logique à implémenter :**
|
||
|
||
1. La logique de récupération des données est la même que pour la vue `index()` : chemin du dossier, appel à `build_image_structure()`, gestion des exceptions.
|
||
|
||
2. La différence réside dans la réponse :
|
||
|
||
* En cas de succès, retournez directement le dictionnaire `structured_images`. Flask le convertira automatiquement en une réponse JSON avec un code de statut `200 OK`.
|
||
|
||
* Si aucune image n'est trouvée, retournez un dictionnaire d'erreur (ex: `{'error': "Aucune image n'a été trouvée"}`) et le code de statut `404 Not Found`.
|
||
|
||
* En cas d'exception serveur, retournez un dictionnaire d'erreur (ex: `{'error': "Erreur interne du serveur"}`) et le code de statut `500 Internal Server Error`.
|
||
|
||
**Diagramme de flux (Étape 4) :**
|
||
|
||
```mermaid
|
||
graph TD
|
||
subgraph Client[Client API ex: Postman, script]
|
||
A[Utilisateur/Service]
|
||
end
|
||
subgraph Serveur[Serveur Flask]
|
||
B[Route /api/images]
|
||
C["views.py: api_images()"]
|
||
D["utils.py: build_image_structure()"]
|
||
E[Réponse JSON]
|
||
end
|
||
subgraph SystemeFichiers[Disque Serveur]
|
||
G["static/images"]
|
||
end
|
||
|
||
A -- "1. Requête GET /api/images" --> B
|
||
B -- "2. Déclenche" --> C
|
||
C -- "3. Construit chemin et appelle" --> D
|
||
D -- "4. Lit le dossier" --> G
|
||
G -- "5. Retourne la structure" --> D
|
||
D -- "6. Retourne dict_images" --> C
|
||
C -- "7. Formate en JSON" --> E
|
||
E -- "8. Envoie" --> A
|
||
```
|
||
|
||
#### Étape 5 : ajouter un ou plusieurs tests pour l'API
|
||
|
||
Pour aller plus loin, ouvrez le fichier `tests/test_app.py` et ajoutez un ou plusieurs nouveaux tests pour votre API.
|
||
|
||
**Idée de test :**
|
||
Créez une fonction `test_api_images_success()` qui :
|
||
|
||
1. Fait un `GET` sur `/api/images`.
|
||
|
||
2. Vérifie que le code de statut est bien `200`.
|
||
|
||
3. Vérifie que le `content_type` de la réponse est bien `application/json`.
|
||
|
||
4. Vérifie que la réponse JSON contient des clés attendues (par exemple, le nom d'une des images).
|
||
|
||
Relancez les tests pour vous assurer que l'ancien et le nouveau code fonctionnent parfaitement.
|
||
|
||
### Bonnes pratiques
|
||
|
||
* La page d'accueil (`/`) s'affiche correctement avec les images. Les images téléchargées par le navigateur sont cohérentes avec la taille réelles des images à afficher.
|
||
|
||
* La page gère correctement l'absence du dossier d'images sans planter, en affichant un message d'erreur.
|
||
|
||
* L'endpoint `/api/images` retourne une structure JSON valide en cas de succès.
|
||
|
||
* L'endpoint `/api/images` retourne les codes d'erreur HTTP et les messages appropriés (404, 500).
|
||
|
||
* L'ensemble des tests fournis (et les vôtre) passent avec succès.
|
||
|
||
* Le code dans `views.py` est clair, commenté et suit les bonnes pratiques Python.
|