SQLAlchemy ORM - Löschen verwandter Objekte

Es ist einfach, einen Löschvorgang für eine einzelne Tabelle durchzuführen. Sie müssen lediglich ein Objekt der zugeordneten Klasse aus einer Sitzung löschen und die Aktion festschreiben. Das Löschen mehrerer verwandter Tabellen ist jedoch wenig schwierig.

In unserer sales.db-Datenbank werden Kunden- und Rechnungsklassen Kunden- und Rechnungstabellen mit einer bis mehreren Arten von Beziehungen zugeordnet. Wir werden versuchen, das Kundenobjekt zu löschen und das Ergebnis zu sehen.

Im Folgenden finden Sie als Kurzreferenz die Definitionen der Kunden- und Rechnungsklassen.

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

Wir richten eine Sitzung ein und erhalten ein Kundenobjekt, indem wir es mit der primären ID mit dem folgenden Programm abfragen.

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

In unserer Beispieltabelle ist x.name zufällig 'Gopal Krishna'. Löschen wir dieses x aus der Sitzung und zählen das Auftreten dieses Namens.

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

Der resultierende SQL-Ausdruck gibt 0 zurück.

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

Die zugehörigen Rechnungsobjekte von x sind jedoch noch vorhanden. Es kann durch den folgenden Code überprüft werden -

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

Hier sind 10 und 14 Rechnungsnummern des Kunden Gopal Krishna. Das Ergebnis der obigen Abfrage ist 2, was bedeutet, dass die zugehörigen Objekte nicht gelöscht wurden.

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

Dies liegt daran, dass SQLAlchemy nicht das Löschen der Kaskade voraussetzt. Wir müssen einen Befehl geben, um es zu löschen.

Um das Verhalten zu ändern, konfigurieren wir Kaskadenoptionen für die Beziehung User.addresses. Lassen Sie uns die laufende Sitzung schließen, new declative_base () verwenden und die User-Klasse neu deklarieren und die Adressbeziehung einschließlich der Kaskadenkonfiguration hinzufügen.

Das Kaskadenattribut in der Beziehungsfunktion ist eine durch Kommas getrennte Liste von Kaskadenregeln, die bestimmt, wie Sitzungsoperationen von übergeordnet zu untergeordnet „kaskadiert“ werden sollen. Standardmäßig ist es False, was bedeutet, dass es "Update speichern, zusammenführen" ist.

Die verfügbaren Kaskaden sind wie folgt:

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

Die häufig verwendete Option ist "all, delete-orphan", um anzugeben, dass verwandte Objekte in allen Fällen zusammen mit dem übergeordneten Objekt folgen und gelöscht werden sollen, wenn die Zuordnung aufgehoben wird.

Daher wird die neu deklarierte Kundenklasse unten angezeigt -

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

Lassen Sie uns den Kunden mit dem Namen Gopal Krishna mit dem folgenden Programm löschen und die Anzahl der zugehörigen Rechnungsobjekte anzeigen.

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

Die Anzahl ist jetzt 0 mit folgendem SQL, das vom obigen Skript ausgegeben wird -

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