Sostieni AppuntiFacili con una piccola donazione su PayPal
Dona con PayPalQuando si lavora con un database, anche piccolo come SQLite, è importante organizzare in modo chiaro:
Questa guida introduce un metodo semplice e professionale per configurare un database SQLite in un progetto Python.
A differenza di MySQL, Postgres o Oracle, SQLite è:
Il modulo sqlite3 è già presente in tutte le installazioni moderne di Python.
Verificare:
python -c "import sqlite3; print(sqlite3.sqlite_version)"
Se non da errori, è tutto pronto.
I migrations (migrazioni) sono file che descrivono come deve essere strutturato il database, ad esempio:
Sono “istruzioni” che descrivono l’evoluzione del database nel tempo.
SQLite è molto semplice, ma i problemi nascono comunque:
Le migrazioni risolvono esattamente questi problemi.
“Run once” significa: esegui lo script solo la prima volta.
Serve per evitare:
È un concetto essenziale quando:
SQLite non offre questa funzionalità, quindi la implementiamo noi (semplice e pulito).
Per una configurazione pulita, si consiglia una struttura come:
progetto/
│-- config/
│ └── settings.py # configurazione database
│-- migrations/
│ └── init.sql # script SQL di creazione tabelle
│-- database/
│ ├── connection.py
│ ├── user_repository.py
│-- models/
│ └── user.py
│-- main.py
│-- miodb.db # generato automaticamente
Separare questi file serve per evitare “spaghetti code” e per mantenere il database indipendente dalla logica Python.
config/settings.py:
DATABASE_PATH = "miodb.db"
Evita percorsi hard-coded e permette di cambiare posizione o nome del database rapidamente.
Sebbene SQLite non abbia un sistema di migrazioni integrato come Django o Alembic, è possibile creare un semplice file SQL con tutte le tabelle.
migrations/init.sql:
CREATE TABLE IF NOT EXISTS utenti (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nome TEXT NOT NULL,
email TEXT UNIQUE NOT NULL
);
CREATE TABLE IF NOT EXISTS prodotti (
id INTEGER PRIMARY KEY AUTOINCREMENT,
nome TEXT NOT NULL,
prezzo REAL NOT NULL
);
Questo file rappresenta la prima versione della struttura del database.
Grazie a IF NOT EXISTS può essere eseguito più volte senza danni.
database/setup.py:
import sqlite3
from config.settings import DATABASE_PATH
def run_migrations():
with sqlite3.connect(DATABASE_PATH) as conn:
cursor = conn.cursor()
# Legge lo script SQL
with open("migrations/init.sql", "r", encoding="utf-8") as f:
sql_script = f.read()
cursor.executescript(sql_script)
print("Migrazioni iniziali eseguite!")
La funzione executescript() permette di eseguire più comandi SQL in un colpo solo.
main.py:
from database.setup import run_migrations
# Eseguo le migrazioni
run_migrations()
print("Applicazione avviata!")
Questo garantisce che il database sia sempre pronto all’uso.
Se un giorno si vuole aggiungere script di migrazione multipli:
migrations/
│── 001_init.sql
│── 002_add_column.sql
│── 003_create_products.sql
Possibile creare un file che tiene traccia delle migrazioni applicate:
Aggiungere all’inizio di init.sql:
CREATE TABLE IF NOT EXISTS _migrations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE
);
Questa tabella registra quali script sono già stati applicati.
import os
import sqlite3
from config.settings import DATABASE_PATH
MIGRATIONS_FOLDER = "migrations"
def run_all_migrations():
with sqlite3.connect(DATABASE_PATH) as conn:
cursor = conn.cursor()
# Creo tabella per tracking
cursor.execute("""
CREATE TABLE IF NOT EXISTS _migrations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE
)
""")
# Leggo le migrazioni già applicate
cursor.execute("SELECT name FROM _migrations")
applied = {row[0] for row in cursor.fetchall()}
# Scorro i file
for fname in sorted(os.listdir(MIGRATIONS_FOLDER)):
if not fname.endswith(".sql"):
continue
if fname in applied:
continue # già eseguita
print(f"Eseguo migrazione: {fname}")
with open(os.path.join(MIGRATIONS_FOLDER, fname), "r", encoding="utf-8") as f:
sql = f.read()
cursor.executescript(sql)
cursor.execute("INSERT INTO _migrations (name) VALUES (?)", (fname,))
print("Tutte le migrazioni applicate!")
Questo simula un sistema di migrazioni simile a framework più avanzati (Django, Flask-Migrate, etc.).
Questo approccio permette di non ricreare file .db accidentalmente:
import os
from database.setup import run_migrations
if not os.path.exists("miodb.db"):
print("Database non trovato. Lo creo…")
run_migrations()
else:
print("Database già presente. Nessuna migrazione necessaria.")
Questo è un approccio comune in applicazioni piccole e medie.
| Concetto | Significato | Utilità |
|---|---|---|
| Migrazione | File .sql che modifica la struttura del DB | Mantiene coerenza e versioning |
| Run once | Eseguire gli script solo la prima volta | Evita duplicazioni/danni |
| Tracking migrazioni | Registra quali script sono stati applicati | Necessario per progetti evolutivi |
| Script SQL separati | Struttura definita fuori dal codice | Più chiaro e manutenibile |
| Struttura a cartelle | Divisione logica del progetto | Pulizia e scalabilità |