SQLAlchemy映射器事件未触发。

huangapple go评论70阅读模式
英文:

SQLAlchemy Mapper Events don't get triggered

问题

我无法让Mapper Events在SqlAlchemy 2.0中触发

简而言之: 我能够执行操作,但事件从未触发。

以下是它们在文档中的示例:

def my_before_insert_listener(mapper, connection, target):
    # 在插入时执行存储过程,将值应用于要插入的行
    target.calculated_value = connection.execute(
        text("select my_special_function(%d)" % target.special_number)
    ).scalar()

# 将监听函数与SomeClass关联,以在“before_insert”挂钩期间执行
event.listen(
    SomeClass, 'before_insert', my_before_insert_listener)

这里是我尝试基本相同的事情的示例:

# models.py

from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, String, DateTime, func, event, MetaData

metadata_obj = MetaData()
Base = declarative_base(metadata=metadata_obj)

class Opportunity(Base):
    __tablename__ = "opportunity"
    id = Column(String, primary_key=True, default=generate)
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
    name = Column(String, nullable=False)

opportunity_table = Opportunity.__table__

我还有以下监听器:

# models.py

def opportunity_after_update(mapper, connection, target):
    print(f"Opportunity {target.id} was updated!")
event.listen(Opportunity, 'after_update', opportunity_after_update)

以下代码成功更新了我的数据库中的表,但监听器从未被调用:

with Session() as session:
    session.query(Opportunity).filter_by(id=some_id).update({'name':'New Name'})
    session.commit()

有什么可能有帮助的线索吗?

英文:

I can't get Mapper Events to trigger in SqlAlchemy 2.0

tl;dr: I am able to perform operations but events never trigger.

Below is the example they have in their docs:

def my_before_insert_listener(mapper, connection, target):
    # execute a stored procedure upon INSERT,
    # apply the value to the row to be inserted
    target.calculated_value = connection.execute(
        text("select my_special_function(%d)" % target.special_number)
    ).scalar()

# associate the listener function with SomeClass,
# to execute during the "before_insert" hook
event.listen(
    SomeClass, 'before_insert', my_before_insert_listener)

And here, is my attempt to implement basically the same thing:

# models.py

from sqlalchemy.orm import declarative_base
from sqlalchemy import Column, String, DateTime, func, event, MetaData

metadata_obj = MetaData()
Base = declarative_base(metadata=metadata_obj)

class Opportunity(Base):
    __tablename__ = "opportunity"
    id = Column(String, primary_key=True, default=generate)
    created_at = Column(DateTime, server_default=func.now())
    updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
    name = Column(String, nullable=False)

opportunity_table = Opportunity.__table__

I also have the following listener

#models.py 

def opportunity_after_update(mapper, connection, target):
    print(f"Opportunity {target.id} was updated!")
event.listen(Opportunity, 'after_update', opportunity_after_update)

The following code successfully updates the table in my database, but the listener never gets called:

with Session() as session:
    session.query(Opportunity).filter_by(id=some_id).update({'name'='New Name'})
    session.commit()

Any clues that may be helpful?

答案1

得分: 1

如果您想要修改现有记录,至少有两种方法可以采用。

首先,您可以检索记录并在会话中进行修改:

with Session() as s:
    opportunity = s.get(Opportunity, some_id)
    opportunity.name = 'new_name'
    s.commit()

在这种情况下,因为涉及的对象保证在会话中,映射器事件侦听器将捕获任何更改并报告它们(或执行您定义的其他操作)。

其次,您可以使用“带有自定义 WHERE 子句的更新方法”:

with Session() as s:
    s.query(Opportunity).filter_by(id=some_id).update({'name': 'new_name'})
    s.commit()

在这种情况下,会话不一定包含对已更新行的引用,因此映射器事件侦听器不会收到通知。

捕获此类事件的推荐解决方案是使用 do_orm_execute 侦听器。此类侦听器将拦截:

从 Session.execute() 方法以及相关方法(如 Session.scalars() 和 Session.scalar())调用的所有顶层 SQL 语句

(引用自前面的链接;在这里,可以将 session.querysession.execute 视为等效)

您选择哪种更新方法 - 因此可以使用哪种侦听器 - 取决于您的用例。第一种方法最常见,但第二种方法在您想要更新任意大量的行时非常有用,例如“所有用户表中名字列等于 'John' 的行”。

英文:

if you want to modify an existing record, there are (at least) two approaches that you could take.

Firstly, you could retrieve the record an modify it in the session.

with Session() as s:
    opportunity = s.get(Opportunity, some_id)
    opportunity.name = 'new_name'
    s.commit()

In this case, because the object involved is guaranteed to be in the session, mapper event listeners will pick up any changes and report them (or perform some other action that you define).

Secondly, you can use the "update with custom where clause method":

with Session() as s:
    s.query(Opportunity).filter_by(id=some_id).update({'name': 'new_name'})
    s.commit()

In this case, the session will not necessarily contain references to the updated rows, so mapper event listeners will not be notified of them.

The recommended solution for capturing such events is to use a do_orm_execute listener. Such a listener will intercept:

> all top-level SQL statements invoked from the Session.execute() method, as well as related methods such as Session.scalars() and Session.scalar()

(quotation taken from the previous link; session.query and session.execute can be considered to be equivalent here)

Which update method you choose - and consequently which listeners you can use - depends on your use case. The first method is most common, but the second method is useful if you want to update an arbitrarily large collection of rows, for example "all the rows in the users table whose name column is equal to 'John'".

huangapple
  • 本文由 发表于 2023年3月4日 07:39:07
  • 转载请务必保留本文链接:https://go.coder-hub.com/75632726.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定