SQLAlchemy ORM: trabajo con combinaciones

Ahora que tenemos dos tablas, veremos cómo crear consultas en ambas tablas al mismo tiempo. Para construir una unión implícita simple entre Cliente y Factura, podemos usar Query.filter () para equiparar sus columnas relacionadas. A continuación, cargamos las entidades Cliente y Factura a la vez utilizando este método:

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))

La expresión SQL emitida por SQLAlchemy es la siguiente:

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

Y el resultado de las líneas de código anteriores es el siguiente:

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

La sintaxis de SQL JOIN real se logra fácilmente usando el método Query.join () de la siguiente manera:

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

La expresión SQL para unión se mostrará en la consola:

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 = ?

Podemos iterar a través del resultado usando for loop -

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 como parámetro de vinculación, se muestra la siguiente salida:

4 Govind Kala 8 8500

Query.join () sabe cómo unirse entre estas tablas porque solo hay una clave externa entre ellas. Si no hubiera claves externas, o más claves externas, Query.join () funciona mejor cuando se usa una de las siguientes formas:

query.join (Factura, id == Address.custid) condición explícita
query.join (Customer.invoices) especificar la relación de izquierda a derecha
query.join (Factura, Customer.invoices) lo mismo, con objetivo explícito
query.join ('facturas') lo mismo, usando una cuerda

De manera similar, la función outsidejoin () está disponible para lograr la unión externa izquierda.

query.outerjoin(Customer.invoices)

El método subquery () produce una expresión SQL que representa la instrucción SELECT incrustada dentro de un alias.

from sqlalchemy.sql import func

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

El objeto stmt contendrá una declaración SQL como se muestra a continuación:

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

Una vez que tenemos nuestra declaración, se comporta como una construcción de tabla. Se puede acceder a las columnas de la declaración a través de un atributo llamado c, como se muestra en el siguiente código:

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)

El bucle for anterior muestra el recuento de facturas por nombre de la siguiente manera:

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