我正在使用 SQLAlchemy 版本 0.8.2(尝试过 python 2.7.5 和 3.3.2)
我不得不在我的代码中使用关联对象模式(用于多对多关系),但是每当我添加关联时,它都会引发 IntegrityError 异常。这是因为它没有执行“INSERT INTO association (left_id, right_id, extra_data) [...]”,而是执行“INSERT INTO association (right_id, extra_data) [...]”,这将引发 IntegrityError异常,因为它缺少主键。
在尝试缩小问题范围并尽可能简化代码后,我找到了罪魁祸首,但我不明白为什么它会这样。
我包含了我的完整代码,以便读者可以按原样进行测试。类声明与文档中的完全相同(带有 backrefs)。
#!/usr/bin/env python2
import sqlalchemy
from sqlalchemy.orm import sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy import ForeignKey
from sqlalchemy.orm import relationship, backref
Base = declarative_base()
class Association(Base):
__tablename__ = 'association'
left_id = Column(Integer, ForeignKey('left.id'), primary_key=True)
right_id = Column(Integer, ForeignKey('right.id'), primary_key=True)
extra_data = Column(String(50))
child = relationship("Child", backref="parent_assocs")
class Parent(Base):
__tablename__ = 'left'
id = Column(Integer, primary_key=True)
children = relationship("Association", backref="parent")
class Child(Base):
__tablename__ = 'right'
id = Column(Integer, primary_key=True)
def main():
engine = sqlalchemy.create_engine('sqlite:///:memory:', echo=True)
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)
session = Session()
# populate old data
session.add(Child())
# new data
p = Parent()
session.add(p) # Commenting this fixes the error.
session.flush()
# rest of new data
a = Association(extra_data="some data")
a.child = session.query(Child).one()
# a.child = Child() # Using this instead of the above line avoids the error - but that's not what I want.
p.children.append(a)
# a.parent = p # Using this instead of the above line fixes the error! They're logically equivalent.
session.add(p)
session.commit()
if __name__ == '__main__':
main()
因此,正如上面代码中的注释所述,有三种方法可以解决/避免该问题。
- 在声明关联之前不要将父级添加到会话中
- 为关联创建一个新的孩子,而不是选择一个已经存在的孩子。
- 在关联上使用 backref
我不了解所有三种情况的行为。
第二种情况做了一些不同的事情,所以这不是一个可能的解决方案。但是,我不理解这种行为,并且希望能解释为什么在这种情况下可以避免该问题。
我认为第一种情况可能与“对象状态”有关,但我也不知道究竟是什么原因造成的。哦,session.autoflush=False
在第一次出现之前添加session.add(p)
也解决了增加我困惑的问题。
对于第三种情况,我完全空白,因为它们在逻辑上应该是等价的。
感谢您的任何见解!