我正在尝试将行从一个db示例复制到另一个db示例,在不同的环境中使用相同的模式。此架构中的两个表的链接方式使它们产生相互依赖的行。插入这些行时 post_update
之后按预期运行,但是update语句将id字段的值设置为 None
而不是预期的id。
这仅在使用已从会话中删除的对象时发生。使用新创建的对象时 post_update
行为完全符合预期。
示例
我的关系是这样的:
class Category(Base):
__tablename__ = 'categories'
id = Column(Integer, primary_key=True)
top_product_id = Column(Integer, ForeignKey('products.id'))
products = relationship('Product', primaryjoin='Product.category_id == Category.id', back_populates='category', cascade='all', lazy='selectin')
top_product = relationship('Product', primaryjoin='Category.top_product_id == Product.id', post_update=True, cascade='all', lazy='selectin')
class Product(Base):
__tablename__ = 'products'
id = Column(Integer, primary_key=True)
category_id = Column(Integer, ForeignKey('categories.id'))
category = relationship('Category', primaryjoin='Product.category_id == Category.id', back_populates='products', cascade='all', lazy='selectin')
如果我从一个数据库中查询一个类别及其相关产品并尝试将它们写入另一个数据库,则 top_product_id
行为不符合预期,并将值设置为 None
相反。以下代码:
category = source_session.query(Category).filter(Category.id == 99).one()
source_session.expunge(category)
make_transient(category)
for products in category.products:
make_transient(product)
# this step is necessary to prevent a foreign key error on the initial category insert
category.top_product_id = None
dest_session.add(category)
导致sqlalchemy生成以下sql:
INSERT INTO categories (name, top_product_id) VALUES (%s, %s)
('SomeCategoryName', None)
INSERT INTO products (name, category_id) VALUES (%s, %s)
('SomeProductName', 99)
UPDATE categories SET top_product_id=%s WHERE categories.id = %s
(None, 99)
但是如果我使用新创建的对象,一切都会正常工作。
category = Category()
product = Product()
category.name = 'SomeCategoryName'
product.name = 'SomeProductName'
product.category = category
category.top_product = product
dest_session.add(category)
结果:
INSERT INTO categories (name, top_product_id) VALUES (%s, %s)
('SomeCategoryName', None)
INSERT INTO products (name, category_id) VALUES (%s, %s)
('SomeProductName', 99)
UPDATE categories SET top_product_id=%s WHERE categories.id = %s
(1, 99)
除了这个区别之外,这两个动作之间的行为都是一样的。所有其他关系都已正确创建,ID和外键按预期设置。只有 top_product_id
在由 post_update
不按预期行事。
作为额外的故障排除步骤,我尝试了:
创建新对象
将它们添加到会话中
将会话刷新到数据库
从会话中删除对象
取消设置对象上的外键id字段(以避免初始插入错误)并使对象成为临时对象
将对象重新添加到会话
重新冲洗至db
在第一次刷新db时 top_product_id
设置正确。在第二个,设定为 None
. 因此,这证实了问题不在于会话中的差异,而是与从会话中删除对象并使其成为临时对象有关。在“删除/生成” transient 过程中,一定有什么事情发生了/没有发生,使这些对象处于根本不同的状态,从而阻止了 post_update
不要表现出应有的行为。
如果您有任何想法,我们将不胜感激。
1条答案
按热度按时间ncgqoxb01#
我想你的
Base
班级混音name
列?你的目标是
inspect(category).committed_state
看起来就像新创建的对象一样(除了id
属性)。每个产品对象都一样。在“新建对象”示例中,
category
的committed_state
在刷新会话之前如下所示:而
product
的committed_state
看起来像这样:要获得更新后的行为,需要两者都过期
category.top_product_id
(以防止其被纳入INSERT
)还有软糖category.top_product
的committed_state
(为了使sqlalchemy相信值已经改变,因此需要引起UPDATE
).首先,过期
category.top_product_id
制作前category
transient :然后是软糖
category.top_product
的committed_state
(这可能发生在category
transient ):完整示例:
产生了这个dml
dest_session
:好像
make_transient
应该重置committed_state
好像它是一个新的物体,但我猜不是。