使用 flask-sqlalchemy 如何级联创建对象?

问题描述

比如我想在 User 对象成功 session.commit() 之后创建一个 Wallet 对象,Wallet 对象创建成功之后可能又需要创建另一个对象。用函数把创建多个对象的代码封装起来这种方式我不想用,因为 User 对象可能不是在代码中显式创建的,有时是通过 load fixtures 的方式(我搞了个类似 django 的 load fixtures 功能)创建的。

尝试过的方法

我试着利用 flask-sqlalchemy 的 models_committed 信号来做触发,比如这样:

    @models_committed.connect_via(app)
    def on_models_committed(sender, changes):
        for obj, change in changes:
            if change == 'insert' and hasattr(obj, '__commit_insert__'):
                obj.__commit_insert__()
            elif change == 'update' and hasattr(obj, '__commit_update__'):
                obj.__commit_update__()
            elif change == 'delete' and hasattr(obj, '__commit_delete__'):
                obj.__commit_delete__()

然后在 User model 中添加 commit_insert 方法,在该方法中创建 Wallet 对象,比如这样:

def __commit_insert__(self):
    wallet = Wallet(name="my_wallet")
    db.session.add(wallet)
    db.session.commit()

实际看到的错误信息又是什么?

然而,在创建 Wallet 对象 commit 的时候,报了这么个错误:

This session is in 'committed' state; no further SQL can be emitted within this transaction.

讲道理,临时搞搞 flask 还没倒出功夫缕源码看看 flask-sqlalchemy 怎么个逻辑,所以先问一下大家,看看有没有遇到过这种情况,类似的场景在 flask 中是否有什么通用的处理方式?

另外,
我是有去 stack-overflow 上先看一眼的,结果这个答案讲道理挺尴尬的.

阅读 3.5k
2 个回答
✓ 已被采纳新手上路,请多包涵

这个问题可以通过直接执行 sql 语句来搞

import sqlalchemy
from sqlalchemy import event
from models import Wallet

class User(db.Model):
  name = sqlalchemy.column(s.String)

  @staticmethod
  def after_create(mapper, connection, target):
    wallet_table = Wallet.__table__
    connection.execute(
        wallet_table.insert().values(name='my wallet')
        )
    
event.listen(User, 'after_insert', User.after_create)

参考了 @低默 给的答案,虽然也会报错,但他的这种 sqlalchemy event 写法很方便获取到 connection 对象,而我原问题中写的 Flask Signaling 方式中如果想获得 connection 需要这样写:

with db.engine.connect() as connection:
    ...

另外还有个问题需要注意就是如果需要执行多个 sql 语句,注意使用事务,例如:

trans = connection.begin()
try:
    ...
except exc.SQLAlchemyError:
    trans.rollback()
        raise
    else:
        trans.commit()

参考链接

试一下SQLAlchemy的event吧(http://docs.sqlalchemy.org/en...
一个简单的使用例子

import sqlalchemy
from sqlalchemy import event
from models import Wallet

class User(db.Model):
  name = sqlalchemy.column(s.String)

  @staticmethod
  def after_create(mapper, connection, target):
    wallet = Wallet()
    db.session.add(wallet)
    db.session.commit()
    
event.listen(User, 'after_insert', User.after_create)
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题