我用 Python 编写了一些代码来管理一些数据库内容。 我想做的就是删除一对多关系的所有父母,这应该自动删除所有孩子。这是我的示例代码:
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, Session
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Parent(Base):
__tablename__ = 'parents'
id = Column(Integer, primary_key=True)
name = Column(String)
single_parent = True
children = relationship("Child", back_populates="parent", cascade="all, delete-orphan")
class Child(Base):
__tablename__ = 'children'
id = Column(Integer, primary_key=True)
name = Column(String)
parent_id = Column(Integer, ForeignKey('parents.id'))
parent = relationship('Parent', back_populates='children')
engine = create_engine("sqlite:///test.db", echo=True)
Base.metadata.create_all(bind=engine)
session = Session(engine)
# create data
parent1 = Parent(name='Parent 1', children=[Child(name='Child 1'), Child(name='Child 2')])
parent2 = Parent(name='Parent 2', children=[Child(name='Child 3'), Child(name='Child 4')])
session.add_all([parent1, parent2])
session.commit()
# Check
print("Before:")
print(session.query(Parent).all())
print(session.query(Child).all())
# Delete
session.query(Parent).delete()
session.commit()
运行时,父元素会被删除,但子元素不会被删除。 我在这里做错了什么? 非常感谢您的帮助!
您需要修改
parent_id
声明:
更换:
parent_id = Column(Integer, ForeignKey('parents.id'))
与:
parent_id = Column(Integer, ForeignKey('parents.id', ondelete='CASCADE'))
但是,当您使用
SQLite
作为数据库时,这是不够的,因为 默认情况下未启用外键约束。 PRAGMA
指令启用外键约束:PRAGMA foreign_keys = ON;
或者您可以通过监听事件使用“SQLAlchemy”来处理此功能:
from sqlalchemy.event import listens_for
from sqlalchemy.engine import Engine
from sqlite3 import Connection as SQLite3Connection
# enable foreign keys. if you do not do this, ON DELETE CASCADE fails silently!
@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()
测试:
# Check
print("Before:")
print(session.query(Parent).all())
print(session.query(Child).all())
# Delete only one parent
session.query(Parent).filter_by(name='Parent 2').delete()
session.commit()
# Check
print("After:")
print(session.query(Parent).all())
print(session.query(Child).all())
输出:
# I just modified the __repr__ of each table
Before:
[Parent(id=1, name='Parent 1'), Parent(id=2, name='Parent 2')]
[Child(id=1, name='Child 1', parent_name='Parent 1'), Child(id=2, name='Child 2', parent_name='Parent 1'), Child(id=3, name='Child 3', parent_name='Parent 2'), Child(id=4, name='Child 4', parent_name='Parent 2')]
After:
[Parent(id=1, name='Parent 1')]
[Child(id=1, name='Child 1', parent_name='Parent 1'), Child(id=2, name='Child 2', parent_name='Parent 1')]
您不需要分别删除父级和子级,如果删除父级,子级也会被删除。
此代码将起作用:
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship, Session
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Parent(Base):
__tablename__ = 'parents'
id = Column(Integer, primary_key=True)
name = Column(String)
single_parent = True
children = relationship("Child", back_populates="parent", cascade="all, delete-orphan")
class Child(Base):
__tablename__ = 'children'
id = Column(Integer, primary_key=True)
name = Column(String)
parent_id = Column(Integer, ForeignKey('parents.id'))
parent = relationship('Parent', back_populates='children')
engine = create_engine("sqlite:///test.db", echo=True)
Base.metadata.create_all(bind=engine)
session = Session(engine)
# create data
parent1 = Parent(name='Parent 1', children=[Child(name='Child 1'), Child(name='Child 2')])
parent2 = Parent(name='Parent 2', children=[Child(name='Child 3'), Child(name='Child 4')])
session.add_all([parent1, parent2])
session.commit()
# Check
print("Before:")
print(session.query(Parent).all())
print(session.query(Child).all())
# Delete
session.query(Parent).delete()
session.commit()