Avancé

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.

Chargement du diagramme…

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 :

python
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.

python
@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.

python
@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
UsageDataFrames, listes, dictsConnexions DB, modèles ML
ComportementCopie le résultat (thread-safe)Partage la même instance
InvalidationPar arguments + TTLJamais (ou redémarrage app)
ExempleRé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 :

python
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

python
# 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_data met en cache les données (DataFrames, listes) avec copie pour chaque utilisateur
  • @st.cache_resource met en cache les ressources (connexions DB) partagées entre tous les utilisateurs
  • Le paramètre ttl contrôle la durée de validité du cache
  • Convertir les listes de dicts en DataFrames pandas avec pd.DataFrame() avant affichage