SQLAlchemy: ลบน้ำตก


116

ฉันต้องพลาดบางอย่างที่ไม่สำคัญกับตัวเลือกการเรียงซ้อนของ SQLAlchemy เพราะฉันไม่สามารถลบน้ำตกแบบง่ายๆเพื่อให้ทำงานได้อย่างถูกต้อง - หากองค์ประกอบหลักถูกลบลูกจะยังคงอยู่พร้อมกับnullคีย์ต่างประเทศ

ฉันได้ใส่กรณีทดสอบที่กระชับไว้ที่นี่:

from sqlalchemy import Column, Integer, ForeignKey
from sqlalchemy.orm import relationship

from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Parent(Base):
    __tablename__ = "parent"
    id = Column(Integer, primary_key = True)

class Child(Base):
    __tablename__ = "child"
    id = Column(Integer, primary_key = True)
    parentid = Column(Integer, ForeignKey(Parent.id))
    parent = relationship(Parent, cascade = "all,delete", backref = "children")

engine = create_engine("sqlite:///:memory:")
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)

session = Session()

parent = Parent()
parent.children.append(Child())
parent.children.append(Child())
parent.children.append(Child())

session.add(parent)
session.commit()

print "Before delete, children = {0}".format(session.query(Child).count())
print "Before delete, parent = {0}".format(session.query(Parent).count())

session.delete(parent)
session.commit()

print "After delete, children = {0}".format(session.query(Child).count())
print "After delete parent = {0}".format(session.query(Parent).count())

session.close()

เอาท์พุท:

Before delete, children = 3
Before delete, parent = 1
After delete, children = 3
After delete parent = 0

มีความสัมพันธ์แบบหนึ่งต่อกลุ่มที่เรียบง่ายระหว่างผู้ปกครองและเด็ก สคริปต์สร้างพาเรนต์เพิ่มลูก 3 ตัวจากนั้นคอมมิต ถัดไปจะลบผู้ปกครอง แต่เด็ก ๆ ยังคงอยู่ ทำไม? ฉันจะทำให้เด็กลบน้ำตกได้อย่างไร


ส่วนนี้ในเอกสาร (อย่างน้อยตอนนี้ 3 ปีต่อมาหลังจากโพสต์ต้นฉบับ) ดูเหมือนว่าจะมีประโยชน์ในเรื่องนี้: docs.sqlalchemy.org/en/rel_0_9/orm/session.html#cascades
Soferio

คำตอบ:


185

ปัญหาคือ sqlalchemy ถือว่าChildเป็นผู้ปกครองเพราะนั่นคือจุดที่คุณกำหนดความสัมพันธ์ของคุณ (แน่นอนว่าคุณไม่สนใจว่าคุณเรียกมันว่า "เด็ก")

หากคุณกำหนดความสัมพันธ์ในParentชั้นเรียนแทนจะได้ผล:

children = relationship("Child", cascade="all,delete", backref="parent")

(หมายเหตุ"Child"เป็นสตริง: อนุญาตให้ใช้เมื่อใช้สไตล์การประกาศเพื่อให้คุณสามารถอ้างถึงคลาสที่ยังไม่ได้กำหนด)

คุณอาจต้องการเพิ่มdelete-orphanด้วยเช่นกัน ( deleteทำให้เด็กถูกลบเมื่อผู้ปกครองถูกลบและdelete-orphanลบเด็ก ๆ ที่ "ลบ" ออกจากพาเรนต์แม้ว่าจะไม่ได้ลบพาเรนต์ก็ตาม)

แก้ไข: เพิ่งค้นพบ: หากคุณต้องการกำหนดความสัมพันธ์ในคลาสจริงๆChildคุณสามารถทำได้ แต่คุณจะต้องกำหนดน้ำตกที่ด้านหลัง (โดยการสร้าง backref อย่างชัดเจน) ดังนี้:

parent = relationship(Parent, backref=backref("children", cascade="all,delete"))

(หมายถึงfrom sqlalchemy.orm import backref)


6
อ๊ะนี่มัน ฉันหวังว่าเอกสารนี้จะชัดเจนกว่านี้!
carl

15
ใช่ เป็นประโยชน์มาก ฉันมีปัญหากับเอกสารของ SQLAlchemy เสมอ
ayaz

1
สิ่งนี้อธิบายได้ดีในเอกสารปัจจุบันdocs.sqlalchemy.org/en/rel_0_9/orm/cascades.html
Epoc

1
@ Lyman Zerga: ในตัวอย่างของ OP: หากคุณลบChildวัตถุออกจากนั้นวัตถุparent.childrenนั้นควรจะถูกลบออกจากฐานข้อมูลหรือควรจะลบเฉพาะการอ้างอิงถึงพาเรนต์เท่านั้น (เช่นตั้งค่าparentidคอลัมน์เป็น null แทนที่จะลบแถว)
Steven

1
เดี๋ยวก่อนrelationshipไม่ได้กำหนดการตั้งค่าแม่ลูก การใช้งานForeignKeyบนโต๊ะเป็นสิ่งที่ทำให้เด็ก ๆ ไม่สำคัญว่าrelationshipผู้ปกครองหรือเด็ก
d512

110

@ Steven's asnwer เป็นสิ่งที่ดีเมื่อคุณกำลังลบsession.delete()สิ่งที่ไม่เคยเกิดขึ้นในกรณีของฉัน ฉันสังเกตเห็นว่าส่วนใหญ่ฉันลบผ่านsession.query().filter().delete()(ซึ่งไม่ได้ใส่องค์ประกอบในหน่วยความจำและลบโดยตรงจาก db) การใช้วิธีนี้ sqlalchemy cascade='all, delete'ไม่ได้ผล มีวิธีแก้ไข: ON DELETE CASCADEผ่าน db (หมายเหตุ: ฐานข้อมูลบางส่วนไม่รองรับ)

class Child(Base):
    __tablename__ = "children"

    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey("parents.id", ondelete='CASCADE'))

class Parent(Base):
    __tablename__ = "parents"

    id = Column(Integer, primary_key=True)
    child = relationship(Child, backref="parent", passive_deletes=True)

3
ขอบคุณที่อธิบายความแตกต่างนี้ - ฉันพยายามใช้session.query().filter().delete()และพยายามหาปัญหา
nighthawk454

4
ฉันต้องตั้งค่าpassive_deletes='all'เพื่อให้เด็ก ๆ ถูกลบโดยฐานข้อมูล cascade เมื่อผู้ปกครองถูกลบ ด้วยเหตุpassive_deletes=Trueนี้อ็อบเจ็กต์ลูกจะถูกยกเลิกการเชื่อมโยง (กำหนดพาเรนต์เป็น NULL) ก่อนที่พาเรนต์จะถูกลบดังนั้นการเรียงซ้อนฐานข้อมูลจึงไม่ได้ทำอะไรเลย
Milorad Pop-Tosic

@ MiloradPop-Tosic ฉันไม่ได้ใช้ SQLAlchemy มานานกว่า 3 ปีแล้ว แต่การอ่านเอกสารดูเหมือนว่า passive_deletes = True ยังคงเป็นสิ่งที่ถูกต้อง
Alex Okrushko

2
ฉันสามารถยืนยันได้ว่าpassive_deletes=Trueทำงานได้อย่างถูกต้องในสถานการณ์นี้
d512

ฉันมีปัญหากับการสร้างการแก้ไขอัตโนมัติที่สร้างขึ้นโดยอัตโนมัติซึ่งรวมถึงการเรียงซ้อนในการลบ - นี่คือคำตอบ
JNW

105

โพสต์เก่า ๆ สวย ๆ แต่ฉันใช้เวลาแค่ชั่วโมงหรือสองชั่วโมงกับเรื่องนี้ฉันจึงอยากแบ่งปันสิ่งที่ฉันค้นพบโดยเฉพาะอย่างยิ่งเนื่องจากความคิดเห็นอื่น ๆ บางส่วนที่ระบุไว้ไม่ถูกต้อง

TL; DR

ให้ตารางลูกเป็นสิ่งแปลกปลอมหรือแก้ไขตารางที่มีอยู่โดยเพิ่มondelete='CASCADE':

parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))

และหนึ่งในความสัมพันธ์ต่อไปนี้:

ก) สิ่งนี้บนตารางหลัก:

children = db.relationship('Child', backref='parent', passive_deletes=True)

b) หรือบนโต๊ะเด็ก:

parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))

รายละเอียด

ออกก่อนแม้จะมีสิ่งที่เป็นคำตอบที่ได้รับการยอมรับว่าการ / ความสัมพันธ์ผู้ปกครองเด็กไม่ได้รับการจัดตั้งขึ้นโดยใช้ก็จัดตั้งขึ้นโดยใช้relationship ForeignKeyคุณสามารถวางrelationshipบนตารางหลักหรือตารางย่อยและจะใช้งานได้ดี แม้ว่าจะเห็นได้ชัดว่าบนตารางลูกคุณต้องใช้backrefฟังก์ชันนี้นอกเหนือจากอาร์กิวเมนต์คำหลัก

ตัวเลือกที่ 1 (แนะนำ)

ประการที่สอง SqlAlchemy รองรับการเรียงซ้อนสองประเภทที่แตกต่างกัน อันแรกและแบบที่ฉันแนะนำนั้นมีอยู่ในฐานข้อมูลของคุณและโดยปกติจะอยู่ในรูปแบบของข้อ จำกัด ในการประกาศคีย์ต่างประเทศ ใน PostgreSQL จะมีลักษณะดังนี้:

CONSTRAINT child_parent_id_fkey FOREIGN KEY (parent_id)
REFERENCES parent_table(id) MATCH SIMPLE
ON DELETE CASCADE

ซึ่งหมายความว่าเมื่อคุณลบระเบียนออกจากparent_tableนั้นchild_tableฐานข้อมูลจะลบแถวที่เกี่ยวข้องทั้งหมดให้คุณ รวดเร็วและเชื่อถือได้และอาจเป็นทางออกที่ดีที่สุดของคุณ คุณตั้งค่านี้ใน SqlAlchemy ผ่านทางForeignKeyนี้ (ส่วนหนึ่งของนิยามตารางลูก):

parent_id = db.Column(db.Integer, db.ForeignKey('parent.id', ondelete='CASCADE'))
parent = db.relationship('Parent', backref=backref('children', passive_deletes=True))

ondelete='CASCADE'เป็นส่วนหนึ่งที่สร้างON DELETE CASCADEบนโต๊ะ

Gotcha!

มีข้อแม้ที่สำคัญที่นี่ สังเกตว่าฉันมีrelationshipระบุด้วยpassive_deletes=True? หากคุณไม่มีสิ่งนั้นทั้งหมดจะไม่ทำงาน นี่เป็นเพราะโดยค่าเริ่มต้นเมื่อคุณลบบันทึกหลัก SqlAlchemy ทำอะไรแปลก ๆ NULLมันชุดคีย์ต่างประเทศของทุกแถวเด็ก ดังนั้นหากคุณลบแถวจากparent_tableที่id= 5 มันก็จะดำเนินการโดยทั่วไป

UPDATE child_table SET parent_id = NULL WHERE parent_id = 5

ทำไมคุณถึงต้องการสิ่งนี้ฉันไม่รู้ ฉันจะแปลกใจถ้าเอ็นจินฐานข้อมูลจำนวนมากอนุญาตให้คุณตั้งค่าคีย์ต่างประเทศที่ถูกต้องเพื่อNULLสร้างเด็กกำพร้า ดูเหมือนจะเป็นความคิดที่ไม่ดี แต่อาจมีกรณีการใช้งาน อย่างไรก็ตามหากคุณปล่อยให้ SqlAlchemy ทำเช่นนี้คุณจะป้องกันไม่ให้ฐานข้อมูลไม่สามารถล้างข้อมูลเด็กโดยใช้ON DELETE CASCADEที่คุณตั้งค่าไว้ เนื่องจากต้องอาศัยคีย์ต่างประเทศเหล่านั้นเพื่อให้ทราบว่าจะลบแถวลูกใด เมื่อ SqlAlchemy ตั้งค่าทั้งหมดNULLเป็นฐานข้อมูลจะไม่สามารถลบออกได้ การตั้งค่าpassive_deletes=Trueป้องกันไม่ให้ SqlAlchemy NULLนำคีย์แปลกปลอมออกมา

คุณสามารถอ่านเพิ่มเติมเกี่ยวกับการลบเรื่อย ๆ ในเอกสาร sqlalchemy

ทางเลือกที่ 2

วิธีอื่นที่คุณสามารถทำได้คือให้ SqlAlchemy ทำแทนคุณ สิ่งนี้ตั้งค่าโดยใช้cascadeอาร์กิวเมนต์ของrelationship. หากคุณมีความสัมพันธ์ที่กำหนดไว้ในตารางหลักจะมีลักษณะดังนี้:

children = relationship('Child', cascade='all,delete', backref='parent')

หากความสัมพันธ์อยู่ที่เด็กคุณจะทำเช่นนี้:

parent = relationship('Parent', backref=backref('children', cascade='all,delete'))

อีกครั้งนี่คือลูกดังนั้นคุณต้องเรียกเมธอดที่เรียกว่าbackrefและใส่ข้อมูลแบบเรียงซ้อนลงไป

ด้วยสิ่งนี้เมื่อคุณลบแถวหลัก SqlAlchemy จะเรียกใช้คำสั่งลบเพื่อให้คุณล้างแถวลูก สิ่งนี้อาจไม่มีประสิทธิภาพเท่ากับการปล่อยให้ฐานข้อมูลนี้จัดการหากสำหรับคุณดังนั้นฉันจึงไม่แนะนำ

นี่คือเอกสาร SqlAlchemyเกี่ยวกับคุณสมบัติการเรียงซ้อนที่รองรับ


ขอบคุณสำหรับคำอธิบาย มันสมเหตุสมผลแล้ว
Odin

1
เหตุใดการประกาศColumnในตารางรองว่าใช้ForeignKey('parent.id', ondelete='cascade', onupdate='cascade')ไม่ได้เช่นกัน ฉันคาดว่าเด็ก ๆ จะถูกลบเมื่อแถวตารางหลักของพวกเขาถูกลบไปด้วย แต่ SQLA จะตั้งค่าเด็กเป็น a parent.id=NULLหรือปล่อยให้เป็น "ตามสภาพ" แต่จะไม่มีการลบ หลังจากที่กำหนดrelationshipในผู้ปกครองเป็นchildren = relationship('Parent', backref='parent')หรือrelationship('Parent', backref=backref('parent', passive_deletes=True)); DB แสดงcascadeกฎใน DDL (SQLite3-based proof-of-concept) คิด?
code_dredd

1
นอกจากนี้ฉันควรทราบว่าเมื่อฉันใช้backref=backref('parent', passive_deletes=True)ฉันได้รับคำเตือนต่อไปนี้: SAWarning: On Parent.children, 'passive_deletes' is normally configured on one-to-many, one-to-one, many-to-many relationships only. "relationships only." % selfแนะนำว่าไม่ชอบใช้passive_deletes=Trueในความสัมพันธ์แบบหนึ่งต่อหลายแม่ลูกด้วยเหตุผลบางประการ
code_dredd

คำอธิบายที่ยอดเยี่ยม คำถามเดียว - deleteซ้ำซ้อนในcascade='all,delete'?
zaggi

1
@zaggi deleteซ้ำซ้อนในcascade='all,delete'เนื่องจากเป็นไปตามเอกสารของ SQLAlchemy , allเป็นคำพ้องสำหรับ:save-update, merge, refresh-expire, expunge, delete
pmsoltani

7

Steven ถูกต้องในการที่คุณต้องสร้าง backref อย่างชัดเจนซึ่งส่งผลให้มีการใช้ cascade กับพาเรนต์ (ซึ่งตรงข้ามกับการใช้กับเด็กเช่นในสถานการณ์ทดสอบ)

อย่างไรก็ตามการกำหนดความสัมพันธ์กับเด็กไม่ได้ทำให้ sqlalchemy ถือว่าเด็กเป็นผู้ปกครอง ไม่สำคัญว่าความสัมพันธ์จะถูกกำหนดไว้ที่ใด (ลูกหรือพาเรนต์) คีย์ต่างประเทศที่เชื่อมโยงตารางสองตารางที่กำหนดว่าเป็นพาเรนต์และอันไหนคือลูก

มันสมเหตุสมผลแล้วที่จะยึดติดกับอนุสัญญาเดียวและจากคำตอบของสตีเวนฉันกำลังกำหนดความสัมพันธ์ลูกทั้งหมดของฉันกับผู้ปกครอง


6

ฉันพยายามดิ้นรนกับเอกสารเช่นกัน แต่พบว่า docstrings เองมักจะง่ายกว่าคู่มือ ตัวอย่างเช่นหากคุณนำเข้าความสัมพันธ์จาก sqlalchemy.orm และทำการ help (ความสัมพันธ์) จะให้ตัวเลือกทั้งหมดที่คุณสามารถระบุสำหรับน้ำตกได้ สัญลักษณ์แสดงหัวข้อย่อยสำหรับdelete-orphanพูดว่า:

หากตรวจพบรายการประเภทของเด็กที่ไม่มีผู้ปกครองให้ทำเครื่องหมายเพื่อลบ
โปรดทราบว่าตัวเลือกนี้จะป้องกันไม่ให้รายการที่รอดำเนินการของชั้นเรียนของเด็กคงอยู่โดยไม่มีผู้ปกครองอยู่

ฉันตระหนักดีว่าปัญหาของคุณมีมากขึ้นเมื่อใช้เอกสารสำหรับกำหนดความสัมพันธ์ระหว่างแม่ลูก แต่มันดูเหมือนว่าคุณยังอาจจะมีปัญหากับตัวเลือกน้ำตกเพราะมี "all" เป็นตัวเลือกเดียวที่ไม่ได้รวมอยู่ใน"delete""delete-orphan""all"


ใช้help(..)กับsqlalchemyสิ่งของช่วยได้มาก! ขอบคุณ :-)))! PyCharm ไม่แสดงสิ่งใดในท่าเทียบเรือบริบทและลืมตรวจสอบไฟล์help. ขอบคุณมาก!
dmitry_romanov

5

คำตอบของสตีเวนนั้นมั่นคง ฉันต้องการชี้ให้เห็นความหมายเพิ่มเติม

ด้วยการใช้relationshipงานคุณกำลังสร้างเลเยอร์แอป (Flask) ที่รับผิดชอบต่อความสมบูรณ์ของการอ้างอิง นั่นหมายความว่ากระบวนการอื่น ๆ ที่เข้าถึงฐานข้อมูลที่ไม่ได้ผ่าน Flask เช่นยูทิลิตี้ฐานข้อมูลหรือบุคคลที่เชื่อมต่อกับฐานข้อมูลโดยตรงจะไม่พบข้อ จำกัด เหล่านั้นและอาจเปลี่ยนแปลงข้อมูลของคุณในลักษณะที่ทำลายโมเดลข้อมูลเชิงตรรกะที่คุณทำงานอย่างหนักเพื่อออกแบบ .

เมื่อใดก็ตามที่เป็นไปได้ให้ใช้ForeignKeyแนวทางที่อธิบายโดย d512 และ Alex เอ็นจิ้น DB นั้นดีมากในการบังคับใช้ข้อ จำกัด อย่างแท้จริง (ในทางที่หลีกเลี่ยงไม่ได้) ดังนั้นนี่จึงเป็นกลยุทธ์ที่ดีที่สุดในการรักษาความสมบูรณ์ของข้อมูล ครั้งเดียวที่คุณต้องพึ่งพาแอปเพื่อจัดการความสมบูรณ์ของข้อมูลคือเมื่อฐานข้อมูลไม่สามารถจัดการได้เช่นเวอร์ชันของ SQLite ที่ไม่รองรับคีย์ต่างประเทศ

หากคุณจำเป็นต้องสร้างการเชื่อมโยงระหว่างหน่วยงานต่อไปเพื่อเปิดใช้งานแอปพลิเคพฤติกรรมเช่นการนำความสัมพันธ์วัตถุผู้ปกครองเด็กที่ใช้ร่วมกับbackrefForeignKey


2

คำตอบโดย Stevan สมบูรณ์แบบ แต่ถ้าคุณยังคงได้รับข้อผิดพลาด การลองอื่น ๆ ที่เป็นไปได้คือ -

http://vincentaudebert.github.io/python/sql/2015/10/09/cascade-delete-sqlalchemy/

คัดลอกจากลิงค์ -

เคล็ดลับด่วนหากคุณมีปัญหากับการพึ่งพาคีย์ภายนอกแม้ว่าคุณจะระบุการลบแบบเรียงซ้อนในโมเดลของคุณ

ใช้ SQLAlchemy เพื่อระบุการลบแบบเรียงซ้อนที่คุณควรมีcascade='all, delete'ในตารางหลักของคุณ ตกลง แต่เมื่อคุณดำเนินการบางอย่างเช่น:

session.query(models.yourmodule.YourParentTable).filter(conditions).delete()

มันทำให้เกิดข้อผิดพลาดเกี่ยวกับคีย์ต่างประเทศที่ใช้ในตารางย่อยของคุณ

วิธีแก้ปัญหาที่ฉันใช้เพื่อค้นหาวัตถุแล้วลบออก:

session = models.DBSession()
your_db_object = session.query(models.yourmodule.YourParentTable).filter(conditions).first()
if your_db_object is not None:
    session.delete(your_db_object)

สิ่งนี้ควรลบบันทึกหลักของคุณและลูก ๆ ทั้งหมดที่เกี่ยวข้อง


1
.first()จำเป็นต้องโทรหรือไม่? เงื่อนไขตัวกรองใดที่ส่งคืนรายการวัตถุและทุกอย่างจะต้องถูกลบ การโทรไม่.first()ได้รับเพียงวัตถุแรกหรือไม่? @Prashant
Kavin Raju S

2

คำตอบของ Alex Okrushko เกือบจะได้ผลดีที่สุดสำหรับฉัน ใช้ ondelete = 'CASCADE' และ passive_deletes = True รวมกัน แต่ฉันต้องทำอะไรเพิ่มเติมเพื่อให้มันใช้งานได้กับ sqlite

Base = declarative_base()
ROOM_TABLE = "roomdata"
FURNITURE_TABLE = "furnituredata"

class DBFurniture(Base):
    __tablename__ = FURNITURE_TABLE
    id = Column(Integer, primary_key=True)
    room_id = Column(Integer, ForeignKey('roomdata.id', ondelete='CASCADE'))


class DBRoom(Base):
    __tablename__ = ROOM_TABLE
    id = Column(Integer, primary_key=True)
    furniture = relationship("DBFurniture", backref="room", passive_deletes=True)

อย่าลืมเพิ่มรหัสนี้เพื่อให้แน่ใจว่าใช้ได้กับ sqlite

from sqlalchemy import event
from sqlalchemy.engine import Engine
from sqlite3 import Connection as SQLite3Connection

@event.listens_for(Engine, "connect")
def _set_sqlite_pragma(dbapi_connection, connection_record):
    if isinstance(dbapi_connection, SQLite3Connection):
        cursor = dbapi_connection.cursor()
        cursor.execute("PRAGMA foreign_keys=ON;")
        cursor.close()

ถูกขโมยจากที่นี่: ภาษานิพจน์ SQLAlchemy และ SQLite ในการลบ cascade


0

TLDR:หากวิธีแก้ปัญหาข้างต้นไม่ได้ผลให้ลองเพิ่ม nullable = False ลงในคอลัมน์ของคุณ

ฉันต้องการเพิ่มจุดเล็ก ๆ ที่นี่สำหรับบางคนที่อาจไม่ได้รับฟังก์ชัน cascade เพื่อทำงานกับโซลูชันที่มีอยู่ (ซึ่งดีมาก) ความแตกต่างที่สำคัญระหว่างงานของฉันกับตัวอย่างคือฉันใช้ระบบอัตโนมัติ ฉันไม่รู้ว่ามันอาจรบกวนการตั้งค่าน้ำตกได้อย่างไร แต่ฉันต้องการทราบว่าฉันใช้มัน ฉันกำลังทำงานกับฐานข้อมูล SQLite ด้วย

ฉันลองใช้วิธีแก้ปัญหาทั้งหมดที่อธิบายไว้ที่นี่ แต่แถวในตารางลูกของฉันยังคงมีคีย์ต่างประเทศที่ตั้งค่าเป็น null เมื่อแถวหลักถูกลบ ฉันลองใช้วิธีแก้ปัญหาทั้งหมดที่นี่แล้ว แต่ก็ไม่มีประโยชน์ อย่างไรก็ตาม cascade ใช้งานได้เมื่อฉันตั้งค่าคอลัมน์ลูกด้วยคีย์นอกเป็น nullable = False

บนโต๊ะเด็กฉันเพิ่ม:

Column('parent_id', Integer(), ForeignKey('parent.id', ondelete="CASCADE"), nullable=False)
Child.parent = relationship("parent", backref=backref("children", passive_deletes=True)

ด้วยการตั้งค่านี้น้ำตกจะทำงานตามที่คาดไว้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.