questo post nasce come modifica dell'originale (riportato sotto come quote). avendo trovato la soluzione, la posto sul forum sperando possa essere di aiuto. se gli amministratori lo riterranno utile possono anche inserire il thread in cima alla lista con una flag sticky. questo post non vuole essere una guida esaustiva su python o sqlalchemy: solo una dritta!
messaggio originale ha scritto:salve,
ho iniziato a smanettare un pò con python e sqlite3. sto sviluppando un'applicazione che dovrebbe accede ad un database creato "altrove e da qualcun altro". il db per adesso è in sqlite3 ma potrebbe migrare in qualsiasi altro tipo di sql (es. mysql). per astrarre il codice dal db e per evitare di imparare tutto sql (che non conosco) solo per alcune piccole query, ho deciso di interfacciare il codice python al db tramite sqlalchemy (lo so studire fa bene ma ho poco tempo.
ora il problema è che di fatto non riesco a capire come fare una query e accedere ai risultati. in particolare ho seguito i comandi di questo tutorial che mi consente di eseguire una query (qui senza filtri) e prenderne i risultati. ma una volta memorizzati in un oggetto python poi non so come analizzare il contenuto di tale oggetto!
l'esempio è qui riportato. nel caso seguente accedo ad una tabella chiamata 'room' che viene perfettamente caricata (ho fatto alcuni controlli sulla sua esistenza in uno stralcio di codice che non riporto):
{snip...}
a questo punto vorrei leggere result, ma anche controllando il tipo e cercando nell'api di sqlalchemy non ci ho capito nulla... qualcuno è pratico della cosa?! esiste un modo divero di approcciare il problema?!
{snip...}
quello che descriverò ora è un riassunto di ciò che ho capito leggendo vari docs su sqlalchemy. di fatto è tutto spiegato sul sito del toolkit ma mi ci è voluto un pochino per capire!
intro
sqlalchemy (d'ora in avanti s.a.) è un orm python molto potente e quindi complesso (a mio avviso). lo scopo di base di un orm è caricare una tabella di un database (di solito sql) e interfacciarla con un normale oggetto tipico della programmazione o-o. s.a. fa molto di più di questo (interfacciare più database tra loro astraendone le specifiche del dialetto sql ad esempio). di seguito io mi limiterò a spiegare come agganciare s.a. ad un file sqlite3 e visualizzare il contenuto di una tabella. nel caso presente, la tabella si chiama 'room' ed è costituita da quattro campi (oltre alla primary key): type, size, budget, speaker_model. la tabella e i campi derivano da un caso reale, la cui spiegazione esula da questo post: prendeteli per quello che sono.
componenti essenziali di s.a.
s.a. possiede 4 componenti fondamentali: engine, metadata, mapper e session.
engine
engine è il "motore" di s.a., il suo scopo è "agganciare" uno specifico database. nel caso presente ci agganceremo ad un database sqlite3 contenuto in un unico file chiamato karray08000. il codice python per fare questo è:
- Codice: Seleziona tutto
import sqlalchemy as sqla
#sql server connection to an sqlite3 file named karray_08000
my_engine = sqla.create_engine('sqlite:///karray_08000', echo=True)
questo codice crea un engine chiamato my_engine che si collega al database suddetto. il parametro echo=True serve per veder visulizzato sulla console l'output sql generato da s.a.
metadata
metadata serve per caricare tutte le informazioi relative alle tabelle presenti in un dato db. dal momento che il db è collegato a s.a. tramite un engine, metadata non fa altro che fare un binding su tale engine per caricare informazioni su una o più tabelle. nel presente caso carichiamo le informazioni relative ad una tabella chiamata room.
- Codice: Seleziona tutto
import sqlalchemy as sqla
#sql server connection to an sqlite3 file named karray_08000
my_engine = sqla.create_engine('sqlite:///karray_08000', echo=True)
#expose table struct and load data from db
my_meta = sqla.MetaData(bind=my_engine) #create and bind metadata
my_room = sqla.Table('room', my_meta, autoload=True) #load table contents
my_meta.create_all() # issue CREATE statements for all tables
il codice utilizza my_engine per accedere alla tabella 'room'. i nostri metadati sono gestiti dall'istanza my_meta. tale oggetto è collegato (bind) al motore di s.a., autocarica la tabella chiamata 'room' e, tramite il metodo create_all esegue il caricamento effettivo dei metadati.
mapper
per poter analizzare il contenuto di una tabella occorre collegarla ad un oggetto: una classe python. questo oggetto non è altro che uno "specchio" della tabella stessa: i suoi membri saranno uguali a tutti i campi della tabella 'room', ad eccezione della primary key. l'oggetto è visto proprio come un "riflesso" in uno specchio, tanto che, tecnicamente, il collegamento oggetto-tabella è chiamato "riflessione della tabella". per riflettere 'room' occorre creare un erede di un oggetto di tipo object - questo definito in s.a.- e "mapparlo" sulla tabella 'room':
- Codice: Seleziona tutto
import sqlalchemy as sqla
from sqlalchemy.orm import mapper #!!!! ho aggiunto l'include per il mapper
class a_room(object): #!!!! creo un oggetto che eredita object
def __init__(self, type=0, budget=0, size=0, model=0):
#definisco un membro per ogni campo di 'room'
self.type = type
self.size = size
self.budget = budget
self.speaker_model = model
#sql server connection to an sqlite3 file named karray_08000
my_engine = sqla.create_engine('sqlite:///karray_08000', echo=True)
#expose table struct and load data from db
my_meta = sqla.MetaData(bind=my_engine) #create and bind metadata
my_room = sqla.Table('room', my_meta, autoload=True) #load table contents
my_meta.create_all() # issue CREATE statements for all tables
#!!!! mappo la tabella sull'oggetto
mapper(a_room, self.room)
la cosa importante da notare è che si mappa direttamente l'oggetto e non una sia istanza!
session
A questo punto il gioco è fatto! qualunque sia l'origine della tabella noi ce la possiamo dimenticare: lavoreremo solo con il suo riflesso! per poter eseguire qualunque attività sql è necessario istanziare un oggetto di tipo session -contenuto in s.a.-. tale oggetto consentirà di creare, elinimare e modificare tabelle o di eseguire delle query. ad esempio il codice seguente stampa tutti i record di 'room':
- Codice: Seleziona tutto
import sqlalchemy as sqla
from sqlalchemy.orm import mapper
from sqlalchemy.orm import sessionmaker #!!!! ho aggiunto l'include per la session
class a_room(object):
def __init__(self, type=0, budget=0, size=0, model=0):
#definisco un membro per ogni campo di 'room'
self.type = type
self.size = size
self.budget = budget
self.speaker_model = model
#sql server connection to an sqlite3 file named karray_08000
my_engine = sqla.create_engine('sqlite:///karray_08000', echo=True)
#expose table struct and load data from db
my_meta = sqla.MetaData(bind=my_engine) #create and bind metadata
my_room = sqla.Table('room', my_meta, autoload=True) #load table contents
my_meta.create_all() # issue CREATE statements for all tables
#mappo la tabella sull'oggetto
mapper(a_room, my_room)
#!!!! creo un istanza di uno oggetto sessione...
Session_obj = sessionmaker(bind=my_engine) #create, bind and instantiate a commit session
my_session = Session_obj()
#!!! ...e lo uso per elencare i contenuti della tabella 'room'
for t, b, s, m in my_session.query(a_room.type, a_room.budget, a_room.size, a_room.speaker_model):
print(t + ',' + b + ',' + s + ',' + m)
anche la sessione my_session deve essere collegata al motore (bind=my_engine). una volta fatto il binding, una query sarà semplicemente la chiamata al metodo 'query' dell'istanza my_session. Due note importanti che all'inizio mi erano sfuggite e mi hanno generato un pò di confusione:
1- occorre creare prima un oggetto con la funzione 'sessionmaker' e poi istanziarlo: la sessione è un'istanza di un oggetto!
2- le operazioni di una sessione sono eseguite su un oggetto python e non su una sua instanza (così come per il mapper)!
conclusioni
non ci sono conclusioni: occorre solo leggere bene le istruzioni degli sviluppatori :-P
ciao,
M
.