SQLAlchemy ORM – Construyendo relaciones

Esta sesión describe cómo crear otra tabla, vinculada a una existente en nuestra base de datos. La tabla de clientes contiene datos básicos de clientes. Ahora necesitamos crear una tabla de facturas que pueda tener cualquier número de facturas propiedad del cliente. Este es el caso de una relación de uno a varios.

Usando declarativamente, definimos esta tabla junto con su clase asociada Facturas como se indica a continuación:

from sqlalchemy import create_engine, ForeignKey, Column, Integer, String
engine = create_engine('sqlite:///sales.db', echo = True)
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
from sqlalchemy.orm import relationship

class Customer(Base):
   __tablename__ = 'customers'

   id = Column(Integer, primary_key = True)
   name = Column(String)
   address = Column(String)
   email = Column(String)

class Invoice(Base):
   __tablename__ = 'invoices'
   
   id = Column(Integer, primary_key = True)
   custid = Column(Integer, ForeignKey('customers.id'))
   invno = Column(Integer)
   amount = Column(Integer)
   customer = relationship("Customer", back_populates = "invoices")

Customer.invoices = relationship("Invoice", order_by = Invoice.id, back_populates = "customer")
Base.metadata.create_all(engine)

Esto enviará una consulta CREATE TABLE al motor SQLite como se muestra a continuación:

CREATE TABLE invoices (
   id INTEGER NOT NULL,
   custid INTEGER,
   invno INTEGER,
   amount INTEGER,
   PRIMARY KEY (id),
   FOREIGN KEY(custid) REFERENCES customers (id)
)

Puede verificar la creación de una nueva tabla en sales.db usando la herramienta SQLiteStudio.

Sales.db Nueva tabla

La clase Invoices aplica la construcción ForeignKey al atributo custid. Esta directiva especifica que los valores en esta columna deben limitarse a los valores presentes en la columna id en la tabla de clientes. Esta es una característica clave de las bases de datos relacionales y el pegamento que transforma un conjunto de tablas no relacionadas para tener relaciones superpuestas ricas.

La segunda directiva, conocida como Relación (), le dice al ORM que la clase Invoice debe asociarse con la clase Customer mediante el atributo Invoice.customer. Relationship () utiliza una relación de clave externa entre dos tablas para determinar la naturaleza de esa relación, determinando que es de varios a uno.

Se coloca una directiva adicional Relations () en la clase Customer asociada bajo el atributo Customer.invoices. El parámetro Relationship.back_populate se asigna para hacer referencia a nombres de atributos adicionales para que cada relación () pueda decidir razonablemente sobre la misma relación expresada en orden inverso. Por un lado, Invoices.customer se refiere a la instancia de Invoices y, por otro lado, Customer.invoices se refiere a la lista de instancias de Clientes.

La función de relación es parte de la API de relación del paquete SQLAlchemy ORM. Proporciona comunicación entre las dos clases mapeadas. Esto corresponde a la relación padre-hijo o asociatividad de la tabla.

A continuación se muestran los principales patrones de relación encontrados.

Uno a muchos

Una relación de uno a varios se refiere al padre que usa la clave externa de la tabla secundaria. Entonces se hace referencia a Relationship () en el elemento padre como una referencia a la colección de elementos representados por el hijo. El parámetro Relationship.back_populate se usa para establecer una relación bidireccional de uno a muchos, donde la parte posterior es de muchos a uno.

Muchos a uno

Por otro lado, una relación de varios a uno coloca una clave externa en la tabla principal para hacer referencia al secundario. Relationship () se declara en el elemento padre, donde se creará un nuevo atributo de almacenamiento escalar. Aquí nuevamente, el parámetro Relations.back_populate se usa para el comportamiento bidireccional.

Doce y cincuenta y nueve de la noche

Una relación uno a uno es esencialmente una relación bidireccional. La bandera de la lista de usuarios especifica la ubicación de un atributo escalar en lugar de una colección en el lado de muchos de la relación. Para convertir una relación de uno a varios en una relación de uno a uno, establezca el parámetro uselist en false.

Muchos a muchos

Una relación de muchos a muchos se establece agregando una tabla de asociación que pertenece a las dos clases definiendo atributos con sus claves externas. Esto se indica mediante el argumento secundario de la función Relationship (). Normalmente, una tabla utiliza un objeto de metadatos asociado con una clase base declarativa para que las directivas ForeignKey puedan encontrar tablas remotas para enlazar. El parámetro Relationship.back_populate para cada relación () establece una relación bidireccional. Ambos lados de la relación contienen una colección.

🚫