考虑以下架构
class Parent(Base):
__tablename__ = "parents"
id = Column(Integer, primary_key=True)
... # A lot of other attributes and relationships
class Child(Base):
__tablename__ = "childs"
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey("parents.id"), nullable=False)
parent = relationship(Parent)
... # A lot of other attributes and relationships
(我使用的是Sqlalchemy 1.4)
假设我有一个来自上一个会话的
Parent
对象
session.expire_on_commit=False
parent = session.query(Parent).filter(Parent.id == 1)
我尝试根据它插入一个新的
Child
实例:
new_child = Child(parent=parent)
session.add(new_child)
session.commit()
这可以工作,但它不是线程安全的,就像我试图针对同一父级并行创建很多
Child
实例一样,我收到与此类似的错误。
sqlalchemy.exc.InvalidRequestError: Object '<Parent at 0x7fdcf96bdc10>' is already attached to session '51' (this is '52')
(错误要么来自父类,要么来自与之相关的另一个类)。
我也不能只用
Child
键实例化 parent_id
,因为应用程序的很多行为都是基于父级的属性及其关系。
天真地,我以为我可以做类似的事情
# Run the application, and when needing to insert the Child, with a new session do:
child.parent_id = child.parent.id
child.parent = None
当我跑步时,虽然表示看起来不错
<Child id=None, parent_id=1, ...>
,但事件
session.add(child)
session.commit()
我收到错误 ```{'S': 'ERROR', 'V': 'ERROR', 'C': '23502', 'M': '关系“childs”的“parent_id”列中的空值违反非空约束...'`
确实,在提交期间的某个地方,我的
parent_id
已被替换,我的 child
现在看起来像这样 <Child id=None, parent_id=None, ...>
。
我已经尝试过
session.merge
中描述的方法这个答案,但由于我的现实世界示例处理更多的关系和属性,它似乎试图创建重复的值。
我在寻找答案:
parent
关系设置为 None
,同时允许在会话提交期间保留 parent_id
,这将是完美的解决方案GrandChild
类,与 child
有关系,并且将与 child
实例同时插入我目前能想到的唯一解决方案是创建一个新的
Child
实例而不指定
new_child_to_insert = Child(parent_id=new_child.parent_id, ...) # Use a lot of other attributes
session.add(new_child_to_insert)
但是,这个解决方案对于我的实际使用来说非常麻烦,因为我有很多不同的 在
Child
类中管理的属性和关系