SQLAlchemy 2.0 文档
SQLAlchemy ORM
- ORM 快速入门
- ORM 映射类配置
- 关系配置
- ORM 查询指南
- 使用 Session
- 事件和内部机制
- ORM 扩展
- 异步 I/O (asyncio)
- 关联代理
- Automap
- Baked Queries
- 声明式扩展
- Mypy / Pep-484 对 ORM 映射的支持
- 突变跟踪
- 排序列表
- 水平分片
- 混合属性¶
- 定义与属性行为不同的表达式行为
- 使用
inplace
创建符合 pep-484 规范的混合属性 - 定义 Setter
- 允许批量 ORM 更新
- 处理关系
- 构建自定义比较器
- 跨子类重用混合属性
- 混合值对象
- API 参考
hybrid_method
hybrid_property
hybrid_property.__init__()
hybrid_property.comparator()
hybrid_property.deleter()
hybrid_property.expression()
hybrid_property.extension_type
hybrid_property.getter()
hybrid_property.inplace
hybrid_property.is_attribute
hybrid_property.overrides
hybrid_property.setter()
hybrid_property.update_expression()
Comparator
HybridExtensionType
- Indexable
- 备用类 Instrumentation
- ORM 示例
项目版本
- 上一篇: 水平分片
- 下一篇: Indexable
- 上级: 首页
- 本页内容
- 混合属性
- 定义与属性行为不同的表达式行为
- 使用
inplace
创建符合 pep-484 规范的混合属性 - 定义 Setter
- 允许批量 ORM 更新
- 处理关系
- 构建自定义比较器
- 跨子类重用混合属性
- 混合值对象
- API 参考
hybrid_method
hybrid_property
hybrid_property.__init__()
hybrid_property.comparator()
hybrid_property.deleter()
hybrid_property.expression()
hybrid_property.extension_type
hybrid_property.getter()
hybrid_property.inplace
hybrid_property.is_attribute
hybrid_property.overrides
hybrid_property.setter()
hybrid_property.update_expression()
Comparator
HybridExtensionType
混合属性¶
在 ORM 映射类上定义具有“混合”行为的属性。
“混合”意味着属性在类级别和实例级别定义了不同的行为。
hybrid
扩展提供了一种特殊形式的方法装饰器,并且对 SQLAlchemy 的其余部分具有最小的依赖性。它的基本操作理论可以与任何基于描述符的表达式系统一起工作。
考虑一个映射 Interval
,表示整数 start
和 end
值。我们可以在映射类上定义更高级别的函数,这些函数在类级别生成 SQL 表达式,并在实例级别生成 Python 表达式求值。在下面,用 hybrid_method
或 hybrid_property
修饰的每个函数可能会接收 self
作为类的实例,或者直接接收类,具体取决于上下文
from __future__ import annotations
from sqlalchemy.ext.hybrid import hybrid_method
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
class Base(DeclarativeBase):
pass
class Interval(Base):
__tablename__ = "interval"
id: Mapped[int] = mapped_column(primary_key=True)
start: Mapped[int]
end: Mapped[int]
def __init__(self, start: int, end: int):
self.start = start
self.end = end
@hybrid_property
def length(self) -> int:
return self.end - self.start
@hybrid_method
def contains(self, point: int) -> bool:
return (self.start <= point) & (point <= self.end)
@hybrid_method
def intersects(self, other: Interval) -> bool:
return self.contains(other.start) | self.contains(other.end)
在上面,length
属性返回 end
和 start
属性之间的差值。对于 Interval
的实例,此减法在 Python 中发生,使用普通的 Python 描述符机制
>>> i1 = Interval(5, 10)
>>> i1.length
5
当处理 Interval
类本身时,hybrid_property
描述符评估以 Interval
类作为参数给定的函数体,当使用 SQLAlchemy 表达式机制评估时,它返回一个新的 SQL 表达式
>>> from sqlalchemy import select
>>> print(select(Interval.length))
SELECT interval."end" - interval.start AS length
FROM interval
>>> print(select(Interval).filter(Interval.length > 10))
SELECT interval.id, interval.start, interval."end"
FROM interval
WHERE interval."end" - interval.start > :param_1
诸如 Select.filter_by()
之类的过滤方法也支持混合属性
>>> print(select(Interval).filter_by(length=5))
SELECT interval.id, interval.start, interval."end"
FROM interval
WHERE interval."end" - interval.start = :param_1
Interval
类示例还说明了两个方法,contains()
和 intersects()
,用 hybrid_method
修饰。此装饰器将相同的想法应用于方法,hybrid_property
将其应用于属性。这些方法返回布尔值,并利用 Python |
和 &
位运算符来生成等效的实例级别和 SQL 表达式级别的布尔行为
>>> i1.contains(6)
True
>>> i1.contains(15)
False
>>> i1.intersects(Interval(7, 18))
True
>>> i1.intersects(Interval(25, 29))
False
>>> print(select(Interval).filter(Interval.contains(15)))
SELECT interval.id, interval.start, interval."end"
FROM interval
WHERE interval.start <= :start_1 AND interval."end" > :end_1
>>> ia = aliased(Interval)
>>> print(select(Interval, ia).filter(Interval.intersects(ia)))
SELECT interval.id, interval.start,
interval."end", interval_1.id AS interval_1_id,
interval_1.start AS interval_1_start, interval_1."end" AS interval_1_end
FROM interval, interval AS interval_1
WHERE interval.start <= interval_1.start
AND interval."end" > interval_1.start
OR interval.start <= interval_1."end"
AND interval."end" > interval_1."end"
定义与属性行为不同的表达式行为¶
在上一节中,考虑到我们的函数对两个布尔值进行运算以返回一个新的布尔值,我们在 Interval.contains
和 Interval.intersects
方法中对 &
和 |
位运算符的使用是幸运的。在许多情况下,Python 函数和 SQLAlchemy SQL 表达式的构造差异很大,应该定义两个单独的 Python 表达式。hybrid
装饰器为此目的定义了一个 修饰符 hybrid_property.expression()
。作为一个例子,我们将定义间隔的半径,这需要使用绝对值函数
from sqlalchemy import ColumnElement
from sqlalchemy import Float
from sqlalchemy import func
from sqlalchemy import type_coerce
class Interval(Base):
# ...
@hybrid_property
def radius(self) -> float:
return abs(self.length) / 2
@radius.inplace.expression
@classmethod
def _radius_expression(cls) -> ColumnElement[float]:
return type_coerce(func.abs(cls.length) / 2, Float)
在上面的示例中,首先分配给名称 Interval.radius
的 hybrid_property
由后续的名为 Interval._radius_expression
的方法进行修改,使用装饰器 @radius.inplace.expression
,它将两个修饰符 hybrid_property.inplace
和 hybrid_property.expression
链接在一起。hybrid_property.inplace
的使用表明 hybrid_property.expression()
修饰符应就地修改 Interval.radius
处的现有混合对象,而无需创建新对象。有关此修饰符及其原理的注释在下一节 使用 inplace 创建符合 pep-484 规范的混合属性 中讨论。@classmethod
的使用是可选的,并且严格来说是为了给类型工具一个提示,即在这种情况下,cls
预计是 Interval
类,而不是 Interval
的实例。
注意
hybrid_property.inplace
以及使用 @classmethod
来获得适当的类型支持从 SQLAlchemy 2.0.4 开始可用,并且在早期版本中不起作用。
现在 Interval.radius
包括表达式元素,当在类级别访问 Interval.radius
时,将返回 SQL 函数 ABS()
>>> from sqlalchemy import select
>>> print(select(Interval).filter(Interval.radius > 5))
SELECT interval.id, interval.start, interval."end"
FROM interval
WHERE abs(interval."end" - interval.start) / :abs_1 > :param_1
使用 inplace
创建符合 pep-484 规范的混合属性¶
在上一节中,说明了一个 hybrid_property
装饰器,它包括两个单独的方法级别函数被装饰,两者都生成一个名为 Interval.radius
的对象属性。实际上,我们可以为 hybrid_property
使用几个不同的修饰符,包括 hybrid_property.expression()
、hybrid_property.setter()
和 hybrid_property.update_expression()
。
SQLAlchemy 的 hybrid_property
装饰器的目的是,添加这些方法可以以与 Python 内置 @property
装饰器相同的方式完成,其中惯用用法是继续重复重新定义属性,每次都使用 相同的属性名称,如下面的示例所示,该示例说明了 hybrid_property.setter()
和 hybrid_property.expression()
用于 Interval.radius
描述符的用法
# correct use, however is not accepted by pep-484 tooling
class Interval(Base):
# ...
@hybrid_property
def radius(self):
return abs(self.length) / 2
@radius.setter
def radius(self, value):
self.length = value * 2
@radius.expression
def radius(cls):
return type_coerce(func.abs(cls.length) / 2, Float)
在上面,有三个 Interval.radius
方法,但是当每个方法都被装饰时,首先被 hybrid_property
装饰器装饰,然后被 @radius
名称本身装饰,最终效果是 Interval.radius
是一个包含三个不同函数的单个属性。这种使用风格取自 Python 文档中记录的 @property 用法。重要的是要注意 @property
和 hybrid_property
的工作方式,每次都会制作 描述符的副本。也就是说,每次调用 @radius.expression
、@radius.setter
等都会创建一个全新的对象。这允许在子类中重新定义属性而不会出现问题(请参阅本节后面的 跨子类重用混合属性,了解如何使用它)。
但是,上述方法与 mypy 和 pyright 等类型工具不兼容。Python 自己的 @property
装饰器没有此限制,仅仅是因为 这些工具硬编码了 @property 的行为,这意味着根据 PEP 484 合规性,SQLAlchemy 无法使用此语法。
为了在保持类型兼容性的同时生成合理的语法,hybrid_property.inplace
装饰器允许使用不同的方法名称重复使用相同的装饰器,同时仍然在一个名称下生成单个装饰器
# correct use which is also accepted by pep-484 tooling
class Interval(Base):
# ...
@hybrid_property
def radius(self) -> float:
return abs(self.length) / 2
@radius.inplace.setter
def _radius_setter(self, value: float) -> None:
# for example only
self.length = value * 2
@radius.inplace.expression
@classmethod
def _radius_expression(cls) -> ColumnElement[float]:
return type_coerce(func.abs(cls.length) / 2, Float)
使用 hybrid_property.inplace
进一步限定了装饰器的使用,即不应制作新副本,从而保持 Interval.radius
名称,同时允许其他方法 Interval._radius_setter
和 Interval._radius_expression
被不同地命名。
2.0.4 版本新增: 添加了 hybrid_property.inplace
,以便在不必使用重复的方法名称的情况下,以更简洁的方式构造复合 hybrid_property
对象。此外,允许在 hybrid_property.expression
、hybrid_property.update_expression
和 hybrid_property.comparator
中使用 @classmethod
,以允许类型工具将方法签名中的 cls
识别为类而不是实例。
定义 Setter¶
hybrid_property.setter()
修饰符允许构造自定义 setter 方法,该方法可以修改对象上的值
class Interval(Base):
# ...
@hybrid_property
def length(self) -> int:
return self.end - self.start
@length.inplace.setter
def _length_setter(self, value: int) -> None:
self.end = self.start + value
现在在设置时调用 length(self, value)
方法
>>> i1 = Interval(5, 10)
>>> i1.length
5
>>> i1.length = 12
>>> i1.end
17
允许批量 ORM 更新¶
混合属性可以为使用 ORM 启用的更新定义自定义“UPDATE”处理程序,从而允许混合属性在 update 的 SET 子句中使用。
通常,当将混合属性与 update()
一起使用时,SQL 表达式用作 SET 的目标列。如果我们的 Interval
类有一个链接到 Interval.start
的混合属性 start_point
,则可以直接替换它
from sqlalchemy import update
stmt = update(Interval).values({Interval.start_point: 10})
但是,当使用像 Interval.length
这样的复合混合属性时,此混合属性表示多列。我们可以设置一个处理程序,该处理程序将适应 VALUES 表达式中传递的值,该值会影响此值,使用 hybrid_property.update_expression()
装饰器。与我们的 setter 类似的处理程序将是
from typing import List, Tuple, Any
class Interval(Base):
# ...
@hybrid_property
def length(self) -> int:
return self.end - self.start
@length.inplace.setter
def _length_setter(self, value: int) -> None:
self.end = self.start + value
@length.inplace.update_expression
def _length_update_expression(
cls, value: Any
) -> List[Tuple[Any, Any]]:
return [(cls.end, cls.start + value)]
在上面,如果我们在 UPDATE 表达式中使用 Interval.length
,我们将获得一个混合 SET 表达式
>>> from sqlalchemy import update
>>> print(update(Interval).values({Interval.length: 25}))
UPDATE interval SET "end"=(interval.start + :start_1)
ORM 会自动适应此 SET 表达式。
另请参阅
ORM 启用的 INSERT、UPDATE 和 DELETE 语句 - 包括有关 ORM 启用的 UPDATE 语句的背景信息
处理关系¶
创建处理相关对象的混合属性与处理基于列的数据的混合属性没有本质区别。对不同表达式的需求往往更大。我们将说明的两个变体是“join-dependent”混合属性和“correlated subquery”混合属性。
Join-Dependent 关系混合¶
考虑以下声明性映射,它将 User
与 SavingsAccount
关联起来
from __future__ import annotations
from decimal import Decimal
from typing import cast
from typing import List
from typing import Optional
from sqlalchemy import ForeignKey
from sqlalchemy import Numeric
from sqlalchemy import String
from sqlalchemy import SQLColumnExpression
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
pass
class SavingsAccount(Base):
__tablename__ = "account"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("user.id"))
balance: Mapped[Decimal] = mapped_column(Numeric(15, 5))
owner: Mapped[User] = relationship(back_populates="accounts")
class User(Base):
__tablename__ = "user"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(100))
accounts: Mapped[List[SavingsAccount]] = relationship(
back_populates="owner", lazy="selectin"
)
@hybrid_property
def balance(self) -> Optional[Decimal]:
if self.accounts:
return self.accounts[0].balance
else:
return None
@balance.inplace.setter
def _balance_setter(self, value: Optional[Decimal]) -> None:
assert value is not None
if not self.accounts:
account = SavingsAccount(owner=self)
else:
account = self.accounts[0]
account.balance = value
@balance.inplace.expression
@classmethod
def _balance_expression(cls) -> SQLColumnExpression[Optional[Decimal]]:
return cast(
"SQLColumnExpression[Optional[Decimal]]",
SavingsAccount.balance,
)
上面的混合属性 balance
处理此用户的帐户列表中的第一个 SavingsAccount
条目。Python getter/setter 方法可以将 accounts
视为 self
上可用的 Python 列表。
提示
上面的示例中,User.balance
getter 访问 self.acccounts
集合,该集合通常通过在 User.balance
relationship()
上配置的 selectinload()
加载策略加载。relationship()
上未另行说明时的默认加载策略是 lazyload()
,它按需发出 SQL。当使用 asyncio 时,不支持按需加载器(如 lazyload()
),因此应注意确保在使用 asyncio 时,self.accounts
集合可用于此混合访问器。
在表达式级别,预期 User
类将在适当的上下文中使用,以便存在与 SavingsAccount
的适当连接
>>> from sqlalchemy import select
>>> print(
... select(User, User.balance)
... .join(User.accounts)
... .filter(User.balance > 5000)
... )
SELECT "user".id AS user_id, "user".name AS user_name,
account.balance AS account_balance
FROM "user" JOIN account ON "user".id = account.user_id
WHERE account.balance > :balance_1
但是请注意,虽然实例级别访问器需要担心 self.accounts
是否甚至存在,但此问题在 SQL 表达式级别以不同的方式表达,在 SQL 表达式级别,我们基本上会使用外连接
>>> from sqlalchemy import select
>>> from sqlalchemy import or_
>>> print(
... select(User, User.balance)
... .outerjoin(User.accounts)
... .filter(or_(User.balance < 5000, User.balance == None))
... )
SELECT "user".id AS user_id, "user".name AS user_name,
account.balance AS account_balance
FROM "user" LEFT OUTER JOIN account ON "user".id = account.user_id
WHERE account.balance < :balance_1 OR account.balance IS NULL
Correlated Subquery 关系混合¶
当然,我们可以放弃依赖封闭查询对连接的使用,而支持相关的子查询,该子查询可以可移植地打包到单个列表达式中。相关的子查询更具可移植性,但在 SQL 级别上的性能通常较差。使用 使用 column_property 中说明的相同技术,我们可以调整我们的 SavingsAccount
示例以聚合 所有 帐户的余额,并对列表达式使用相关的子查询
from __future__ import annotations
from decimal import Decimal
from typing import List
from sqlalchemy import ForeignKey
from sqlalchemy import func
from sqlalchemy import Numeric
from sqlalchemy import select
from sqlalchemy import SQLColumnExpression
from sqlalchemy import String
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
from sqlalchemy.orm import relationship
class Base(DeclarativeBase):
pass
class SavingsAccount(Base):
__tablename__ = "account"
id: Mapped[int] = mapped_column(primary_key=True)
user_id: Mapped[int] = mapped_column(ForeignKey("user.id"))
balance: Mapped[Decimal] = mapped_column(Numeric(15, 5))
owner: Mapped[User] = relationship(back_populates="accounts")
class User(Base):
__tablename__ = "user"
id: Mapped[int] = mapped_column(primary_key=True)
name: Mapped[str] = mapped_column(String(100))
accounts: Mapped[List[SavingsAccount]] = relationship(
back_populates="owner", lazy="selectin"
)
@hybrid_property
def balance(self) -> Decimal:
return sum(
(acc.balance for acc in self.accounts), start=Decimal("0")
)
@balance.inplace.expression
@classmethod
def _balance_expression(cls) -> SQLColumnExpression[Decimal]:
return (
select(func.sum(SavingsAccount.balance))
.where(SavingsAccount.user_id == cls.id)
.label("total_balance")
)
上面的方法将为我们提供 balance
列,该列呈现相关的 SELECT
>>> from sqlalchemy import select
>>> print(select(User).filter(User.balance > 400))
SELECT "user".id, "user".name
FROM "user"
WHERE (
SELECT sum(account.balance) AS sum_1 FROM account
WHERE account.user_id = "user".id
) > :param_1
构建自定义比较器¶
混合属性还包括一个帮助程序,允许构建自定义比较器。比较器对象允许人们单独自定义每个 SQLAlchemy 表达式运算符的行为。当创建在 SQL 端具有一些高度特殊行为的自定义类型时,它们非常有用。
注意
本节中介绍的 hybrid_property.comparator()
装饰器取代了 hybrid_property.expression()
装饰器的使用。它们不能一起使用。
下面的示例类允许对名为 word_insensitive
的属性进行不区分大小写的比较
from __future__ import annotations
from typing import Any
from sqlalchemy import ColumnElement
from sqlalchemy import func
from sqlalchemy.ext.hybrid import Comparator
from sqlalchemy.ext.hybrid import hybrid_property
from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
class Base(DeclarativeBase):
pass
class CaseInsensitiveComparator(Comparator[str]):
def __eq__(self, other: Any) -> ColumnElement[bool]: # type: ignore[override] # noqa: E501
return func.lower(self.__clause_element__()) == func.lower(other)
class SearchWord(Base):
__tablename__ = "searchword"
id: Mapped[int] = mapped_column(primary_key=True)
word: Mapped[str]
@hybrid_property
def word_insensitive(self) -> str:
return self.word.lower()
@word_insensitive.inplace.comparator
@classmethod
def _word_insensitive_comparator(cls) -> CaseInsensitiveComparator:
return CaseInsensitiveComparator(cls.word)
在上面,针对 word_insensitive
的 SQL 表达式会将 LOWER()
SQL 函数应用于两侧
>>> from sqlalchemy import select
>>> print(select(SearchWord).filter_by(word_insensitive="Trucks"))
SELECT searchword.id, searchword.word
FROM searchword
WHERE lower(searchword.word) = lower(:lower_1)
上面的 CaseInsensitiveComparator
实现了 ColumnOperators
接口的一部分。像小写这样的“强制”操作可以应用于所有比较操作(即 eq
、lt
、gt
等),使用 Operators.operate()
class CaseInsensitiveComparator(Comparator):
def operate(self, op, other, **kwargs):
return op(
func.lower(self.__clause_element__()),
func.lower(other),
**kwargs,
)
跨子类重用混合属性¶
可以从超类引用混合属性,以允许修改诸如 hybrid_property.getter()
、hybrid_property.setter()
之类的方法,以用于重新定义子类上的这些方法。这类似于标准 Python @property
对象的工作方式
class FirstNameOnly(Base):
# ...
first_name: Mapped[str]
@hybrid_property
def name(self) -> str:
return self.first_name
@name.inplace.setter
def _name_setter(self, value: str) -> None:
self.first_name = value
class FirstNameLastName(FirstNameOnly):
# ...
last_name: Mapped[str]
# 'inplace' is not used here; calling getter creates a copy
# of FirstNameOnly.name that is local to FirstNameLastName
@FirstNameOnly.name.getter
def name(self) -> str:
return self.first_name + " " + self.last_name
@name.inplace.setter
def _name_setter(self, value: str) -> None:
self.first_name, self.last_name = value.split(" ", 1)
在上面,FirstNameLastName
类引用了 FirstNameOnly.name
中的混合属性,以将其 getter 和 setter 重新用于子类。
当仅覆盖 hybrid_property.expression()
和 hybrid_property.comparator()
作为对超类的第一个引用时,这些名称与类级别的 QueryableAttribute
对象上返回的同名访问器冲突。要在直接引用父类描述符时覆盖这些方法,请添加特殊限定符 hybrid_property.overrides
,它会将检测到的属性反向引用回混合对象
class FirstNameLastName(FirstNameOnly):
# ...
last_name: Mapped[str]
@FirstNameOnly.name.overrides.expression
@classmethod
def name(cls):
return func.concat(cls.first_name, " ", cls.last_name)
混合值对象¶
请注意,在我们之前的示例中,如果我们将 SearchWord
实例的 word_insensitive
属性与纯 Python 字符串进行比较,则纯 Python 字符串不会被强制转换为小写 - 我们构建的 CaseInsensitiveComparator
,由 @word_insensitive.comparator
返回,仅适用于 SQL 端。
更全面的自定义比较器形式是构建混合值对象。此技术将目标值或表达式应用于值对象,然后在所有情况下由访问器返回该值对象。值对象允许控制对值的所有操作以及如何处理比较值,无论是在 SQL 表达式端还是在 Python 值端。将之前的 CaseInsensitiveComparator
类替换为新的 CaseInsensitiveWord
类
class CaseInsensitiveWord(Comparator):
"Hybrid value representing a lower case representation of a word."
def __init__(self, word):
if isinstance(word, basestring):
self.word = word.lower()
elif isinstance(word, CaseInsensitiveWord):
self.word = word.word
else:
self.word = func.lower(word)
def operate(self, op, other, **kwargs):
if not isinstance(other, CaseInsensitiveWord):
other = CaseInsensitiveWord(other)
return op(self.word, other.word, **kwargs)
def __clause_element__(self):
return self.word
def __str__(self):
return self.word
key = "word"
"Label to apply to Query tuple results"
在上面,CaseInsensitiveWord
对象表示 self.word
,它可以是 SQL 函数,也可以是 Python 原生对象。通过重写 operate()
和 __clause_element__()
以便基于 self.word
工作,所有比较操作都将针对 word
的“转换”形式进行,无论是在 SQL 端还是 Python 端。我们的 SearchWord
类现在可以从单个混合调用中无条件地传递 CaseInsensitiveWord
对象
class SearchWord(Base):
__tablename__ = "searchword"
id: Mapped[int] = mapped_column(primary_key=True)
word: Mapped[str]
@hybrid_property
def word_insensitive(self) -> CaseInsensitiveWord:
return CaseInsensitiveWord(self.word)
word_insensitive
属性现在普遍具有不区分大小写的比较行为,包括 SQL 表达式与 Python 表达式的比较(请注意,Python 值在此处的 Python 端转换为小写)
>>> print(select(SearchWord).filter_by(word_insensitive="Trucks"))
SELECT searchword.id AS searchword_id, searchword.word AS searchword_word
FROM searchword
WHERE lower(searchword.word) = :lower_1
SQL 表达式与 SQL 表达式的比较
>>> from sqlalchemy.orm import aliased
>>> sw1 = aliased(SearchWord)
>>> sw2 = aliased(SearchWord)
>>> print(
... select(sw1.word_insensitive, sw2.word_insensitive).filter(
... sw1.word_insensitive > sw2.word_insensitive
... )
... )
SELECT lower(searchword_1.word) AS lower_1,
lower(searchword_2.word) AS lower_2
FROM searchword AS searchword_1, searchword AS searchword_2
WHERE lower(searchword_1.word) > lower(searchword_2.word)
仅 Python 表达式
>>> ws1 = SearchWord(word="SomeWord")
>>> ws1.word_insensitive == "sOmEwOrD"
True
>>> ws1.word_insensitive == "XOmEwOrX"
False
>>> print(ws1.word_insensitive)
someword
混合值模式对于任何可能具有多种表示形式的值都非常有用,例如时间戳、时间间隔、计量单位、货币和加密密码。
另请参阅
Hybrids and Value Agnostic Types - 在 techspot.zzzeek.org 博客上
Value Agnostic Types, Part II - 在 techspot.zzzeek.org 博客上
API 参考¶
对象名称 | 描述 |
---|---|
一个辅助类,可以轻松构建自定义的 |
|
一个装饰器,允许定义具有实例级别和类级别行为的 Python 对象方法。 |
|
一个装饰器,允许定义具有实例级别和类级别行为的 Python 描述符。 |
|
一个枚举。 |
- class sqlalchemy.ext.hybrid.hybrid_method¶
一个装饰器,允许定义具有实例级别和类级别行为的 Python 对象方法。
类签名
class
sqlalchemy.ext.hybrid.hybrid_method
(sqlalchemy.orm.base.InspectionAttrInfo
,typing.Generic
)-
method
sqlalchemy.ext.hybrid.hybrid_method.
__init__(func: Callable[[Concatenate[Any, _P]], _R], expr: Callable[[Concatenate[Any, _P]], SQLCoreOperations[_R]] | None = None)¶ 创建一个新的
hybrid_method
。用法通常是通过装饰器
from sqlalchemy.ext.hybrid import hybrid_method class SomeClass: @hybrid_method def value(self, x, y): return self._value + x + y @value.expression @classmethod def value(cls, x, y): return func.some_function(cls._value, x, y)
-
method
sqlalchemy.ext.hybrid.hybrid_method.
expression(expr: Callable[[Concatenate[Any, _P]], SQLCoreOperations[_R]]) → hybrid_method[_P, _R]¶ 提供一个修改装饰器,用于定义生成 SQL 表达式的方法。
-
attribute
sqlalchemy.ext.hybrid.hybrid_method.
extension_type: InspectionAttrExtensionType = 'HYBRID_METHOD'¶ 扩展类型(如果有)。默认为
NotExtension.NOT_EXTENSION
-
attribute
sqlalchemy.ext.hybrid.hybrid_method.
inplace¶ 返回此
hybrid_method
的就地修改器。当调用
hybrid_method.expression()
装饰器时,hybrid_method
已经执行“就地”修改,因此此属性返回 Self。2.0.4 版本新增。
-
attribute
sqlalchemy.ext.hybrid.hybrid_method.
is_attribute = True¶ 如果此对象是 Python 描述符,则为 True。
这可以指多种类型之一。通常是
QueryableAttribute
,它代表MapperProperty
处理属性事件。但也可能是扩展类型,例如AssociationProxy
或hybrid_property
。InspectionAttr.extension_type
将引用标识特定子类型的常量。
-
method
- class sqlalchemy.ext.hybrid.hybrid_property¶
一个装饰器,允许定义具有实例级别和类级别行为的 Python 描述符。
成员
__init__(), comparator(), deleter(), expression(), extension_type, getter(), inplace, is_attribute, overrides, setter(), update_expression()
类签名
class
sqlalchemy.ext.hybrid.hybrid_property
(sqlalchemy.orm.base.InspectionAttrInfo
,sqlalchemy.orm.base.ORMDescriptor
)-
method
sqlalchemy.ext.hybrid.hybrid_property.
__init__(fget: _HybridGetterType[_T], fset: _HybridSetterType[_T] | None = None, fdel: _HybridDeleterType[_T] | None = None, expr: _HybridExprCallableType[_T] | None = None, custom_comparator: Comparator[_T] | None = None, update_expr: _HybridUpdaterType[_T] | None = None)¶ 创建一个新的
hybrid_property
。用法通常是通过装饰器
from sqlalchemy.ext.hybrid import hybrid_property class SomeClass: @hybrid_property def value(self): return self._value @value.setter def value(self, value): self._value = value
-
method
sqlalchemy.ext.hybrid.hybrid_property.
comparator(comparator: _HybridComparatorCallableType[_T]) → hybrid_property[_T]¶ 提供一个修改装饰器,用于定义自定义比较器生成方法。
装饰方法的返回值应该是
Comparator
的实例。注意
hybrid_property.comparator()
装饰器替换了hybrid_property.expression()
装饰器的使用。它们不能一起使用。当在类级别调用混合属性时,此处给出的
Comparator
对象被包装在专门的QueryableAttribute
中,这与 ORM 用于表示其他映射属性的对象类型相同。这样做的原因是,以便可以在返回的结构中维护其他类级别的属性,例如文档字符串和对混合属性本身的引用,而无需对传入的原始比较器对象进行任何修改。注意
当从拥有类引用混合属性时(例如
SomeClass.some_hybrid
),将返回QueryableAttribute
的实例,表示表达式或比较器对象以及此混合对象。但是,该对象本身具有名为expression
和comparator
的访问器;因此,当尝试在子类上覆盖这些装饰器时,可能需要首先使用hybrid_property.overrides
修饰符来限定它。有关详细信息,请参阅该修饰符。
-
method
sqlalchemy.ext.hybrid.hybrid_property.
deleter(fdel: _HybridDeleterType[_T]) → hybrid_property[_T]¶ 提供一个修改装饰器,用于定义删除方法。
-
method
sqlalchemy.ext.hybrid.hybrid_property.
expression(expr: _HybridExprCallableType[_T]) → hybrid_property[_T]¶ 提供一个修改装饰器,用于定义生成 SQL 表达式的方法。
当在类级别调用混合属性时,此处给出的 SQL 表达式被包装在专门的
QueryableAttribute
中,这与 ORM 用于表示其他映射属性的对象类型相同。这样做的原因是,以便可以在返回的结构中维护其他类级别的属性,例如文档字符串和对混合属性本身的引用,而无需对传入的原始 SQL 表达式进行任何修改。注意
当从拥有类引用混合属性时(例如
SomeClass.some_hybrid
),将返回QueryableAttribute
的实例,表示表达式或比较器对象以及此混合对象。但是,该对象本身具有名为expression
和comparator
的访问器;因此,当尝试在子类上覆盖这些装饰器时,可能需要首先使用hybrid_property.overrides
修饰符来限定它。有关详细信息,请参阅该修饰符。另请参阅
-
attribute
sqlalchemy.ext.hybrid.hybrid_property.
extension_type: InspectionAttrExtensionType = 'HYBRID_PROPERTY'¶ 扩展类型(如果有)。默认为
NotExtension.NOT_EXTENSION
-
method
sqlalchemy.ext.hybrid.hybrid_property.
getter(fget: _HybridGetterType[_T]) → hybrid_property[_T]¶ 提供一个修改装饰器,用于定义 getter 方法。
1.2 版本新增。
-
attribute
sqlalchemy.ext.hybrid.hybrid_property.
inplace¶ 返回此
hybrid_property
的就地修改器。这是为了允许混合属性的就地修改,从而允许重用具有特定名称的第一个混合方法,以便添加更多方法,而无需将这些方法命名为相同名称,例如:
class Interval(Base): # ... @hybrid_property def radius(self) -> float: return abs(self.length) / 2 @radius.inplace.setter def _radius_setter(self, value: float) -> None: self.length = value * 2 @radius.inplace.expression def _radius_expression(cls) -> ColumnElement[float]: return type_coerce(func.abs(cls.length) / 2, Float)
2.0.4 版本新增。
-
attribute
sqlalchemy.ext.hybrid.hybrid_property.
is_attribute = True¶ 如果此对象是 Python 描述符,则为 True。
这可以指多种类型之一。通常是
QueryableAttribute
,它代表MapperProperty
处理属性事件。但也可能是扩展类型,例如AssociationProxy
或hybrid_property
。InspectionAttr.extension_type
将引用标识特定子类型的常量。
-
attribute
sqlalchemy.ext.hybrid.hybrid_property.
overrides¶ 用于覆盖现有属性的方法的前缀。
hybrid_property.overrides
访问器仅返回此混合对象,当从父类在类级别调用时,它将取消引用通常在此级别返回的“instrumented attribute”,并允许使用修改装饰器(如hybrid_property.expression()
和hybrid_property.comparator()
),而不会与通常存在于QueryableAttribute
上的同名属性冲突。class SuperClass: # ... @hybrid_property def foobar(self): return self._foobar class SubClass(SuperClass): # ... @SuperClass.foobar.overrides.expression def foobar(cls): return func.subfoobar(self._foobar)
1.2 版本新增。
另请参阅
-
method
sqlalchemy.ext.hybrid.hybrid_property.
setter(fset: _HybridSetterType[_T]) → hybrid_property[_T]¶ 提供一个修改装饰器,用于定义 setter 方法。
-
method
sqlalchemy.ext.hybrid.hybrid_property.
update_expression(meth: _HybridUpdaterType[_T]) → hybrid_property[_T]¶ 提供一个修改装饰器,用于定义生成 UPDATE 元组的方法。
该方法接受单个值,该值是要呈现到 UPDATE 语句的 SET 子句中的值。然后,该方法应将此值处理为适合最终 SET 子句的各个列表达式,并将它们作为 2 元组序列返回。每个元组都包含一个列表达式作为键和一个要呈现的值。
例如:
class Person(Base): # ... first_name = Column(String) last_name = Column(String) @hybrid_property def fullname(self): return first_name + " " + last_name @fullname.update_expression def fullname(cls, value): fname, lname = value.split(" ", 1) return [(cls.first_name, fname), (cls.last_name, lname)]
1.2 版本新增。
-
method
- class sqlalchemy.ext.hybrid.Comparator¶
一个辅助类,可以轻松构建自定义的
PropComparator
类,用于混合属性。
- class sqlalchemy.ext.hybrid.HybridExtensionType¶
一个枚举。
类签名
class
sqlalchemy.ext.hybrid.HybridExtensionType
(sqlalchemy.orm.base.InspectionAttrExtensionType
)-
attribute
sqlalchemy.ext.hybrid.HybridExtensionType.
HYBRID_METHOD = 'HYBRID_METHOD'¶ 符号,指示类型为
hybrid_method
的InspectionAttr
。分配给
InspectionAttr.extension_type
属性。另请参阅
Mapper.all_orm_attributes
-
attribute
sqlalchemy.ext.hybrid.HybridExtensionType.
HYBRID_PROPERTY = 'HYBRID_PROPERTY'¶ - 符号表示一个
InspectionAttr
,它是 类型为
hybrid_method
。
分配给
InspectionAttr.extension_type
属性。另请参阅
Mapper.all_orm_attributes
- 符号表示一个
-
attribute
flambé! 龙和 The Alchemist 图像设计由 Rotem Yaari 创作并慷慨捐赠。
使用 Sphinx 7.2.6 创建。文档最后生成于:Tue 11 Mar 2025 02:40:17 PM EDT