Réutiliser le code de la séance 1
Le repository écrit pour Flask lors de la séance 1 est directement importable dans Streamlit, sans modification.
Pourquoi le repository est directement importable
La classe VentesRepository (méthodes get_ventes(), get_stats(), get_par_region()) est du Python pur — aucune dépendance à Flask, aucun import de flask, aucune référence au contexte de requête HTTP.
Architecture en couches
Le repository s'importe directement dans Streamlit sans modification. La couche d'accès aux données est indépendante du framework qui l'expose.
Structure de projet
projet/
├── app.py # point d'entrée Streamlit
├── pages/ # pages secondaires (séance 4)
│ ├── 1_Ventes.py
│ └── 2_Statistiques.py
├── api/ # code Flask de la séance 1 (inchangé)
│ ├── app.py
│ ├── blueprints/
│ └── repository.py
└── db/
└── ventes.db
L'import dans le fichier Streamlit app.py :
import sys
sys.path.append(".")
from api.repository import VentesRepository
sys.path.append
sys.path.append(".") ajoute le répertoire courant au chemin de recherche des modules Python. C'est nécessaire pour que l'import from api.repository import ... fonctionne correctement lorsque vous lancez streamlit run app.py depuis la racine du projet.
Le problème des appels lents
Rappel : chaque interaction utilisateur réexécute tout le script. Chaque clic sur un filtre ou un slider relance toutes les requêtes SQL.
Sur quelques dizaines de lignes, pas de problème. Sur des dizaines de milliers de lignes, le délai devient perceptible.
D'où le cache Streamlit.
Les deux décorateurs de cache
Streamlit fournit deux décorateurs de cache aux comportements distincts.
@st.cache_data — pour les données
Met en cache le résultat d'une fonction qui retourne des données (DataFrame, liste, dict). Le résultat est indexé par les arguments de la fonction : si les arguments changent, la fonction est réexécutée.
@st.cache_data(ttl=300) # cache valide 300 secondes
def get_ventes_cached(region, annee):
return repo.get_ventes(region=region, annee=annee)
Le paramètre ttl (time to live) définit la durée de validité du cache en secondes. Après expiration, la fonction est réexécutée au prochain appel.
Thread safety
@st.cache_data copie le résultat pour chaque appelant. Cela garantit que deux utilisateurs simultanés ne modifient pas les mêmes données. C'est le décorateur à utiliser par défaut pour les données.
@st.cache_resource — pour les ressources partagées
Pour les objets instanciés une seule fois et partagés entre tous les utilisateurs : connexions à une base de données, modèles ML.
@st.cache_resource
def get_repository():
return VentesRepository("db/ventes.db")
repo = get_repository()
Pas de copie
@st.cache_resource partage la même instance entre tous les utilisateurs. C'est parfait pour une connexion DB (pas besoin d'en créer une par utilisateur), mais dangereux pour des données modifiables.
Comparaison
| Aspect | @st.cache_data | @st.cache_resource |
|---|---|---|
| Usage | DataFrames, listes, dicts | Connexions DB, modèles ML |
| Comportement | Copie le résultat (thread-safe) | Partage la même instance |
| Invalidation | Par arguments + TTL | Jamais (ou redémarrage app) |
| Exemple | Résultat de get_ventes(region, annee) | Instance de VentesRepository |
Quel décorateur de cache utiliser pour mettre en cache une instance de VentesRepository ?
Convertir les résultats en DataFrame pandas
Les méthodes du repository retournent des listes de dictionnaires (format JSON). Streamlit et les bibliothèques de visualisation (Plotly, Matplotlib) travaillent avec des DataFrames pandas.
La conversion :
import pandas as pd
data = repo.get_par_region(annee=2024) # [{"region": "IDF", ...}, ...]
df = pd.DataFrame(data) # conversion en DataFrame
Opérations courantes avant affichage
# Renommer les colonnes pour la lisibilité
df = df.rename(columns={
"chiffre_affaires": "CA (€)",
"nombre_ventes": "Nb ventes",
"panier_moyen": "Panier moyen (€)"
})
# Arrondir les décimales
df = df.round(2)
# Afficher
st.dataframe(df, use_container_width=True)
Noms de colonnes
Les noms de colonnes du DataFrame deviennent les en-têtes du tableau affiché. Des noms techniques comme chiffre_affaires ou nb_ventes sont peu lisibles — renommez-les avant affichage.
Pourquoi le paramètre ttl de @st.cache_data est-il important ?
À retenir
Points clés
- Le repository de la séance 1 est importable tel quel dans Streamlit
@st.cache_datamet en cache les données (DataFrames, listes) avec copie pour chaque utilisateur@st.cache_resourcemet en cache les ressources (connexions DB) partagées entre tous les utilisateurs- Le paramètre
ttlcontrôle la durée de validité du cache - Convertir les listes de dicts en DataFrames pandas avec
pd.DataFrame()avant affichage