Lezione 9: Dati locali — JSON e persistenza
Obiettivi
Section titled “Obiettivi”- Capire cos’è JSON e come si usa in Python
- Salvare dati su file con il modulo
json - Caricare dati all’avvio dell’app
- Creare una Todo List con persistenza
1. Cos’è JSON?
Section titled “1. Cos’è JSON?”JSON (JavaScript Object Notation) è un formato per rappresentare dati strutturati. È il formato più usato per salvare e scambiare dati.
{ "nome": "Mario", "eta": 17, "studente": true, "voti": [8, 7, 9], "indirizzo": { "via": "Roma", "civico": 10 }}In Python, JSON corrisponde a:
{ "nome": "Mario", "eta": 17, "studente": True, "voti": [8, 7, 9], "indirizzo": { "via": "Roma", "civico": 10 }}2. Il modulo json in Python
Section titled “2. Il modulo json in Python”Convertire Python → JSON (serializzare)
Section titled “Convertire Python → JSON (serializzare)”import json
dati = { "nome": "Mario", "eta": 17, "materie": ["Python", "Italiano", "Matematica"]}
# Python → stringa JSONjson_string = json.dumps(dati, indent=2)print(json_string)Output:
{ "nome": "Mario", "eta": 17, "materie": ["Python", "Italiano", "Matematica"]}Convertire JSON → Python (deserializzare)
Section titled “Convertire JSON → Python (deserializzare)”import json
json_string = '{"nome": "Mario", "eta": 17}'dati = json.loads(json_string)print(dati["nome"]) # Mario3. Salvare su file con Flet
Section titled “3. Salvare su file con Flet”In Flet, possiamo salvare i dati in un file JSON usando il modulo json standard di Python.
Salvare dati
Section titled “Salvare dati”import jsonimport os
def salva_dati(filename, dati): """Salva una lista/dict in un file JSON.""" with open(filename, "w", encoding="utf-8") as f: json.dump(dati, f, indent=2, ensure_ascii=False)Caricare dati
Section titled “Caricare dati”def carica_dati(filename): """Carica dati da un file JSON. Se non esiste, ritorna lista vuota.""" if os.path.exists(filename): with open(filename, "r", encoding="utf-8") as f: return json.load(f) return []4. Esempio guidato: Todo List persistente
Section titled “4. Esempio guidato: Todo List persistente”Mettiamo tutto insieme: una Todo List che ricorda i task anche dopo aver chiuso l’app.
import flet as ftimport jsonimport os
FILE_DATI = "todo_data.json"
def main(page: ft.Page): page.title = "Todo List Persistente" page.padding = 30 page.scroll = ft.ScrollMode.AUTO
# Carica dati all'avvio def carica_todo(): if os.path.exists(FILE_DATI): with open(FILE_DATI, "r", encoding="utf-8") as f: return json.load(f) return []
# Salva dati su file def salva_todo(): with open(FILE_DATI, "w", encoding="utf-8") as f: json.dump(tasks, f, indent=2, ensure_ascii=False)
# Stato tasks = carica_todo() # Lista di dizionari: {"testo": "...", "fatto": false}
# Widget lista lista_tasks = ft.ListView(spacing=10, padding=10, expand=True)
def costruisci_lista(): """Ricostruisce la UI della lista partendo da tasks.""" lista_tasks.controls.clear() for i, task in enumerate(tasks): # Checkbox per completato checkbox = ft.Checkbox( value=task["fatto"], on_change=lambda e, idx=i: toggle_task(idx), )
# Testo del task testo = ft.Text( task["testo"], size=16, style=ft.TextStyle( decoration=ft.TextDecoration.LINE_THROUGH if task["fatto"] else None, ), color="grey" if task["fatto"] else "black", )
# Bottone elimina elimina = ft.IconButton( ft.icons.DELETE, icon_color="red", on_click=lambda e, idx=i: elimina_task(idx), )
# Card card = ft.Container( content=ft.Row([checkbox, testo, elimina], alignment=ft.MainAxisAlignment.SPACE_BETWEEN), padding=10, bgcolor="white", border_radius=8, shadow=ft.BoxShadow(blur_radius=3, color=ft.colors.GREY_300), ) lista_tasks.controls.append(card)
def aggiungi_task(e): if input_task.value and input_task.value.strip(): tasks.append({"testo": input_task.value.strip(), "fatto": False}) salva_todo() costruisci_lista() input_task.value = "" page.update()
def toggle_task(idx): tasks[idx]["fatto"] = not tasks[idx]["fatto"] salva_todo() costruisci_lista() page.update()
def elimina_task(idx): tasks.pop(idx) salva_todo() costruisci_lista() page.update()
def svuota_lista(e): tasks.clear() salva_todo() costruisci_lista() page.update()
# Input input_task = ft.TextField( hint_text="Nuovo task...", expand=True, on_submit=aggiungi_task, )
# Costruisce interfaccia page.add( ft.Text("📋 Todo List", size=28, weight="bold"), ft.Text(f"Task: {len(tasks)}", color="grey"), ft.Divider(), ft.Row([input_task, ft.ElevatedButton("➕", on_click=aggiungi_task)]), ft.Row([ ft.OutlinedButton("🗑️ Svuota tutto", on_click=svuota_lista, icon=ft.icons.DELETE), ]), ft.Divider(), lista_tasks, )
# Mostra dati caricati costruisci_lista() page.update()
ft.app(target=main)5. Tecniche di salvataggio
Section titled “5. Tecniche di salvataggio”Salvataggio automatico a ogni modifica
Section titled “Salvataggio automatico a ogni modifica”def aggiungi_task(e): # ... modifica tasks ... salva_dati() # Salva OGNI volta che si modificaSalvataggio con pulsante “Salva”
Section titled “Salvataggio con pulsante “Salva””bottone_salva = ft.ElevatedButton("Salva", on_click=lambda e: salva_dati())Messaggio di conferma
Section titled “Messaggio di conferma”bottone_salva = ft.ElevatedButton("Salva", on_click=lambda e: salva_con_feedback(e))
def salva_con_feedback(e): salva_dati() page.snack_bar = ft.SnackBar(ft.Text("✅ Dati salvati!"), open=True) page.update()6. Gestione errori di file
Section titled “6. Gestione errori di file”A volte il file potrebbe essere danneggiato o non accessibile:
def carica_dati_sicura(filename): """Carica dati con gestione errori.""" try: if os.path.exists(filename): with open(filename, "r", encoding="utf-8") as f: return json.load(f) except (json.JSONDecodeError, IOError): # File danneggiato → ricomincia da capo print("File danneggiato, ricreo...") return []7. Tabella riassuntiva
Section titled “7. Tabella riassuntiva”| Comando | Cosa fa |
|---|---|
json.dumps(dati) | Converte Python → stringa JSON |
json.loads(stringa) | Converte stringa JSON → Python |
json.dump(dati, file) | Salva dati su file |
json.load(file) | Carica dati da file |
os.path.exists(file) | Controlla se un file esiste |
open(file, "w") | Apre file in scrittura |
open(file, "r") | Apre file in lettura |
8. Esercizio autonomo
Section titled “8. Esercizio autonomo”🎯 Esercizio: “Diario personale”
Section titled “🎯 Esercizio: “Diario personale””Crea un file diario.py che realizzi un diario personale con persistenza:
- All’avvio, carica i post salvati dal file
diario.json - Mostra la lista dei post (ogni post ha: titolo, data, testo)
- Un form per aggiungere un nuovo post con:
- Titolo (TextField)
- Testo (TextField multiline)
- Data (messa automaticamente:
import datetime; oggi = str(datetime.date.today()))
- Bottone “Aggiungi” che salva il post e aggiorna la lista
- Ogni post nella lista mostra: titolo, data, prime parole del testo
- Bottone “Elimina” su ogni post per rimuoverlo
- Tutto deve essere persistente: alla prossima apertura, i post devono essere ancora lì
Formato JSON suggerito
Section titled “Formato JSON suggerito”[ { "titolo": "Primo giorno di scuola", "data": "2026-09-15", "testo": "Oggi è iniziato il nuovo anno scolastico..." }, ...]Suggerimenti
Section titled “Suggerimenti”- Usa
datetime.date.today().isoformat()per la data automatica - Per mostrare solo l’inizio del testo:
post["testo"][:50] + "..." - Usa
ft.Cardper ogni post - Per il testo lungo, usa
multiline=Truenel TextField