SqlAlchemy 2.0 中文文档(三十二)(4)https://developer.aliyun.com/article/1562554
构建自定义比较器
混合属性还包括一个助手,允许构建自定义比较器。比较器对象允许用户单独定制每个 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 值端。用新的CaseInsensitiveWord
类替换之前的CaseInsensitiveComparator
类:
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
混合值模式对于任何可能具有多个表示形式的值非常有用,例如时间戳、时间间隔、测量单位、货币和加密密码等。
另请参阅
混合类型和值无关类型 - 在 techspot.zzzeek.org 博客上
值无关类型,第二部分 - 在 techspot.zzzeek.org 博客上
API 参考
对象名称 | 描述 |
Comparator | 一个辅助类,允许轻松构建用于混合类型的自定义 PropComparator 类。 |
hybrid_method | 允许定义具有实例级和类级行为的 Python 对象方法的装饰器。 |
hybrid_property | 允许定义具有实例级和类级行为的 Python 描述符的装饰器。 |
HybridExtensionType | 枚举类型。 |
class sqlalchemy.ext.hybrid.hybrid_method
允许定义具有实例级和类级行为的 Python 对象方法的装饰器。
成员
init(), expression(), extension_type, inplace, is_attribute
类签名
class sqlalchemy.ext.hybrid.hybrid_method
(sqlalchemy.orm.base.InspectionAttrInfo
, typing.Generic
)
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 expression(expr: Callable[[Concatenate[Any, _P]], SQLCoreOperations[_R]]) → hybrid_method[_P, _R]
提供一个修改装饰器,定义一个生成 SQL 表达式的方法。
attribute extension_type: InspectionAttrExtensionType = 'HYBRID_METHOD'
扩展类型(如果有)。默认为 NotExtension.NOT_EXTENSION
另请参阅
HybridExtensionType
AssociationProxyExtensionType
attribute inplace
返回此 hybrid_method
的 inplace mutator。
当调用 hybrid_method.expression()
装饰器时,hybrid_method
类已经执行“in place”变异,因此此属性返回 Self。
2.0.4 版中的新功能。
另请参阅
使用 inplace 创建符合 pep-484 标准的混合属性(#hybrid-pep484-naming)
attribute is_attribute = True
如果此对象是 Python 描述符,则为 True。
这可能是许多类型之一。通常是一个 QueryableAttribute
,它代表一个 MapperProperty
上的属性事件。但也可以是一个扩展类型,例如 AssociationProxy
或 hybrid_property
。InspectionAttr.extension_type
将指示一个常量,用于标识特定的子类型。
另请参阅
Mapper.all_orm_descriptors
class sqlalchemy.ext.hybrid.hybrid_property
一个装饰器,允许定义既有实例级别又有类级别行为的 Python 描述符。
成员
init(), comparator(), deleter(), expression(), extension_type, getter(), inplace, is_attribute, overrides, setter(), update_expression()
类签名
类sqlalchemy.ext.hybrid.hybrid_property
(sqlalchemy.orm.base.InspectionAttrInfo
, sqlalchemy.orm.base.ORMDescriptor
)
method __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 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 deleter(fdel: _HybridDeleterType[_T]) → hybrid_property[_T]
提供一个修改装饰器,定义一个删除方法。
method expression(expr: _HybridExprCallableType[_T]) → hybrid_property[_T]
提供一个修改装饰器,定义一个生成 SQL 表达式的方法。
当在类级别调用混合时,此处给出的 SQL 表达式将包装在一个专门的 QueryableAttribute
中,该对象与 ORM 用于表示其他映射属性的对象相同。这样做的原因是为了在返回的结构中保持其他类级别属性(如文档字符串和对混合本身的引用),而不对传入的原始 SQL 表达式进行任何修改。
注意
当从拥有类引用混合属性时(例如 SomeClass.some_hybrid
),会返回一个 QueryableAttribute
的实例,表示表达式或比较器对象以及此混合对象。然而,该对象本身有名为 expression
和 comparator
的访问器;因此,在尝试在子类上覆盖这些装饰器时,可能需要首先使用 hybrid_property.overrides
修饰符进行限定。详情请参阅该修饰符。
参见
定义与属性行为不同的表达式行为
attribute extension_type: InspectionAttrExtensionType = 'HYBRID_PROPERTY'
扩展类型,如果有的话。默认为 NotExtension.NOT_EXTENSION
参见
HybridExtensionType
AssociationProxyExtensionType
method getter(fget: _HybridGetterType[_T]) → hybrid_property[_T]
提供一个修改装饰器,定义一个 getter 方法。
自 1.2 版新功能。
attribute inplace
返回此 hybrid_property
的 inplace mutator。
这是为了允许对混合进行原地变异,从而允许重用某个特定名称的第一个混合方法以添加更多方法,而无需将这些方法命名为相同的名称,例如:
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 版新功能。
参见
使用 inplace 创建符合 pep-484 的混合属性
attribute is_attribute = True
如果此对象是 Python 描述符,则为 True。
这可以指代许多类型之一。通常是一个 QueryableAttribute
,它代表一个 MapperProperty
的属性事件。但也可以是一个扩展类型,如 AssociationProxy
或 hybrid_property
。InspectionAttr.extension_type
将引用一个常量,用于标识特定的子类型。
另请参见
Mapper.all_orm_descriptors
attribute 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 setter(fset: _HybridSetterType[_T]) → hybrid_property[_T]
提供一个定义 setter 方法的修改装饰器。
method 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 中新增。
class sqlalchemy.ext.hybrid.Comparator
一个辅助类,允许轻松构建用于混合使用的自定义 PropComparator
类。
类签名
class sqlalchemy.ext.hybrid.Comparator
(sqlalchemy.orm.PropComparator
)
class sqlalchemy.ext.hybrid.HybridExtensionType
一个枚举。
成员
HYBRID_METHOD, HYBRID_PROPERTY
类签名
类sqlalchemy.ext.hybrid.HybridExtensionType
(sqlalchemy.orm.base.InspectionAttrExtensionType
)
attribute HYBRID_METHOD = 'HYBRID_METHOD'
表示一个InspectionAttr
的符号,类型为hybrid_method
赋予InspectionAttr.extension_type
属性。
另请参阅
Mapper.all_orm_attributes
attribute HYBRID_PROPERTY = 'HYBRID_PROPERTY'
表示一个InspectionAttr
的符号
类型为hybrid_method
。
赋予InspectionAttr.extension_type
属性。
另请参阅
Mapper.all_orm_attributes
(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 版新功能。 参见 使用 inplace 创建符合 pep-484 的混合属性 ```py attribute is_attribute = True
如果此对象是 Python 描述符,则为 True。
这可以指代许多类型之一。通常是一个 QueryableAttribute
,它代表一个 MapperProperty
的属性事件。但也可以是一个扩展类型,如 AssociationProxy
或 hybrid_property
。InspectionAttr.extension_type
将引用一个常量,用于标识特定的子类型。
另请参见
Mapper.all_orm_descriptors
attribute 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 setter(fset: _HybridSetterType[_T]) → hybrid_property[_T]
提供一个定义 setter 方法的修改装饰器。
method 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 中新增。
class sqlalchemy.ext.hybrid.Comparator
一个辅助类,允许轻松构建用于混合使用的自定义 PropComparator
类。
类签名
class sqlalchemy.ext.hybrid.Comparator
(sqlalchemy.orm.PropComparator
)
class sqlalchemy.ext.hybrid.HybridExtensionType
一个枚举。
成员
HYBRID_METHOD, HYBRID_PROPERTY
类签名
类sqlalchemy.ext.hybrid.HybridExtensionType
(sqlalchemy.orm.base.InspectionAttrExtensionType
)
attribute HYBRID_METHOD = 'HYBRID_METHOD'
表示一个InspectionAttr
的符号,类型为hybrid_method
赋予InspectionAttr.extension_type
属性。
另请参阅
Mapper.all_orm_attributes
attribute HYBRID_PROPERTY = 'HYBRID_PROPERTY'
表示一个InspectionAttr
的符号
类型为hybrid_method
。
赋予InspectionAttr.extension_type
属性。
另请参阅
Mapper.all_orm_attributes