【问题标题】:SQLALchemy "after_insert" doesn't update target object fieldsSQLALchemy“after_insert”不更新目标对象字段
【发布时间】:2014-11-05 16:35:01
【问题描述】:

我有一个模型(见下面的代码),我想在插入一个对象后执行一个函数,该函数将更新对象的一个​​字段。我正在使用 after_insert 映射器事件来执行此操作。

我已经确认 after_insert 正确调用了 event_extract_audio_text() 处理程序,并且 target 正在更新为正确的 audio_text 值。但是,一旦事件处理程序完成执行,就不会为数据库中的对象设置文本值。

代码

# Event handler
def event_extract_audio_text(mapper, connect, target):

    # Extract text from audio file
    audio_text = compute_text_from_audio_file(target.filename)

    # Update the 'text' field with extracted text
    target.audio_text = audio_text

# Model
class SoundsRaw(db.Model):
    __tablename__ = 'soundsraw'

    id = db.Column(db.BigInteger(), primary_key=True, autoincrement=True)
    filename = db.Column(db.String(255))
    audio_text = db.Column(db.Text())

# Event listener
event.listen(SoundsRaw, 'after_insert', event_extract_audio_text)

我也尝试调用 db.session.commit() 以尝试使用文本值更新对象,但随后我得到以下堆栈跟踪:

File "/Users/alexmarse/.virtualenvs/techmuseum/lib/python2.7/site-packages/sqlalchemy/orm/session.py", line 219, in _assert_active
raise sa_exc.ResourceClosedError(closed_msg)

ResourceClosedError: This transaction is closed

有什么想法吗?

软件版本

SQLAlchemy 0.9.4 烧瓶 0.10.1 Flask-SQLAlchemy 1.0

【问题讨论】:

  • 嘿@alex-marse,你解决了吗?有同样的问题..干杯
  • 嘿@ducu,看看我刚刚发布的答案。我认为解决 ResourceClosedError 的是第二个代码块中的 create_db_session() 行,所以也许你可以在 Mapper Event 代码中使用它,看看它是如何工作的。我很好奇。
  • 感谢您的回复,我在其他地方发现了类似的问题(不记得了),我将在下面发布解决方案,它对我有用,我会说它比您的解决方案更清洁下面

标签: postgresql python-2.7 flask sqlalchemy flask-sqlalchemy


【解决方案1】:

我最终通过放弃 Mapper Event 方法并改用 Flask 的 Signalling Support 来解决这个问题。

基本上,您可以在模型上注册“信号”,它们本质上是回调函数,每当发生特定类型的事件时都会调用它们。就我而言,该事件是我模型的“更新”。

为了配置信号,我将此方法添加到我的app.py 文件中:

def on_models_committed(sender, changes):
    """Handler for model change signals"""
    for model, change in changes:
        if change == 'insert' and hasattr(model, '__commit_insert__'):
            model.__commit_insert__()
        if change == 'update' and hasattr(model, '__commit_update__'):
            model.__commit_update__()
        if change == 'delete' and hasattr(model, '__commit_delete__'):
            model.__commit_delete__()

然后,在我的模型上,我添加了这个函数来处理更新事件:

 # Event methods
def __commit_update__(self):

    # create a new db session, which avoids the ResourceClosedError
    session = create_db_session()

    from techmuseum.modules.sensors.models import SoundsRaw
    # Get the SoundsRaw record by uuid (self contains the object being updated, 
    # but we can't just update/commit self -- we'd get a ResourceClosedError)
    sound = session.query(SoundsRaw).filter_by(uuid=self.uuid).first()

    # Extract text from audio file
    audio_text = compute_text_from_audio_file(sound)

    # Update the 'text' field of the sound
    sound.text = audio_text

    # Commit the update to the sound
    session.add(sound)
    session.commit()

def create_db_session():
    # create a new Session
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker

    psql_url = app.config['SQLALCHEMY_DATABASE_URI']
    some_engine = create_engine(psql_url)
  
    # create a configured "Session" class
    session = sessionmaker(bind=some_engine)

    return session

【讨论】:

    【解决方案2】:

    'after_insert' 类型的处理程序是直接使用连接。我是这样做的:

    class Link(db.Model):
        "News link data."
    
        __tablename__ = 'news_links'
    
        id = db.Column(db.BigInteger, primary_key=True)
        slug = db.Column(db.String, unique=True) #, nullable=False 
    
        url = db.Column(db.String, nullable=False, unique=True)
        title = db.Column(db.String)
        image_url = db.Column(db.String)
        description = db.Column(db.String)
    
    @db.event.listens_for(Link, "after_insert")
    def after_insert(mapper, connection, target):
        link_table = Link.__table__
        if target.slug is None:
            connection.execute(
                link_table.update().
                where(link_table.c.id==target.id).
                values(slug=slugify(target.id))
            )
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-10-28
    • 2021-05-22
    • 2022-11-01
    • 2014-07-24
    • 2016-05-10
    • 2022-08-24
    • 2016-03-21
    • 2019-06-28
    相关资源
    最近更新 更多