SQLAlchemy ORM - Creazione di relazioni

Questa sessione descrive la creazione di un'altra tabella correlata a una già esistente nel nostro database. La tabella dei clienti contiene i dati anagrafici dei clienti. Ora è necessario creare una tabella delle fatture che può contenere un numero qualsiasi di fatture appartenenti a un cliente. Questo è un caso di relazioni uno a molti.

Usando dichiarativo, definiamo questa tabella insieme alla sua classe mappata, Fatture come indicato di seguito -

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)

Questo invierà una query CREATE TABLE al motore SQLite come di seguito -

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

Possiamo controllare che la nuova tabella venga creata in sales.db con l'aiuto dello strumento SQLiteStudio.

La classe Invoices applica il costrutto ForeignKey sull'attributo custid. Questa direttiva indica che i valori in questa colonna devono essere vincolati a essere valori presenti nella colonna id nella tabella dei clienti. Questa è una caratteristica fondamentale dei database relazionali ed è il "collante" che trasforma una raccolta di tabelle non connessa per avere ricche relazioni sovrapposte.

Una seconda direttiva, nota come relationship (), dice all'ORM che la classe Invoice deve essere collegata alla classe Customer utilizzando l'attributo Invoice.customer. La relazione () utilizza le relazioni di chiave esterna tra le due tabelle per determinare la natura di questo collegamento, determinando che è molti a uno.

Un'ulteriore direttiva relationship () viene inserita nella classe mappata Customer sotto l'attributo Customer.invoices. Il parametro relationship.back_populate è assegnato per fare riferimento ai nomi degli attributi complementari, in modo che ogni relazione () possa prendere decisioni intelligenti sulla stessa relazione espressa al contrario. Da un lato, Invoices.customer si riferisce all'istanza di Invoices e, dall'altro lato, Customer.invoices si riferisce a un elenco di istanze dei clienti.

La funzione di relazione fa parte dell'API Relationship del pacchetto ORM di SQLAlchemy. Fornisce una relazione tra due classi mappate. Corrisponde a una relazione padre-figlio o tabella associativa.

Di seguito sono riportati i modelli di relazione di base trovati:

Uno a molti

Una relazione Uno a molti si riferisce al genitore con l'aiuto di una chiave esterna nella tabella figlio. relationship () viene quindi specificato sul genitore, come riferimento a una raccolta di elementi rappresentati dal figlio. Il parametro relationship.back_populate viene utilizzato per stabilire una relazione bidirezionale in uno-a-molti, dove il lato "inverso" è un molti a uno.

Molti a uno

D'altra parte, la relazione Molti a uno inserisce una chiave esterna nella tabella padre per fare riferimento al figlio. relazione () è dichiarata sul genitore, dove verrà creato un nuovo attributo di mantenimento scalare. Anche in questo caso il parametro relationship.back_populate viene utilizzato per Bidirectionalbehaviour.

Uno a uno

La relazione One To One è essenzialmente una relazione bidirezionale in natura. Il flag uselist indica il posizionamento di un attributo scalare invece di una raccolta sul lato "molti" della relazione. Per convertire uno-a-molti in un tipo di relazione uno-a-uno, impostare il parametro uselist su false.

Molti a molti

La relazione molti a molti viene stabilita aggiungendo una tabella di associazione relativa a due classi definendo gli attributi con le rispettive chiavi esterne. È indicato dall'argomento secondario di relationship (). Di solito, la tabella utilizza l'oggetto MetaData associato alla classe base dichiarativa, in modo che le direttive ForeignKey possano individuare le tabelle remote con cui collegarsi. Il parametro relationship.back_populated per ogni relationship () stabilisce una relazione bidirezionale. Entrambi i lati della relazione contengono una raccolta.