事件


SQLAlchemy 包括一个事件 API,它将各种钩子发布到 SQLAlchemy Core 和 ORM 的内部。


活动注册


订阅事件通过单个 API 点、listen() 函数或 listens_for() 装饰器进行。这些函数接受目标、标识要拦截的事件的字符串标识符和用户定义的侦听函数。特定类型的事件可能支持这两个函数的其他位置和关键字参数,这些事件可以为给定事件函数指定备用接口,或根据给定目标提供有关辅助事件目标的指令。


事件的名称和相应侦听器函数的参数签名派生自类绑定规范方法,该方法与文档中描述的标记类绑定存在。例如,PoolEvents.connect() 的文档指示事件名称为 “connect” 并且用户定义的侦听器函数应接收两个位置参数:

from sqlalchemy.event import listen
from sqlalchemy.pool import Pool


def my_on_connect(dbapi_con, connection_record):
    print("New DBAPI connection:", dbapi_con)


listen(Pool, "connect", my_on_connect)


使用 listens_for() 装饰器进行监听如下所示:

from sqlalchemy.event import listens_for
from sqlalchemy.pool import Pool


@listens_for(Pool, "connect")
def my_on_connect(dbapi_con, connection_record):
    print("New DBAPI connection:", dbapi_con)


命名参数样式


侦听器函数可以接受一些参数样式。以 PoolEvents.connect() 为例,此函数记录为接收 dbapi_connectionconnection_record 参数。我们可以选择按名称接收这些参数,方法是建立一个接受 **keyword 参数的侦听器函数,通过将 named=True 传递给 listen()listens_for() 的调用

from sqlalchemy.event import listens_for
from sqlalchemy.pool import Pool


@listens_for(Pool, "connect", named=True)
def my_on_connect(**kw):
    print("New DBAPI connection:", kw["dbapi_connection"])


使用命名参数传递时,函数参数规范中列出的名称将用作字典中的键。


命名样式按名称传递所有参数,而不管函数签名如何,因此只要名称匹配,也可以按任何顺序列出特定参数:

from sqlalchemy.event import listens_for
from sqlalchemy.pool import Pool


@listens_for(Pool, "connect", named=True)
def my_on_connect(dbapi_connection, **kw):
    print("New DBAPI connection:", dbapi_connection)
    print("Connection record:", kw["connection_record"])


上面,**kw 的存在告诉 listens_for() 参数应该按名称传递给函数,而不是按位置传递。


目标


listen() 函数在目标方面非常灵活。它通常接受类、这些类的实例以及可以从中派生相应目标的相关类或对象。例如,上面提到的 “connect” 事件接受 Engine 类和对象以及 Pool 类和对象:

from sqlalchemy.event import listen
from sqlalchemy.pool import Pool, QueuePool
from sqlalchemy import create_engine
from sqlalchemy.engine import Engine
import psycopg2


def connect():
    return psycopg2.connect(user="ed", host="127.0.0.1", dbname="test")


my_pool = QueuePool(connect)
my_engine = create_engine("postgresql+psycopg2://ed@localhost/test")

# associate listener with all instances of Pool
listen(Pool, "connect", my_on_connect)

# associate listener with all instances of Pool
# via the Engine class
listen(Engine, "connect", my_on_connect)

# associate listener with my_pool
listen(my_pool, "connect", my_on_connect)

# associate listener with my_engine.pool
listen(my_engine, "connect", my_on_connect)


修饰符


一些侦听器允许将修饰符传递给 listen()。这些修饰符有时会为侦听器提供备用调用签名。例如,对于 ORM 事件,某些事件侦听器可以具有一个返回值,该值会修改后续处理。默认情况下,没有侦听器需要返回值,而是通过传递 retval=True 可以支持此值:

def validate_phone(target, value, oldvalue, initiator):
    """Strip non-numeric characters from a phone number"""

    return re.sub(r"\D", "", value)


# setup listener on UserContact.phone attribute, instructing
# it to use the return value
listen(UserContact.phone, "set", validate_phone, retval=True)


事件和多处理


SQLAlchemy 的事件钩子是通过 Python 函数和对象实现的,因此事件通过 Python 函数调用传播。Python 多处理遵循与我们对 OS 多处理的思考方式相同,例如父进程分叉子进程,因此我们可以使用相同的模型描述 SQLAlchemy 事件系统的行为。


在父进程中注册的事件钩子将出现在注册钩子后从该父进程分叉的新子进程中,因为子进程在生成时从父进程中分叉出所有现有 Python 结构的副本。在注册钩子之前已经存在的子进程将不会收到这些新的事件钩子,因为对父进程中的 Python 结构所做的更改不会传播到子进程。


对于事件本身,这些是 Python 函数调用,它们没有任何能力在进程之间传播。SQLAlchemy 的事件系统不实现任何进程间通信。可以实现在其中使用 Python 进程间消息传递的事件钩子,但这需要由用户实现。


事件参考


SQLAlchemy Core 和 SQLAlchemy ORM 都具有各种各样的事件钩子:


  • 核心事件 - 这些事件在 Core Events 的 Events,包括特定于连接池生命周期、SQL 语句执行、事务生命周期以及 schema 创建和拆除的事件钩子。


  • ORM 事件 - 这些事件在 ORM 事件,包括特定于类和属性插桩的事件钩子、对象初始化钩子、属性更改钩子、会话状态、刷新和提交钩子、映射器初始化、对象/结果填充和每实例持久性钩子。


API 参考


对象名称

描述


包含(目标、标识符、fn)


如果给定的 target/ident/fn 设置为侦听,则返回 True。


listen(目标、标识符、fn、*args、**kw)


为给定目标注册侦听器函数。


listens_for(目标、标识符、*args、**kw)


将函数装饰为给定目标 + 标识符的侦听器。


删除 (目标、标识符、FN)


删除事件侦听器。


函数 sqlalchemy.event 中。listentarget Any标识符 strfn Callable[[...] Any]*args Any**kw Any


为给定目标注册侦听器函数。


listen() 函数是 SQLAlchemy 事件系统主接口的一部分,记录在 事件.


例如:

from sqlalchemy import event
from sqlalchemy.schema import UniqueConstraint


def unique_constraint_name(const, table):
    const.name = "uq_%s_%s" % (table.name, list(const.columns)[0].name)


event.listen(
    UniqueConstraint, "after_parent_attach", unique_constraint_name
)

参数

  • insertbool) – 事件处理程序的默认行为是将 将 decorated 的用户定义函数添加到已注册的内部列表中 事件侦听器。如果用户使用 insert=True,SQLAlchemy 会将函数插入(预置)到 内部列表。此功能通常不使用或 由 SQLAlchemy 维护者推荐,但提供它是为了确保 某些用户定义的函数可以在其他函数之前运行,例如当 更改 MySQL 中的 sql_mode


  • namedbool) – 当使用命名参数传递时,函数参数规范中列出的名称将用作字典中的键。请参见命名参数样式


  • oncebool) – 私有/内部 API 使用。荒废的。此参数将提供事件函数将仅针对给定目标运行一次。但是,它并不意味着自动取消注册 listener 函数;关联任意数量的侦听器而不显式删除它们将导致内存无限增长,即使指定了 once=True 也是如此。


  • propagatebool) – propagate kwarg 在使用 ORM 插桩和映射事件时可用。请参阅 MapperEvents MapperEvents.before_mapper_configured() 例如。


  • retval布尔值) –


    此标志仅适用于特定的事件侦听器,每个事件侦听器都包含说明何时应使用它的文档。默认情况下,没有侦听器需要返回值。但是,某些侦听器确实支持返回值的特殊行为,并在其文档中包括 retval=True 标志对于处理返回值是必需的。


    使用 listen.retval 的事件侦听器套件 包括 ConnectionEventsAttributeEvents 的 PropertyEvents 中。


注意


不能在运行目标事件的同时调用 listen() 函数。这对线程安全有影响,也意味着不能从 listener 函数内部为其自身添加事件。要运行的事件列表存在于可变集合中,在迭代期间无法更改。


事件注册和删除并非旨在成为 “高速”作;这是一个配置作。对于需要快速大规模关联和取消关联事件的系统,请使用从单个侦听器内部处理的可变结构。


函数 sqlalchemy.event 中。listens_fortarget Any标识符 str*args Any**kw 任何 Callable[[Callable[[...] 任意]], 可调用[[...] 任意]


将函数装饰为给定目标 + 标识符的侦听器。


listens_for() 装饰器是 SQLAlchemy 事件系统主接口的一部分,记录在 事件 中。


此函数通常与 listen() 共享相同的 kwargs。


例如:

from sqlalchemy import event
from sqlalchemy.schema import UniqueConstraint


@event.listens_for(UniqueConstraint, "after_parent_attach")
def unique_constraint_name(const, table):
    const.name = "uq_%s_%s" % (table.name, list(const.columns)[0].name)


也可以使用 once 参数仅在事件的第一次调用时调用给定函数:

@event.listens_for(Mapper, "before_configure", once=True)
def on_config():
    do_config()


警告


once 参数并不意味着在第一次调用侦听器函数后自动取消注册该函数;侦听器条目将保持与目标对象的关联。关联任意数量的侦听器而不显式删除它们将导致内存无限增长,即使 once=True 也是如此 。


另请参阅


listen() - 事件侦听的一般描述


函数 sqlalchemy.event 中。removetarget Any标识符 strfn Callable[[...] Any]


删除事件侦听器。


此处的参数应与发送到 listen();由于此调用而进行的所有事件注册都将通过调用具有相同参数的 remove() 来恢复。


例如:

# if a function was registered like this...
@event.listens_for(SomeMappedClass, "before_insert", propagate=True)
def my_listener_function(*arg):
    pass


# ... it's removed like this
event.remove(SomeMappedClass, "before_insert", my_listener_function)


在上面,与 SomeMappedClass 关联的侦听器函数也被传播到 SomeMappedClass 的子类;remove() 函数将还原所有这些作。


注意


不能在运行目标事件的同时调用 remove() 函数。这对线程安全有影响,也意味着不能从 listener 函数内部删除事件本身。要运行的事件列表存在于可变集合中,在迭代期间无法更改。


事件注册和删除并非旨在成为 “高速”作;这是一个配置作。对于需要快速大规模关联和取消关联事件的系统,请使用从单个侦听器内部处理的可变结构。


另请参阅


listen()


函数 sqlalchemy.event 中。containstarget Any标识符 strfn Callable[[...] Any] bool


如果给定的 target/ident/fn 设置为侦听,则返回 True。