SQLAlchemy错误附加到关系

时间:2014-05-08 16:37:57

标签: python sqlalchemy

我一直在使用带有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就没有孩子了我认为这与正在发生的事情有关,我只是不明白为什么会发生这种情况

任何帮助都会非常感激,因为我根本不明白这里发生了什么。如果您需要我发布更多信息,请告诉我们。提前谢谢。

1 个答案:

答案 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的人来纠正我,但据我所知,这是我最好的解释:)