(Flask)带有回滚的SqlAlchemy after_insert事件监听器导致TransactionClosed错误

时间:2013-11-24 11:44:51

标签: python sqlalchemy flask-sqlalchemy

我有两张表AB。 在表B中创建新行后,我需要在表ForeignKey(带A引用)中创建一些行。但是如果我在事件监听器中遇到异常,我需要在监听器内对已执行的代码进行回滚。我该怎么办?

例如:

class A(db.Model):
    __tablename__ = 'a'

    id = Column(BigInteger, primary_key=True)
    ...

class B(db.Model):
    __tablename__ = 'b'

    id = Column(BigInteger, primary_key=True)
    a_id = Column(BigInteger, ForeignKey('a.id'), nullable=False, index=True)
    ...

@event.listens_for(A, 'after_insert')
def after_insert_listener(mapper, connection, target):
    transaction = connection.begin()
    try:
        ...
        connection.execute(B.__table__.insert(), a_id=target.id, ...)
        transaction.commit()
        ...
    except Exception as e:
        # need rollback here if i got exception inside my code
        transaction.rollback()

接下来,在我的代码中,我有类似的东西:

a = A(some_field='foo')
db.session.add(a)
db.session.commit()

此代码将我引导至ResourceClosedError: This Connection is closed(这是我在侦听器中关闭conn时)或InvalidRequestError: This transaction is inactive

请帮帮我,我做错了什么?我认为使用这段代码,我将获得嵌套事务,但据我所知,只有一个事务从session.commit()执行到侦听器结束。

有关after_insert侦听器的一些信息: http://docs.sqlalchemy.org/en/latest/orm/events.html#sqlalchemy.orm.events.MapperEvents.after_insert

如果它可以有所作为,我使用Flask-SqlAlchemy扩展名。

1 个答案:

答案 0 :(得分:0)

除非你在after_insert_listener中提供的示例代码不好,否则你的方法是错误的:而是使用一个监听器来插入B个实例,只需让sqlalchemy通过定义来处理这个问题。 AB之间的relationship

class A(db.Model):
    __tablename__ = 'a'
    id = Column(BigInteger, primary_key=True)
    ...
    b_s = relationship("b", backref="a") # @note: new code

...
a = A(some_field='foo')
b1 = B(some_b_field='some value')
a.b_s.append(b1)
db.session.add(a)
#db.session.add(b1) # @note: no need for this, as b1 will be added 
                    # automatically due to relationship with "a" added above
db.session.commit() # @note: this will insert A and then all Bs 
                    # related to it with correct `a_id`