SQLAlchemy ORM - Construindo Relacionamento

Esta sessão descreve a criação de outra tabela que está relacionada a uma já existente em nosso banco de dados. A tabela de clientes contém dados mestre de clientes. Agora precisamos criar uma tabela de faturas que pode ter qualquer número de faturas pertencentes a um cliente. Este é o caso de um para muitos relacionamentos.

Usando declarativo, definimos esta tabela junto com sua classe mapeada, Faturas conforme fornecido abaixo -

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)

Isso enviará uma consulta CREATE TABLE para o mecanismo SQLite conforme abaixo -

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

Podemos verificar se uma nova tabela foi criada em sales.db com a ajuda da ferramenta SQLiteStudio.

A classe Faturas aplica a construção ForeignKey no atributo custid. Esta diretiva indica que os valores nesta coluna devem ser restritos a valores presentes na coluna id na tabela de clientes. Esse é um recurso central dos bancos de dados relacionais e é a “cola” que transforma a coleção não conectada de tabelas em relacionamentos ricos e sobrepostos.

Uma segunda diretiva, conhecida como relacionamento (), informa ao ORM que a classe Invoice deve ser vinculada à classe Customer usando o atributo Invoice.customer. O relacionamento () usa os relacionamentos de chave estrangeira entre as duas tabelas para determinar a natureza dessa ligação, determinando que é muitos para um.

Uma diretiva relacionamento adicional () é colocada na classe mapeada do Cliente sob o atributo Customer.invoices. O parâmetro relationship.back_populates é atribuído para se referir aos nomes de atributos complementares, de modo que cada relacionamento () possa tomar uma decisão inteligente sobre o mesmo relacionamento conforme expresso no reverso. Por um lado, Invoices.customer se refere à instância Invoices e, por outro lado, Customer.invoices se refere a uma lista de instâncias Clientes.

A função de relacionamento faz parte da API de relacionamento do pacote SQLAlchemy ORM. Ele fornece um relacionamento entre duas classes mapeadas. Isso corresponde a um relacionamento pai-filho ou tabela associativa.

A seguir estão os padrões básicos de relacionamento encontrados -

Um para muitos

Um relacionamento Um para Muitos refere-se ao pai com a ajuda de uma chave estrangeira na tabela filho. relacionamento () é então especificado no pai, referenciando uma coleção de itens representados pelo filho. O parâmetro relationship.back_populates é usado para estabelecer um relacionamento bidirecional em um-para-muitos, onde o lado “reverso” é muitos para um.

Muitos para um

Por outro lado, o relacionamento Muitos para Um coloca uma chave estrangeira na tabela pai para se referir ao filho. relacionamento () é declarado no pai, onde um novo atributo de retenção escalar será criado. Aqui, novamente, o parâmetro relationship.back_populates é usado para Bidirectionalbehaviour.

Um a um

O relacionamento Um para Um é essencialmente um relacionamento bidirecional por natureza. O sinalizador uselist indica a colocação de um atributo escalar em vez de uma coleção no lado “muitos” do relacionamento. Para converter um para muitos em um tipo de relação um para um, defina o parâmetro uselist como false.

Muitos para muitos

O relacionamento muitos para muitos é estabelecido adicionando-se uma tabela de associação relacionada a duas classes, definindo atributos com suas chaves estrangeiras. É indicado pelo argumento secundário para relacionamento (). Normalmente, a Tabela usa o objeto MetaData associado à classe base declarativa, para que as diretivas ForeignKey possam localizar as tabelas remotas com as quais se vincular. O parâmetro relationship.back_populates para cada relacionamento () estabelece um relacionamento bidirecional. Ambos os lados do relacionamento contêm uma coleção.