SQLAlchemy ORM - Eliminazione di oggetti correlati
È facile eseguire l'operazione di cancellazione su una singola tabella. Tutto quello che devi fare è eliminare un oggetto della classe mappata da una sessione e eseguire il commit dell'azione. Tuttavia, l'operazione di eliminazione su più tabelle correlate è un po 'complicata.
Nel nostro database sales.db, le classi Customer e Invoice sono mappate alla tabella cliente e fattura con un tipo di relazione uno a molti. Cercheremo di eliminare l'oggetto Cliente e vedere il risultato.
Come riferimento rapido, di seguito sono riportate le definizioni delle classi Cliente e Fattura:
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")
Impostiamo una sessione e otteniamo un oggetto Cliente interrogandolo con l'ID principale utilizzando il programma seguente:
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
session = Session()
x = session.query(Customer).get(2)
Nella nostra tabella di esempio, x.name sembra essere "Gopal Krishna". Cancelliamo questa x dalla sessione e contiamo l'occorrenza di questo nome.
session.delete(x)
session.query(Customer).filter_by(name = 'Gopal Krishna').count()
L'espressione SQL risultante restituirà 0.
SELECT count(*)
AS count_1
FROM (
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.name = ?)
AS anon_1('Gopal Krishna',) 0
Tuttavia, gli oggetti Fattura correlati di x sono ancora presenti. Può essere verificato dal seguente codice:
session.query(Invoice).filter(Invoice.invno.in_([10,14])).count()
Qui, 10 e 14 sono i numeri di fattura appartenenti al cliente Gopal Krishna. Il risultato della query precedente è 2, il che significa che gli oggetti correlati non sono stati eliminati.
SELECT count(*)
AS count_1
FROM (
SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM invoices
WHERE invoices.invno IN (?, ?))
AS anon_1(10, 14) 2
Questo perché SQLAlchemy non presuppone l'eliminazione di cascade; dobbiamo dare un comando per cancellarlo.
Per modificare il comportamento, configuriamo le opzioni a cascata nella relazione User.addresses. Chiudiamo la sessione in corso, usiamo new dichiarative_base () e dichiariamo nuovamente la classe User, aggiungendo nella relazione degli indirizzi inclusa la configurazione a cascata.
L'attributo cascade nella funzione di relazione è un elenco separato da virgole di regole a cascata che determina il modo in cui le operazioni di sessione devono essere "a cascata" da genitore a figlio. Per impostazione predefinita, è False, il che significa che è "salva-aggiorna, unisci".
Le cascate disponibili sono le seguenti:
- save-update
- merge
- expunge
- delete
- delete-orphan
- refresh-expire
L'opzione utilizzata spesso è "all, delete-orphan" per indicare che gli oggetti correlati devono seguire in tutti i casi insieme all'oggetto padre e devono essere eliminati quando vengono rimossi.
Quindi la classe cliente ridichiarata è mostrata di seguito:
class Customer(Base):
__tablename__ = 'customers'
id = Column(Integer, primary_key = True)
name = Column(String)
address = Column(String)
email = Column(String)
invoices = relationship(
"Invoice",
order_by = Invoice.id,
back_populates = "customer",
cascade = "all,
delete, delete-orphan"
)
Eliminiamo il cliente con il nome Gopal Krishna utilizzando il programma seguente e vediamo il conteggio dei suoi oggetti Fattura correlati -
from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind = engine)
session = Session()
x = session.query(Customer).get(2)
session.delete(x)
session.query(Customer).filter_by(name = 'Gopal Krishna').count()
session.query(Invoice).filter(Invoice.invno.in_([10,14])).count()
Il conteggio ora è 0 con il seguente SQL emesso dallo script sopra -
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.id = ?
(2,)
SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM invoices
WHERE ? = invoices.custid
ORDER BY invoices.id (2,)
DELETE FROM invoices
WHERE invoices.id = ? ((1,), (2,))
DELETE FROM customers
WHERE customers.id = ? (2,)
SELECT count(*)
AS count_1
FROM (
SELECT customers.id
AS customers_id, customers.name
AS customers_name, customers.address
AS customers_address, customers.email
AS customers_email
FROM customers
WHERE customers.name = ?)
AS anon_1('Gopal Krishna',)
SELECT count(*)
AS count_1
FROM (
SELECT invoices.id
AS invoices_id, invoices.custid
AS invoices_custid, invoices.invno
AS invoices_invno, invoices.amount
AS invoices_amount
FROM invoices
WHERE invoices.invno IN (?, ?))
AS anon_1(10, 14)
0