添加关联两个现有对象的对象

Posted

技术标签:

【中文标题】添加关联两个现有对象的对象【英文标题】:Add an object associating two existing objects 【发布时间】:2012-01-09 18:12:33 【问题描述】:

我有两个表 testInstance 和 bugzilla 由第三个表 bzCheck 关联,如下所示:

class Instance(Base):
    __tablename__ = "testInstance"

    id = Column(Integer, primary_key=True)

    bz_checks = relation(BZCheck, backref="instance")

class BZCheck(Base):
    __tablename__ = "bzCheck"

    instance_id = Column(Integer, ForeignKey("testInstance.id"), primary_key=True)
    bz_id = Column(Integer, ForeignKey("bugzilla.id"), primary_key=True)
    status = Column(String, nullable=False)

    bug = relation(Bugzilla, backref="checks")

class Bugzilla(Base):
    __tablename__ = "bugzilla"

    id = Column(Integer, primary_key=True)

后端是一个postgresql服务器;我正在使用 SQLalchemy 0.5

如果我创建了 Instance、Bugzilla 和 BZCheck 对象,那么就这样做

bzcheck.bug = bugzilla
instance.bz_checks.append(bzcheck)

然后添加并提交它们;一切都很好。

但是现在,让我们假设我有一个现有的实例和一个现有的 bugzilla,并希望将它们关联起来:

instance = session.query(Instance).filter(Instance.id == 31).one()
bugzilla = session.query(Bugzilla).filter(Bugzilla.id == 19876).one()
check = BZCheck(status="OK")
check.bug = bugzilla
instance.bz_checks.append(check)

失败了:

In [6]: instance.bz_checks.append(check)
2012-01-09 18:43:50,713 INFO sqlalchemy.engine.base.Engine.0x...3bd0 select nextval('"bzCheck_instance_id_seq"')
2012-01-09 18:43:50,713 INFO sqlalchemy.engine.base.Engine.0x...3bd0 None
2012-01-09 18:43:50,713 INFO sqlalchemy.engine.base.Engine.0x...3bd0 ROLLBACK

它尝试从不存在的序列中获取新 ID,而不是使用外键“testInstance.id”...我不明白为什么。 在提交对象后尝试修改对象时,我遇到了类似的问题;我应该错过一些基本的东西,但是什么?

【问题讨论】:

你为什么不使用最新版本,.7? 因为最终使用它的机器运行的发行版只有 0.5.8,我想避免手动安装 【参考方案1】:

您在这里缺少的部分是堆栈跟踪。始终查看堆栈跟踪 - 这里的关键是它是自动刷新的,由instance.bz_checks 的访问产生:

Traceback (most recent call last):
  File "test.py", line 44, in <module>
    instance.bz_checks.append(check)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 168, in __get__
    return self.impl.get(instance_state(instance),dict_)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/attributes.py", line 453, in get
    value = self.callable_(state, passive)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/strategies.py", line 563, in _load_for_state
    result = q.all()
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/query.py", line 1983, in all
    return list(self)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/query.py", line 2092, in __iter__
    self.session._autoflush()
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/session.py", line 973, in _autoflush
    self.flush()
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/session.py", line 1547, in flush
    self._flush(objects)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/session.py", line 1616, in _flush
    flush_context.execute()
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/unitofwork.py", line 328, in execute
    rec.execute(self)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/unitofwork.py", line 472, in execute
    uow
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/mapper.py", line 2291, in _save_obj
    execute(statement, params)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/engine/base.py", line 1405, in execute
    params)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/engine/base.py", line 1538, in _execute_clauseelement
    compiled_sql, distilled_params
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/engine/base.py", line 1646, in _execute_context
    context)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/engine/base.py", line 1639, in _execute_context
    context)
  File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/engine/default.py", line 330, in do_execute
    cursor.execute(statement, parameters)
sqlalchemy.exc.IntegrityError: (IntegrityError) null value in column "instance_id" violates not-null constraint
 'INSERT INTO "bzCheck" (bz_id, status) VALUES (%(bz_id)s, %(status)s) RETURNING "bzCheck".instance_id' 'status': 'OK', 'bz_id': 19876

你可以看到这个,因为这行代码是:

instance.bz_checks.append(check)

然后自动刷新:

self.session._autoflush()
File "/Users/classic/dev/sqlalchemy/lib/sqlalchemy/orm/session.py", line 973, in _autoflush

三种解决方案:

一个。暂时禁用自动刷新(参见http://www.sqlalchemy.org/trac/wiki/UsageRecipes/DisableAutoflush)

b.确保 BZCheck 关联对象始终以在访问任何集合之前所需的完整状态创建

BZState(bug=bugzilla, instance=instance)

(这对于关联对象来说通常是个好主意——它们代表了两点之间的关联,因此最适合用这种状态实例化它们)

c.更改级联规则,以便check.bug = somebug 的操作实际上还没有将check 放入会话中。您可以使用cascade_backrefs 执行此操作,如http://www.sqlalchemy.org/docs/orm/session.html#controlling-cascade-on-backrefs 所述。 (但您需要使用 0.6 或 0.7)

【讨论】:

禁用自动刷新有效,非常感谢。稍后我会记住解决方案 B。

以上是关于添加关联两个现有对象的对象的主要内容,如果未能解决你的问题,请参考以下文章

关联对象实现原理

关联对象实现原理

关联对象实现原理

Rails 3.2 - 在现有表单上添加新关联

使用 sqlalchemy 将 onupdate="cascade" 添加到 postgres 中的现有列

使用Javascript将对象添加到现有对象[重复]