SQLAlchemy 2.0 文档
- 上一篇: SQL 表达式作为映射属性
- 下一篇: 复合列类型
- 上级: 首页
- 本页内容
更改属性行为¶
本节将讨论用于修改 ORM 映射属性行为的特性和技术,包括使用 mapped_column()
、relationship()
和其他方法映射的属性。
简单验证器¶
向属性添加“验证”例程的快速方法是使用 validates()
装饰器。属性验证器可以引发异常,阻止属性值更改的过程,或者可以将给定值更改为其他值。与所有属性扩展一样,验证器仅由正常的 userland 代码调用;当 ORM 填充对象时,不会发出调用。
from sqlalchemy.orm import validates
class EmailAddress(Base):
__tablename__ = "address"
id = mapped_column(Integer, primary_key=True)
email = mapped_column(String)
@validates("email")
def validate_email(self, key, address):
if "@" not in address:
raise ValueError("failed simple email validation")
return address
当项目添加到集合时,验证器还会接收集合追加事件。
from sqlalchemy.orm import validates
class User(Base):
# ...
addresses = relationship("Address")
@validates("addresses")
def validate_address(self, key, address):
if "@" not in address.email:
raise ValueError("failed simplified email validation")
return address
默认情况下,验证函数不会为集合删除事件发出,因为通常的期望是丢弃的值不需要验证。但是,validates()
通过向装饰器指定 include_removes=True
来支持接收这些事件。当设置此标志时,验证函数必须接收一个额外的布尔参数,如果 True
,则指示该操作是删除。
from sqlalchemy.orm import validates
class User(Base):
# ...
addresses = relationship("Address")
@validates("addresses", include_removes=True)
def validate_address(self, key, address, is_remove):
if is_remove:
raise ValueError("not allowed to remove items from the collection")
else:
if "@" not in address.email:
raise ValueError("failed simplified email validation")
return address
通过 backref 链接的相互依赖的验证器的情况也可以定制,使用 include_backrefs=False
选项;当此选项设置为 False
时,如果事件是由于 backref 引起的,则阻止验证函数发出。
from sqlalchemy.orm import validates
class User(Base):
# ...
addresses = relationship("Address", backref="user")
@validates("addresses", include_backrefs=False)
def validate_address(self, key, address):
if "@" not in address:
raise ValueError("failed simplified email validation")
return address
上面,如果我们像 some_address.user = some_user
那样分配给 Address.user
,则 不会 发出 validate_address()
函数,即使 some_user.addresses
发生了追加 - 该事件是由 backref 引起的。
请注意,validates()
装饰器是构建在属性事件之上的便捷函数。需要更多控制属性更改行为配置的应用程序可以使用此系统,该系统在 AttributeEvents
中描述。
对象名称 | 描述 |
---|---|
validates(*names, [include_removes, include_backrefs]) |
将方法装饰为一个或多个命名属性的“验证器”。 |
- function sqlalchemy.orm.validates(*names: str, include_removes: bool = False, include_backrefs: bool = True) → Callable[[_Fn], _Fn]¶
将方法装饰为一个或多个命名属性的“验证器”。
将方法指定为验证器,该方法接收属性的名称以及要分配的值,或者在集合的情况下,接收要添加到集合的值。然后,该函数可以引发验证异常以阻止进程继续(其中 Python 的内置
ValueError
和AssertionError
异常是合理的选择),或者可以在继续之前修改或替换值。否则,该函数应返回给定值。请注意,集合的验证器 不能 在验证例程中发出该集合的加载 - 此用法会引发断言以避免递归溢出。这是一种不支持的可重入条件。
- 参数:
*names¶ – 要验证的属性名称列表。
include_removes¶ – 如果为 True,则也会发送“remove”事件 - 验证函数必须接受一个额外的参数“is_remove”,该参数将是一个布尔值。
include_backrefs¶ –
默认为
True
;如果为False
,则如果发起者是通过 backref 相关的属性事件,则验证函数将不会发出。这可以用于双向validates()
用法,其中每个属性操作只应发出一个验证器。Changed in version 2.0.16: 此参数在 2.0.0 到 2.0.15 版本中意外地默认为
False
。其正确的默认值True
在 2.0.16 中恢复。
另请参阅
简单验证器 -
validates()
的用法示例
在核心层使用自定义数据类型¶
影响列值的一种非 ORM 方法,使其适合在 Python 中的表示形式与数据库中的表示形式之间转换数据,可以通过使用应用于映射的 Table
元数据的自定义数据类型来实现。这在数据进入数据库和返回时发生的某种样式的编码/解码情况下更为常见;在核心文档中的 增强现有类型 中阅读有关此内容的更多信息。
使用描述符和混合属性¶
为属性生成修改行为的更全面的方法是使用 描述符。这些通常在 Python 中使用 property()
函数。描述符的标准 SQLAlchemy 技术是创建一个普通的描述符,并使其从具有不同名称的映射属性读取/写入。下面我们使用 Python 2.6 样式的属性来说明这一点
class EmailAddress(Base):
__tablename__ = "email_address"
id = mapped_column(Integer, primary_key=True)
# name the attribute with an underscore,
# different from the column name
_email = mapped_column("email", String)
# then create an ".email" attribute
# to get/set "._email"
@property
def email(self):
return self._email
@email.setter
def email(self, email):
self._email = email
上述方法有效,但我们可以添加更多内容。虽然我们的 EmailAddress
对象将通过 email
描述符将值传递到 _email
映射属性中,但类级别的 EmailAddress.email
属性不具有可与 Select
一起使用的常用表达式语义。为了提供这些,我们改为使用 hybrid
扩展,如下所示
from sqlalchemy.ext.hybrid import hybrid_property
class EmailAddress(Base):
__tablename__ = "email_address"
id = mapped_column(Integer, primary_key=True)
_email = mapped_column("email", String)
@hybrid_property
def email(self):
return self._email
@email.setter
def email(self, email):
self._email = email
当我们在 EmailAddress
类级别(即直接从 EmailAddress
类)使用时,.email
属性除了在拥有 EmailAddress
实例时提供 getter/setter 行为外,还提供 SQL 表达式。
from sqlalchemy.orm import Session
from sqlalchemy import select
session = Session()
address = session.scalars(
select(EmailAddress).where(EmailAddress.email == "address@example.com")
).one()
SELECT address.email AS address_email, address.id AS address_id
FROM address
WHERE address.email = ?
('address@example.com',)
address.email = "otheraddress@example.com"
session.commit()
UPDATE address SET email=? WHERE address.id = ?
('otheraddress@example.com', 1)
COMMIT
hybrid_property
还允许我们更改属性的行为,包括在使用 hybrid_property.expression()
修饰符在实例级别与类/表达式级别访问属性时定义不同的行为。例如,如果我们想自动添加主机名,我们可以定义两组字符串操作逻辑
class EmailAddress(Base):
__tablename__ = "email_address"
id = mapped_column(Integer, primary_key=True)
_email = mapped_column("email", String)
@hybrid_property
def email(self):
"""Return the value of _email up until the last twelve
characters."""
return self._email[:-12]
@email.setter
def email(self, email):
"""Set the value of _email, tacking on the twelve character
value @example.com."""
self._email = email + "@example.com"
@email.expression
def email(cls):
"""Produce a SQL expression that represents the value
of the _email column, minus the last twelve characters."""
return func.substr(cls._email, 0, func.length(cls._email) - 12)
上面,访问 EmailAddress
实例的 email
属性将返回 _email
属性的值,从值中删除或添加主机名 @example.com
。当我们查询 email
属性时,会渲染一个 SQL 函数,产生相同的效果。
address = session.scalars(
select(EmailAddress).where(EmailAddress.email == "address")
).one()
SELECT address.email AS address_email, address.id AS address_id
FROM address
WHERE substr(address.email, ?, length(address.email) - ?) = ?
(0, 12, 'address')
在 混合属性 中阅读有关混合属性的更多信息。
同义词¶
同义词是映射器级别的构造,允许类上的任何属性“镜像”另一个映射的属性。
从最基本的意义上讲,同义词是使特定属性可以通过其他名称访问的简单方法。
from sqlalchemy.orm import synonym
class MyClass(Base):
__tablename__ = "my_table"
id = mapped_column(Integer, primary_key=True)
job_status = mapped_column(String(50))
status = synonym("job_status")
上面的类 MyClass
有两个属性,.job_status
和 .status
,它们在表达式级别都表现为一个属性。
>>> print(MyClass.job_status == "some_status")
my_table.job_status = :job_status_1
>>> print(MyClass.status == "some_status")
my_table.job_status = :job_status_1
以及在实例级别。
>>> m1 = MyClass(status="x")
>>> m1.status, m1.job_status
('x', 'x')
>>> m1.job_status = "y"
>>> m1.status, m1.job_status
('y', 'y')
synonym()
可以用于任何类型的映射属性,这些属性是 MapperProperty
的子类,包括映射列和关系,以及同义词本身。
除了简单的镜像之外,synonym()
也可以设置为引用用户定义的 描述符。我们可以为我们的 status
同义词提供一个 @property
。
class MyClass(Base):
__tablename__ = "my_table"
id = mapped_column(Integer, primary_key=True)
status = mapped_column(String(50))
@property
def job_status(self):
return "Status: " + self.status
job_status = synonym("status", descriptor=job_status)
当使用声明式时,可以使用 synonym_for()
装饰器更简洁地表达上述模式。
from sqlalchemy.ext.declarative import synonym_for
class MyClass(Base):
__tablename__ = "my_table"
id = mapped_column(Integer, primary_key=True)
status = mapped_column(String(50))
@synonym_for("status")
@property
def job_status(self):
return "Status: " + self.status
虽然 synonym()
对于简单的镜像很有用,但在现代用法中,使用描述符增强属性行为的用例最好使用 混合属性 功能来处理,该功能更面向 Python 描述符。从技术上讲,synonym()
可以完成 hybrid_property
可以完成的所有操作,因为它也支持注入自定义 SQL 功能,但在更复杂的情况下,混合属性更易于使用。
对象名称 | 描述 |
---|---|
synonym(name, *, [map_column, descriptor, comparator_factory, init, repr, default, default_factory, compare, kw_only, hash, info, doc]) |
将属性名称表示为映射属性的同义词,该属性将镜像另一个属性的值和表达式行为。 |
- function sqlalchemy.orm.synonym(name: str, *, map_column: bool | None = None, descriptor: Any | None = None, comparator_factory: Type[PropComparator[_T]] | None = None, init: _NoArg | bool = _NoArg.NO_ARG, repr: _NoArg | bool = _NoArg.NO_ARG, default: _NoArg | _T = _NoArg.NO_ARG, default_factory: _NoArg | Callable[[], _T] = _NoArg.NO_ARG, compare: _NoArg | bool = _NoArg.NO_ARG, kw_only: _NoArg | bool = _NoArg.NO_ARG, hash: _NoArg | bool | None = _NoArg.NO_ARG, info: _InfoType | None = None, doc: str | None = None) → Synonym[Any]¶
将属性名称表示为映射属性的同义词,该属性将镜像另一个属性的值和表达式行为。
例如:
class MyClass(Base): __tablename__ = "my_table" id = Column(Integer, primary_key=True) job_status = Column(String(50)) status = synonym("job_status")
- 参数:
name¶ – 现有映射属性的名称。这可以引用在类上配置的字符串名称 ORM 映射属性,包括列绑定属性和关系。
descriptor¶ – Python 描述符,当在实例级别访问此属性时,将用作 getter(以及可能的 setter)。
map_column¶ –
仅适用于经典映射和针对现有 Table 对象的映射。如果为
True
,则synonym()
构造将找到映射表上的Column
对象,该对象通常与此同义词的属性名称关联,并生成一个新的ColumnProperty
,该属性将此Column
映射到作为同义词的“name”参数给出的备用名称;通过这种方式,通常的重新定义Column
映射以使用不同名称的步骤是不必要的。这通常旨在在Column
将被替换为也使用描述符的属性时使用,即与synonym.descriptor
参数结合使用。my_table = Table( "my_table", metadata, Column("id", Integer, primary_key=True), Column("job_status", String(50)), ) class MyClass: @property def _job_status_descriptor(self): return "Status: %s" % self._job_status mapper( MyClass, my_table, properties={ "job_status": synonym( "_job_status", map_column=True, descriptor=MyClass._job_status_descriptor, ) }, )
上面,名为
_job_status
的属性会自动映射到job_status
列。>>> j1 = MyClass() >>> j1._job_status = "employed" >>> j1.job_status Status: employed
当使用声明式时,为了结合同义词提供描述符,请使用
sqlalchemy.ext.declarative.synonym_for()
助手。但是,请注意,通常应优先使用 混合属性 功能,尤其是在重新定义属性行为时。info¶ – 可选数据字典,将填充到此对象的
InspectionAttr.info
属性中。comparator_factory¶ –
一个
PropComparator
的子类,将在 SQL 表达式级别提供自定义比较行为。注意
对于提供重新定义属性的 Python 级别和 SQL 表达式级别行为的属性的用例,请参阅 使用描述符和混合属性 中介绍的混合属性,以获得更有效的技术。
运算符自定义¶
SQLAlchemy ORM 和核心表达式语言使用的“运算符”是完全可自定义的。例如,比较表达式 User.name == 'ed'
使用 Python 本身内置的运算符 operator.eq
- SQLAlchemy 与此类运算符关联的实际 SQL 构造可以修改。新的操作也可以与列表达式关联。列表达式发生的运算符最直接地在类型级别重新定义 - 请参阅 重新定义和创建新运算符 部分以获取描述。
ORM 级别函数(如 column_property()
、relationship()
和 composite()
)也通过将 PropComparator
子类传递给每个函数的 comparator_factory
参数来提供 ORM 级别的运算符重新定义。在此级别自定义运算符是一种罕见的用例。请参阅 PropComparator
中的文档以获取概述。
flambé! 龙和 炼金术士 图像设计由 Rotem Yaari 创作并慷慨捐赠。
使用 Sphinx 7.2.6 创建。文档最后生成时间:Tue 11 Mar 2025 02:40:17 PM EDT