SQLAlchemy ORM - usuwanie powiązanych obiektów

Operację usuwania na pojedynczej tabeli można łatwo wykonać. Wszystko, co musisz zrobić, to usunąć obiekt zamapowanej klasy z sesji i zatwierdzić akcję. Jednak operacja usuwania na wielu powiązanych tabelach jest trochę trudna.

W naszej bazie danych sales.db klasy Klient i Faktura są mapowane na tabelę klientów i faktur z typami relacji jeden do wielu. Spróbujemy usunąć obiekt klienta i zobaczyć wynik.

W skrócie poniżej znajdują się definicje klas klienta i faktury -

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

Konfigurujemy sesję i uzyskujemy obiekt klienta, odpytując go o podstawowy identyfikator za pomocą poniższego programu -

from sqlalchemy.orm import sessionmaker
Session = sessionmaker(bind=engine)
session = Session()
x = session.query(Customer).get(2)

W naszej przykładowej tabeli x.name to „Gopal Krishna”. Usuńmy ten x z sesji i policzmy wystąpienie tej nazwy.

session.delete(x)
session.query(Customer).filter_by(name = 'Gopal Krishna').count()

Wynikowe wyrażenie SQL zwróci 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

Jednak powiązane obiekty faktury z x nadal tam są. Można to zweryfikować za pomocą następującego kodu -

session.query(Invoice).filter(Invoice.invno.in_([10,14])).count()

Tutaj 10 i 14 to numery faktur należące do klienta Gopal Krishna. Wynik powyższego zapytania to 2, co oznacza, że ​​powiązane obiekty nie zostały usunięte.

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

Dzieje się tak, ponieważ SQLAlchemy nie zakłada usunięcia kaskady; musimy wydać polecenie usunięcia go.

Aby zmienić zachowanie, konfigurujemy opcje kaskadowe w relacji Użytkownik.adresy. Zamknijmy trwającą sesję, użyjmy nowej declarative_base () i ponownie zadeklarujmy klasę User, dodając relację adresów, w tym konfigurację kaskadową.

Atrybut kaskady w funkcji relacji jest rozdzieloną przecinkami listą reguł kaskadowych, która określa, w jaki sposób operacje sesji powinny być „kaskadowane” od rodzica do podrzędnego. Domyślnie jest to Fałsz, co oznacza, że ​​jest to „zapisz-aktualizacja, scal”.

Dostępne kaskady są następujące -

  • save-update
  • merge
  • expunge
  • delete
  • delete-orphan
  • refresh-expire

Często używaną opcją jest „wszystkie, usuń-osierocone”, aby wskazać, że powiązane obiekty powinny we wszystkich przypadkach towarzyszyć obiektowi nadrzędnemu i zostać usunięte po usunięciu skojarzenia.

W związku z tym ponownie zadeklarowana klasa klienta jest pokazana poniżej -

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

Usuńmy klienta z nazwą Gopal Krishna za pomocą poniższego programu i zobaczmy liczbę powiązanych z nim obiektów faktur -

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

Liczba wynosi teraz 0, a następujący kod SQL jest emitowany przez powyższy skrypt -

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