SQLAlchemy ORM - Lavorare con i join

Ora che abbiamo due tabelle, vedremo come creare query su entrambe le tabelle contemporaneamente. Per costruire un semplice join implicito tra Customer e Invoice, possiamo usare Query.filter () per equiparare tra loro le colonne correlate. Di seguito, carichiamo le entità Cliente e Fattura contemporaneamente utilizzando questo metodo:

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()

for c, i in session.query(Customer, Invoice).filter(Customer.id == Invoice.custid).all():
   print ("ID: {} Name: {} Invoice No: {} Amount: {}".format(c.id,c.name, i.invno, i.amount))

L'espressione SQL emessa da SQLAlchemy è la seguente:

SELECT customers.id 
AS customers_id, customers.name 
AS customers_name, customers.address 
AS customers_address, customers.email 
AS customers_email, invoices.id 
AS invoices_id, invoices.custid 
AS invoices_custid, invoices.invno 
AS invoices_invno, invoices.amount 
AS invoices_amount
FROM customers, invoices
WHERE customers.id = invoices.custid

E il risultato delle righe di codice precedenti è il seguente:

ID: 2 Name: Gopal Krishna Invoice No: 10 Amount: 15000
ID: 2 Name: Gopal Krishna Invoice No: 14 Amount: 3850
ID: 3 Name: Govind Pant Invoice No: 3 Amount: 10000
ID: 3 Name: Govind Pant Invoice No: 4 Amount: 5000
ID: 4 Name: Govind Kala Invoice No: 7 Amount: 12000
ID: 4 Name: Govind Kala Invoice No: 8 Amount: 8500
ID: 5 Name: Abdul Rahman Invoice No: 9 Amount: 15000
ID: 5 Name: Abdul Rahman Invoice No: 11 Amount: 6000

L'attuale sintassi SQL JOIN può essere facilmente ottenuta utilizzando il metodo Query.join () come segue:

session.query(Customer).join(Invoice).filter(Invoice.amount == 8500).all()

L'espressione SQL per il join verrà visualizzata sulla console:

SELECT customers.id 
AS customers_id, customers.name 
AS customers_name, customers.address 
AS customers_address, customers.email 
AS customers_email
FROM customers JOIN invoices ON customers.id = invoices.custid
WHERE invoices.amount = ?

Possiamo iterare attraverso il risultato usando il ciclo for -

result = session.query(Customer).join(Invoice).filter(Invoice.amount == 8500)
for row in result:
   for inv in row.invoices:
      print (row.id, row.name, inv.invno, inv.amount)

Con 8500 come parametro bind, viene visualizzato il seguente output:

4 Govind Kala 8 8500

Query.join () sa come unire queste tabelle perché tra di loro c'è solo una chiave esterna. Se non c'erano chiavi esterne, o più chiavi esterne, Query.join () funziona meglio quando viene utilizzata una delle seguenti forme:

query.join (Fattura, id == Address.custid) condizione esplicita
query.join (Customer.invoices) specificare la relazione da sinistra a destra
query.join (Invoice, Customer.invoices) stesso, con target esplicito
query.join ("fatture") stesso, usando una stringa

Allo stesso modo la funzione outerjoin () è disponibile per ottenere il join esterno sinistro.

query.outerjoin(Customer.invoices)

Il metodo subquery () produce un'espressione SQL che rappresenta l'istruzione SELECT incorporata in un alias.

from sqlalchemy.sql import func

stmt = session.query(
   Invoice.custid, func.count('*').label('invoice_count')
).group_by(Invoice.custid).subquery()

L'oggetto stmt conterrà un'istruzione SQL come di seguito:

SELECT invoices.custid, count(:count_1) AS invoice_count FROM invoices GROUP BY invoices.custid

Una volta che abbiamo la nostra dichiarazione, si comporta come un costrutto di tabella. Le colonne dell'istruzione sono accessibili tramite un attributo chiamato c come mostrato nel codice seguente -

for u, count in session.query(Customer, stmt.c.invoice_count).outerjoin(stmt, Customer.id == stmt.c.custid).order_by(Customer.id):
   print(u.name, count)

Il ciclo for precedente mostra il conteggio delle fatture in base al nome come segue:

Arjun Pandit None
Gopal Krishna 2
Govind Pant 2
Govind Kala 2
Abdul Rahman 2