2012-01-09 53 views
2

我有两个表,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

如果我创建实例,Bugzilla的和BZCheck ojects,然后做

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”...我不明白为什么。 在提交对象后尝试修改对象时,我遇到过类似的问题;我应该错过一些基本的东西,但是什么?

+0

为什么你不使用最新版本,.7? – 2012-01-09 18:43:21

+0

,因为最终使用它的机器运行的发行版只有0.5.8,我想避免手动安装 – deubeuliou 2012-01-10 21:43:35

回答

2

你在这里丢失的部分是堆栈跟踪。总是看堆栈跟踪 - 什么是关键这里是它的自动冲洗,通过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 

三种解决方案:

a。暂时禁用自动刷新功能(请参阅http://www.sqlalchemy.org/trac/wiki/UsageRecipes/DisableAutoflush

b。确保总是与它创建的BZCheck关联对象的访问任何收集前需要满状态:

BZState(错误=的Bugzilla,实例=实例)

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

c。更改级联规则,以便check.bug = somebug的操作实际上不会将check放置到刚才的会话中。您可以使用cascade_backrefs来完成此操作,如http://www.sqlalchemy.org/docs/orm/session.html#controlling-cascade-on-backrefs所述。 (但你需要在0.6或0.7)

+0

禁用autoflush工作,非常感谢。以后我会记住解决方案B. – deubeuliou 2012-01-10 08:01:11