级联¶ T0>

映射器支持在relationship()结构上配置cascade行为的概念。这涉及如何将相对于特定Session的“父”对象执行的操作传播到由该关系引用的项目(例如“子”对象),并且受到relationship.cascade选项。

级联的默认行为仅限于所谓的save-updatemerge设置的级联。级联的典型“替代”设置是添加deletedelete-orphan选项;这些设置适用于相关对象,只要它们连接到它们的父级,并且以其他方式删除,它们就会存在。

relationship()中使用cascade选项配置级联行为:

class Order(Base):
    __tablename__ = 'order'

    items = relationship("Item", cascade="all, delete-orphan")
    customer = relationship("User", cascade="save-update")

要设置backref上的级联,同一个标志可以与backref()函数一起使用,该函数最终将其参数返回给relationship()

class Item(Base):
    __tablename__ = 'item'

    order = relationship("Order",
                    backref=backref("items", cascade="all, delete-orphan")
                )

cascade的默认值是save-update merge此参数的典型替代设置是all或更常见的all, delete-orphanThe all symbol is a synonym for save-update, merge, refresh-expire, expunge, delete, and using it in conjunction with delete-orphan indicates that the child object should follow along with its parent in all cases, and be deleted once it is no longer associated with that parent.

以下小节介绍了可以为cascade参数指定的可用值列表。

保存更新¶ T0>

save-update cascade indicates that when an object is placed into a Session via Session.add(), all the objects associated with it via this relationship() should also be added to that same Session. 假设我们有一个包含两个相关对象address1address2的对象user1

>>> user1 = User()
>>> address1, address2 = Address(), Address()
>>> user1.addresses = [address1, address2]

If we add user1 to a Session, it will also add address1, address2 implicitly:

>>> sess = Session()
>>> sess.add(user1)
>>> address1 in sess
True

save-update级联还影响已存在于Session中的对象的属性操作。If we add a third object, address3 to the user1.addresses collection, it becomes part of the state of that Session:

>>> address3 = Address()
>>> user1.append(address3)
>>> address3 in sess
>>> True

save-update has the possibly surprising behavior which is that persistent objects which were removed from a collection or in some cases a scalar attribute may also be pulled into the Session of a parent object; this is so that the flush process may handle that related object appropriately. 这种情况通常只能在从一个Session中删除一个对象并添加到另一个时才会出现:

>>> user1 = sess1.query(User).filter_by(id=1).first()
>>> address1 = user1.addresses[0]
>>> sess1.close()   # user1, address1 no longer associated with sess1
>>> user1.addresses.remove(address1)  # address1 no longer associated with user1
>>> sess2 = Session()
>>> sess2.add(user1)   # ... but it still gets added to the new session,
>>> address1 in sess2  # because it's still "pending" for flush
True

save-update级联在默认情况下处于启用状态,通常视为理所当然;它通过对Session.add()的单个调用一次性在该Session内注册对象的整个结构来简化代码。虽然它可以被禁用,但通常不需要这样做。

One case where save-update cascade does sometimes get in the way is in that it takes place in both directions for bi-directional relationships, e.g. backrefs, meaning that the association of a child object with a particular parent can have the effect of the parent object being implicitly associated with that child object’s Session; this pattern, as well as how to modify its behavior using the cascade_backrefs flag, is discussed in the section Controlling Cascade on Backrefs.

删除¶ T0>

delete级联指示当“父”对象被标记为删除时,其相关的“子”对象也应被标记为删除。例如,如果我们有User.addressesdelete级联关系的配置关系:

class User(Base):
    # ...

    addresses = relationship("Address", cascade="save-update, merge, delete")

如果使用上述映射,我们有一个User对象和两个相关的Address对象:

>>> user1 = sess.query(User).filter_by(id=1).first()
>>> address1, address2 = user1.addresses

如果我们将user1标记为删除,在刷新操作继续之后,address1address2也将被删除:

>>> sess.delete(user1)
>>> sess.commit()
DELETE FROM address WHERE address.id = ? ((1,), (2,)) DELETE FROM user WHERE user.id = ? (1,) COMMIT

Alternatively, if our User.addresses relationship does not have delete cascade, SQLAlchemy’s default behavior is to instead de-associate address1 and address2 from user1 by setting their foreign key reference to NULL. 使用如下映射:

class User(Base):
    # ...

    addresses = relationship("Address")

在删除父User对象时,address中的行不会被删除,而是取消关联:

>>> sess.delete(user1)
>>> sess.commit()
UPDATE address SET user_id=? WHERE address.id = ? (None, 1) UPDATE address SET user_id=? WHERE address.id = ? (None, 2) DELETE FROM user WHERE user.id = ? (1,) COMMIT

delete cascade is more often than not used in conjunction with delete-orphan cascade, which will emit a DELETE for the related row if the “child” object is deassociated from the parent. deletedelete-orphan级联的组合涵盖了SQLAlchemy必须决定将外键列设置为NULL还是完全删除行的两种情况。

ORM级别“删除”级联与FOREIGN KEY级别“ON DELETE”级联

The behavior of SQLAlchemy’s “delete” cascade has a lot of overlap with the ON DELETE CASCADE feature of a database foreign key, as well as with that of the ON DELETE SET NULL foreign key setting when “delete” cascade is not specified. Database level “ON DELETE” cascades are specific to the “FOREIGN KEY” construct of the relational database; SQLAlchemy allows configuration of these schema-level constructs at the DDL level using options on ForeignKeyConstraint which are described at ON UPDATE and ON DELETE.

重要的是要注意ORM和关系数据库的“级联”概念之间的区别以及它们如何整合:

当使用relationship()时,也使用secondary选项包含多对多表,SQLAlchemy的删除级联会自动处理此多对多表中的行。就像正如Deleting Rows from the Many to Many Table中所描述的那样,从多对多集合中添加或删除对象会导致INSERT或DELETE行中的many-当由于父对象删除操作而激活时,delete级联会删除“child”表中的行,但也会删除多对多表中的行。

删除-孤儿¶ T0>

delete-orphan级联会将行为添加到delete级联中,以便将子对象从父级中解除关联时标记为删除,而不仅仅是父级被标记为删除。在处理由其父代“拥有”的相关对象时,这是一个常见功能,使用NOT NULL外键,因此从父集合中删除项目会导致其删除。

delete-orphan cascade implies that each child object can only have one parent at a time, so is configured in the vast majority of cases on a one-to-many relationship. 将其设置为多对一或多对多的关系更为尴尬;对于这个用例,SQLAlchemy要求使用single_parent参数配置relationship(),建立Python端验证,确保该对象仅与一个父对象关联时间。

合并¶ T0>

merge cascade指示Session.merge()操作应该从作为Session.merge()主题的父级传播引用对象。这个级联也是默认的。

刷新-到期¶ T0>

refresh-expire是一个不常见的选项,表明Session.expire()操作应该从父级传播到引用的对象。当使用Session.refresh()时,被引用的对象只会过期,但不会实际刷新。

抹去¶ T0>

expunge cascade indicates that when the parent object is removed from the Session using Session.expunge(), the operation should be propagated down to referred objects.

控制Backrefs上的级联

缺省情况下,save-update级联发生在从backrefs发出的属性更改事件上。这可能是一个令人困惑的陈述,通过示范更容易描述;这意味着,给定一个这样的映射:

mapper(Order, order_table, properties={
    'items' : relationship(Item, backref='order')
})

If an Order is already in the session, and is assigned to the order attribute of an Item, the backref appends the Item to the items collection of that Order, resulting in the save-update cascade taking place:

>>> o1 = Order()
>>> session.add(o1)
>>> o1 in session
True

>>> i1 = Item()
>>> i1.order = o1
>>> i1 in o1.items
True
>>> i1 in session
True

使用cascade_backrefs标志可禁用此行为:

mapper(Order, order_table, properties={
    'items' : relationship(Item, backref='order',
                                cascade_backrefs=False)
})

如上所述,i1.order = o1的分配将会附加i1 o1items集合,但不会将i1添加到会话中。You can, of course, add() i1 to the session at a later point. 此选项可能有助于在对象需要在会话结束之前保持在会话之外的情况,但仍需要将对象关联到目标会话中已经存在的对象。