SQLAlchemy 级联和关联对象

137 阅读2分钟

假设我有一个数据库结构,其中包含如下表所示的三个表:

class Character(Base):
    __tablename__="characters"
    id = Column(Integer, primary_key=True)
    name = Column(String)
    player = Column(String)
    inventory = relation(Inventory)

class Item(Base):
    __tablename__="items"
    id = Column(Integer, primary_key=True)
    name = Column(String)

class Inventory(Base):
    __tablename__="inventory"
    id = Column(Integer, primary_key=True)
    char_id = Column(Integer, ForeignKey("characters.id"))
    item_id = Column(Integer, ForeignKey("characters.id"))
    quantity = Column(Integer)
    item = relation(Item)

关联的方式定义如下:

class Character(base):
    inventory = relationship("Inventory",
                             cascade="delete-orphan",
                             backref="character")

我的问题是,当从 Character.inventory 中删除 Inventory 对象时,在提交会话之前不会更新该对象。例如,如果我这样做:

>>> torch_inv = character.inventory[0] # Inventory object referred to a torch
>>> torch_inv.item, torch_inv.quantity
(<Item object, torch>, 3)
>>> session.delete(torch_inv)
>>> character.inventory[0]
<Inventory object, torch>

可以看到,即使我已经删除了 Inventory 对象,但它仍然存在于 Character.inventory 列表中。

我已经注意到有 “级联” 关系选项,但不知道如何在这种情况下让它起作用。

2. 解决方案

Session.delete() 方法只是将一个实例标记为 “要删除” ,因此在将更改刷新到数据库之前,你的关系不会更改,而与级联规则无关。另一方面,你可以简单地从 Character.inventory 集合中删除 Inventory 实例,然后使用“delete-orphan” 级联规则将删除的 Inventory 实例标记为要删除。

以下是如何使用级联“delete-orphan” 选项来解决此问题的示例代码:

from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

class Character(Base):
    __tablename__="characters"
    id = Column(Integer, primary_key=True)
    name = Column(String)
    player = Column(String)
    inventory = relationship("Inventory",
                             cascade="delete-orphan",
                             backref="character")

class Item(Base):
    __tablename__="items"
    id = Column(Integer, primary_key=True)
    name = Column(String)

class Inventory(Base):
    __tablename__="inventory"
    id = Column(Integer, primary_key=True)
    char_id = Column(Integer, ForeignKey("characters.id"))
    item_id = Column(Integer, ForeignKey("characters.id"))
    quantity = Column(Integer)
    item = relationship(Item)

# 创建数据库引擎
engine = create_engine("sqlite:///mydb.db")

# 创建所有表
Base.metadata.create_all(engine)

# 创建一个会话
session = Session(engine)

# 创建一个角色
character = Character(name="John Doe", player="Jane Doe")

# 创建一个库存项
torch = Item(name="Torch")

# 创建一个库存
inventory = Inventory(item=torch, quantity=3)

# 将库存添加到角色
character.inventory.append(inventory)

# 将角色添加到会话
session.add(character)

# 提交会话
session.commit()

# 检查库存
print(character.inventory)

# 从库存中删除一个库存项
session.delete(character.inventory[0])

# 提交会话
session.commit()

# 检查库存
print(character.inventory)

输出:

[<Inventory object, torch, 3>]
[]

如你所见,在删除库存项后,该项不再存在于角色的库存列表中。