SqlAlchemy 2.0 中文文档(四十七)(1)https://developer.aliyun.com/article/1563045
事件注册
通过单个 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_connection
和connection_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"])
使用命名参数传递时,函数参数规范中列出的名称将用作字典中的键。
Named 风格通过名称传递所有参数,而不管函数签名如何,因此特定参数也可以列出,以任何顺序,只要名称匹配即可:
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 多进程遵循与我们所思考的操作系统多进程相同的方式,例如父进程分叉出子进程,因此我们可以使用相同的模型描述 SQLAlchemy 事件系统的行为。
在父进程中注册的事件钩子将存在于从该父进程分叉出的新子进程中,因为子进程在生成时从父进程开始时具有所有现有 Python 结构的副本。在注册钩子之前存在的子进程将不会接收到这些新的事件钩子,因为在父进程中对 Python 结构所做的更改不会传播到子进程。
对于事件本身,这些是 Python 函数调用,它们没有任何传播到进程之间的能力。SQLAlchemy 的事件系统不实现任何进程间通信。可能会实现在其中使用 Python 进程间消息传递的事件钩子,但这需要用户实现。
事件参考
SQLAlchemy 核心和 SQLAlchemy ORM 都具有各种各样的事件钩子:
- 核心事件 - 这些在 核心事件 中描述,包括特定于连接池生命周期、SQL 语句执行、事务生命周期和架构创建和拆除的事件钩子。
- ORM 事件 - 这些在 ORM 事件 中描述,包括特定于类和属性检测、对象初始化钩子、属性变更钩子、会话状态、刷新和提交钩子、映射器初始化、对象/结果填充和每个实例持久性钩子的事件钩子。
API 参考
对象名称 | 描述 |
contains(target, identifier, fn) | 如果给定的目标/标识符/fn 被设置为监听,则返回 True。 |
listen(target, identifier, fn, *args, **kw) | 为给定的目标注册一个侦听器函数。 |
listens_for(target, identifier, *args, **kw) | 将函数装饰为给定目标 + 标识符的侦听器。 |
remove(target, identifier, fn) | 移除一个事件侦听器。 |
function sqlalchemy.event.listen(target: Any, identifier: str, fn: Callable[[...], Any], *args: Any, **kw: Any) → None
为给定的目标注册一个侦听器函数。
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)
参数:
insert
(bool) – 事件处理程序的默认行为是在发现时将装饰的用户定义函数追加到已注册的事件侦听器的内部列表中。如果用户使用insert=True
注册一个函数,SQLAlchemy 将在发现时将函数插入(预置)到内部列表中。这个功能通常不被 SQLAlchemy 维护者使用或推荐,但为了确保某些用户定义的函数可以在其他函数之前运行,比如更改 MySQL 中的 sql_mode 时,提供了此功能。named
(bool) – 在使用命名参数传递时,函数参数规范中列出的名称将用作字典中的键。请参阅命名参数风格。once
(bool) – 私有/内部 API 使用。已弃用。此参数将提供事件函数仅在给定目标上运行一次。但是,这并不意味着侦听器函数会自动取消注册;如果未显式删除关联的任意数量的侦听器,则即使指定了once=True
,内存也会无限增长。propagate
(bool) – 在使用 ORM 仪器和映射事件时,propagate
关键字参数是可用的。参见MapperEvents
和MapperEvents.before_mapper_configured()
进行示例。retval
(bool) –
此标志仅适用于特定的事件侦听器,每个事件侦听器都包含说明何时应该使用它的文档。默认情况下,没有侦听器需要返回值。但是,一些侦听器确实支持返回值的特殊行为,并在其文档中包含了必须使用retval=True
标志才能处理返回值的说明。
利用listen.retval
的事件侦听器套件包括ConnectionEvents
和AttributeEvents
。
注意
listen()
函数不能在目标事件正在运行时调用。这对线程安全性有影响,并且还意味着无法从监听器函数内部为自身添加事件。要运行的事件列表存在于一个可变集合内,在迭代期间不能更改。
事件注册和移除并不意味着是“高速”操作;它是一种配置操作。对于需要在高比例下快速关联和取消关联事件的系统,请使用可变结构,并且从单个监听器内部处理。
另请参阅
listens_for()
remove()
function sqlalchemy.event.listens_for(target: Any, identifier: str, *args: Any, **kw: Any) → Callable[[Callable[[...], Any]], Callable[[...], Any]]
将函数装饰为给定目标 + 标识符的监听器。
listens_for()
装饰器是 SQLAlchemy 事件系统的主要接口之一,在事件文档中有说明。
此函数通常具有与 listens()
相同的 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()
- 事件监听的一般描述
function sqlalchemy.event.remove(target: Any, identifier: str, fn: Callable[[...], Any]) → None
移除事件监听器。
此处的参数应与发送到 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()
函数不能在目标事件正在运行时调用。这对线程安全性有影响,并且还意味着无法从监听器函数内部移除事件本身。要运行的事件列表存在于一个可变集合内,在迭代期间不能更改。
事件注册和移除并不意味着是“高速”操作;它是一种配置操作。对于需要在高比例下快速关联和取消关联事件的系统,请使用可变结构,并且从单个监听器内部处理。
另请参阅
listen()
function sqlalchemy.event.contains(target: Any, identifier: str, fn: Callable[[...], Any]) → bool
如果给定的目标/标识符/函数已设置为监听,则返回 True。
运行时检查 API
检查模块提供了inspect()
函数,该函数提供了关于 SQLAlchemy 对象的运行时信息,包括核心部分和 ORM 部分。
inspect()
函数是访问 SQLAlchemy 公共 API 以查看内存对象配置和构建的入口点。根据传递给inspect()
的对象类型,返回值将是提供已知接口的相关对象,或者在许多情况下将返回对象本身。
inspect()
的理由是双重的。一是它取代了需要了解 SQLAlchemy 中大量“获取信息”的函数的需求,例如Inspector.from_engine()
(在 1.4 中已弃用)、instance_state()
、class_mapper()
等。另一个是inspect()
的返回值保证遵守文档化的 API,从而允许基于 SQLAlchemy 配置构建的第三方工具以向前兼容的方式构建。
对象名称 | 描述 |
inspect(subject[, raiseerr]) | 为给定目标生成一个检查对象。 |
function sqlalchemy.inspect(subject: Any, raiseerr: bool = True) → Any
为给定目标生成一个检查对象。
在某些情况下,返回的值可能与给定的对象相同,例如如果传递了一个Mapper
对象。在其他情况下,它将是给定对象的注册检查类型的实例,例如如果传递了一个Engine
,则返回一个Inspector
对象。
参数:
subject
– 要检查的主题。raiseerr
– 当为True
时,如果给定主题不对应于已知的 SQLAlchemy 检查类型,则引发sqlalchemy.exc.NoInspectionAvailable
。如果为False
,则返回None
。
可检查的目标
下面是许多常见检查目标的列表。
Connectable
(即Engine
、Connection
) - 返回一个Inspector
对象。ClauseElement
- 所有 SQL 表达式组件,包括Table
、Column
,都作为自己的检查对象,这意味着传递给inspect()
的这些对象返回它们自身。object
- 给定一个对象,ORM 将检查其映射 - 如果是这样,将返回一个表示对象的映射状态的InstanceState
。InstanceState
还通过AttributeState
接口提供对每个属性状态的访问,以及通过History
对象提供对任何属性的每次 flush 的“历史”访问。
请参见
映射实例的检查type
(即类) - 由 ORM 给定的类将被检查映射 - 如果是这样,将返回该类的Mapper
。
请参见
Mapper 对象的检查- 映射属性 - 将映射属性传递给
inspect()
,例如inspect(MyClass.some_attribute)
,将返回一个QueryableAttribute
对象,它是与映射类关联的 描述符。该描述符指的是一个MapperProperty
,通常是ColumnProperty
或RelationshipProperty
的实例,通过其QueryableAttribute.property
属性。 AliasedClass
- 返回一个AliasedInsp
对象。
可用的检查目标
以下是许多常见检查目标的列表。
Connectable
(即Engine
,Connection
) - 返回一个Inspector
对象。ClauseElement
- 所有 SQL 表达式组件,包括Table
,Column
,都作为自己的检查对象,这意味着任何这些对象传递给inspect()
都会返回它们自己。object
- 给定的对象将由 ORM 检查映射 - 如果是这样,将返回一个表示对象映射状态的InstanceState
。InstanceState
还通过AttributeState
接口提供对每个属性状态的访问,以及通过History
对象提供对任何属性的每次刷新“历史”的访问。
另请参见
映射实例的检查type
(即一个类) - 将给定的类检查 ORM 是否有映射 - 如果有,将返回该类的Mapper
。
另请参见
Mapper 对象的检查- 传递映射属性 - 将映射属性传递给
inspect()
,例如inspect(MyClass.some_attribute)
,将返回一个QueryableAttribute
对象,该对象是与映射类相关联的描述符。此描述符指向一个MapperProperty
,通常是ColumnProperty
或RelationshipProperty
的实例,通过其QueryableAttribute.property
属性。 AliasedClass
- 返回一个AliasedInsp
对象。
核心异常
与 SQLAlchemy 一起使用的异常。
基础异常类是 SQLAlchemyError
。作为 DBAPI 异常的结果引发的异常都是 DBAPIError
的子类。
exception sqlalchemy.exc.AmbiguousForeignKeysError
当在联接过程中无法定位两个可选择项之间的多个匹配外键时引发。
类签名
类 sqlalchemy.exc.AmbiguousForeignKeysError
(sqlalchemy.exc.ArgumentError
)
exception sqlalchemy.exc.ArgumentError
当提供了无效或冲突的函数参数时引发。
此错误通常对应于构建时状态错误。
类签名
类 sqlalchemy.exc.ArgumentError
(sqlalchemy.exc.SQLAlchemyError
)
exception sqlalchemy.exc.AwaitRequired
如果在需要异步操作但没有等待时调用了异步 greenlet spawn,则会引发错误。
类签名
类 sqlalchemy.exc.AwaitRequired
(sqlalchemy.exc.InvalidRequestError
)
exception sqlalchemy.exc.Base20DeprecationWarning
用于特定于 SQLAlchemy 2.0 中已弃用或传统的 API 的使用。
另请参阅
在 SQLAlchemy 2.0 中 将不再 。
SQLAlchemy 2.0 弃用模式
类签名
类 sqlalchemy.exc.Base20DeprecationWarning
(sqlalchemy.exc.SADeprecationWarning
)
attribute deprecated_since: str | None = '1.4'
表明开始引发此废弃警告的版本
exception sqlalchemy.exc.CircularDependencyError
当检测到循环依赖时,通过拓扑排序解决。
出现此错误的两种情况如下:
- 在会话刷新操作中,如果两个对象相互依赖,它们不能仅通过 INSERT 或 DELETE 语句进行插入或删除;需要使用 UPDATE 来后关联或先取消关联其中一个外键约束值。在 指向自身的行 / 相互依赖的行 中描述的
post_update
标志可以解决这种循环。 - 在
MetaData.sorted_tables
操作中,两个ForeignKey
或ForeignKeyConstraint
对象相互引用。对其中一个或两个应用use_alter=True
标志,参见 通过 ALTER 创建/删除外键约束。
类签名
类 sqlalchemy.exc.CircularDependencyError
(sqlalchemy.exc.SQLAlchemyError
)
method __init__(message: str, cycles: Any, edges: Any, msg: str | None = None, code: str | None = None)
exception sqlalchemy.exc.CompileError
在 SQL 编译期间发生错误时引发
类签名
类 sqlalchemy.exc.CompileError
(sqlalchemy.exc.SQLAlchemyError
)
exception sqlalchemy.exc.ConstraintColumnNotFoundError
当约束引用不存在于被约束表中的字符串列名称时引发。
在版本 2.0 中新增。
类签名
类 sqlalchemy.exc.ConstraintColumnNotFoundError
(sqlalchemy.exc.ArgumentError
)
exception sqlalchemy.exc.DBAPIError
当数据库操作执行失败时引发。
封装由 DB-API 底层数据库操作引发的异常。可能的情况下,SQLAlchemy 的 DBAPIError
匹配 DB-API 标准异常类型的特定驱动程序实现被封装为相应的子类型。DB-API 的 Error
类型映射到 SQLAlchemy 中的 DBAPIError
,否则名称相同。请注意,不能保证不同的 DB-API 实现将为任何给定的错误条件引发相同的异常类型。
DBAPIError
包含 StatementError.statement
和 StatementError.params
属性,提供有关出现问题的语句的上下文信息,典型情况下,错误是在发出 SQL 语句的上下文中引发的。
封装的异常对象在 StatementError.orig
属性中可用。其类型和属性是特定于 DB-API 实现的。
类签名
类 sqlalchemy.exc.DBAPIError
(sqlalchemy.exc.StatementError
)
method __init__(statement: str | None, params: _AnyExecuteParams | None, orig: BaseException, hide_parameters: bool = False, connection_invalidated: bool = False, code: str | None = None, ismulti: bool | None = None)
exception sqlalchemy.exc.DataError
包装一个 DB-API DataError。
类签名
类 sqlalchemy.exc.DataError
(sqlalchemy.exc.DatabaseError
)
exception sqlalchemy.exc.DatabaseError
包装一个 DB-API DatabaseError。
类签名
类 sqlalchemy.exc.DatabaseError
(sqlalchemy.exc.DBAPIError
)
exception sqlalchemy.exc.DisconnectionError
在原始 DB-API 连接上检测到断开连接。
此错误由连接池内部引发并消耗。它可以由 PoolEvents.checkout()
事件引发,以便主机池强制重试;在池放弃并引发关于连接尝试的 InvalidRequestError
之前,该异常将被连续捕获三次。
类签名
类 sqlalchemy.exc.DisconnectionError
(sqlalchemy.exc.SQLAlchemyError
)
对象名称 | 描述 |
DontWrapMixin | 一个混合类,当应用于用户定义的异常类时,在执行语句过程中发出错误时不会被包装在 StatementError 内。 |
HasDescriptionCode | 添加 ‘code’ 作为属性和 ‘_code_str’ 作为方法的助手 |
class sqlalchemy.exc.DontWrapMixin
一个混合类,当应用于用户定义的异常类时,在执行语句过程中发出错误时不会被包装在 StatementError
内。
例如:
from sqlalchemy.exc import DontWrapMixin class MyCustomException(Exception, DontWrapMixin): pass class MySpecialType(TypeDecorator): impl = String def process_bind_param(self, value, dialect): if value == 'invalid': raise MyCustomException("invalid!")
exception sqlalchemy.exc.DuplicateColumnError
正在向表添加列,该列将替换另一列,但没有适当的参数允许此操作。
版本 2.0.0b4 中新增。
类签名
类 sqlalchemy.exc.DuplicateColumnError
(sqlalchemy.exc.ArgumentError
)
class sqlalchemy.exc.HasDescriptionCode
添加 ‘code’ 作为属性和 ‘_code_str’ 作为方法的助手
exception sqlalchemy.exc.IdentifierError
当模式名称超出最大字符限制时引发
类签名
类 sqlalchemy.exc.IdentifierError
(sqlalchemy.exc.SQLAlchemyError
)
exception sqlalchemy.exc.IllegalStateChangeError
追踪状态的对象遇到了某种类型的非法状态改变。
版本 2.0 中新增。
类签名
类sqlalchemy.exc.IllegalStateChangeError
(sqlalchemy.exc.InvalidRequestError
)
exception sqlalchemy.exc.IntegrityError
包装了一个 DB-API IntegrityError。
SqlAlchemy 2.0 中文文档(四十七)(3)https://developer.aliyun.com/article/1563047