我一直在使用带有Python版本2.7.3的SQLAlchemy 0.9.2,并且遇到了一些我似乎无法解释的奇怪问题。这是我的相关代码:
Base = declarative_base()
class Parent(Base):
__tablename__ = 'parents'
__table_args__ = (UniqueConstraint('first_name', 'last_name', name='_name_constraint'),)
id = Column(Integer, primary_key=True)
first_name = Column(String(32), nullable=False)
last_name = Column(String(32), nullable=False)
children = relationship(Child, cascade='all,delete', backref='parent')
## Constructors and other methods ##
class Child(Base):
__tablename__ = 'children'
id = Column(Integer, primary_key=True)
parent_id = Column(Integer, ForeignKey('parents.id'))
foo = Column(String(32), nullable=False)
## Constructors and other methods ##
这是一套非常基本的模型。我遇到的问题是我想将一个子项添加到保存到数据库的父项。踢球者是孩子当前与数据库中不的父母相关。请考虑以下示例:
database_engine = create_engine("mysql://user:password@localhost/db", echo=False)
session = scoped_session(sessionmaker(autoflush=True,autocommit=False))
p1 = Parent("Foo", "Bar") # Create a parent and append a child
c1 = Child("foo")
p1.children.append(c1)
session.add(p1)
session.commit() # This works without a problem
db_parent = session.query(Parent).first()
db_parent.children.append(Child("bar"))
session.commit() # This also works without a problem
p2 = Parent("Foo", "Bar")
c3 = Child("baz")
p2.children.append(c3)
db_parent = session.query(Parent).first()
db_parent.children.append(p2.children[0])
session.commit() # ERROR: This blows up
我收到的错误是我打破了完整性约束,即'_name_constraint'。 SQLAlchemy告诉我正在尝试插入具有相同信息的Parent。我的问题是,为什么世界上它试图增加一个次要的父母?
这些是我到目前为止所采取的步骤,但没有一个好的答案:
db_parent.children[2]
一旦我将其添加到列表中,它指向与p1
相同的内存地址p2.children
。奇怪的是,p2
一旦我把孩子附加到db_parent
就没有孩子了我认为这与正在发生的事情有关,我只是不明白为什么会发生这种情况任何帮助都会非常感激,因为我根本不明白这里发生了什么。如果您需要我发布更多信息,请告诉我们。提前谢谢。
答案 0 :(得分:1)
好的,在经过一些挖掘之后,我想我找到了解决问题的方法,但我还没有得到答案为什么它的发生方式,但我想我可能有猜测。我发现的解决方案是在session.expunge(p2)
session.commit()
我开始探索SQLAlchemy's Internals,尤其是实例状态。我发现,一旦将子项添加到父项,原始父项的状态就会变为待处理状态。这是一个例子:
from sqlalchemy import inspect
p2 = Parent("Foo", "Bar")
p2_inst = inspect(p2)
c3 = Child("Baz")
c3_inst = inspect(c3)
db_parent = session.query(Parent).first()
db_parent_inst = inspect(db_parent)
print("Pending State before append:")
print("p2_inst : {}".format(p2_inst.pending))
print("c3_inst : {}".format(c3_inst.pending))
print("db_parent_inst : {}".format(db_parent_inst.pending))
db_parent.children.append(p2.children[0])
print("Pending State after append:")
print("p2_inst : {}".format(p2_inst.pending))
print("c3_inst : {}".format(c3_inst.pending))
print("db_parent_inst : {}".format(db_parent_inst.pending))
session.expunge(p2)
print("Pending State after expunge:")
print("p2_inst : {}".format(p2_inst.pending))
print("c3_inst : {}".format(c3_inst.pending))
print("db_parent_inst : {}".format(db_parent_inst.pending))
session.commit()
运行它的结果将是:
Pending State before append:
p2_inst : False
c3_inst : False
db_parent_inst : False
Pending State after append:
p2_inst : True
c3_inst : True
db_parent_inst : False
Pending State after expunge:
p2_inst : False
c3_inst : True
db_parent_inst : False
你有它。一旦我想到它,我想它是有道理的。 db_parent没有理由进入“挂起”状态,因为你实际上并没有对MySQL中的记录做任何事情。我猜测p2
为什么会挂起的原因是由于操作的顺序?为了使c3
成为待定状态,它的所有关系必须存在(包括p2
),因此即使更改子项的父项,会话仍然认为它需要添加父项。
我喜欢那些更了解SQLAlchemy的人来纠正我,但据我所知,这是我最好的解释:)