SqlAlchemy 2.0 中文文档(三十二)(5)

简介: SqlAlchemy 2.0 中文文档(三十二)

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接口的一部分。像小写化这样的“强制转换”操作可以应用于所有比较操作(即eqltgt等)使用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 上的属性事件。但也可以是一个扩展类型,例如 AssociationProxyhybrid_propertyInspectionAttr.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_propertysqlalchemy.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的实例,表示表达式或比较器对象作为这个混合对象。然而,该对象本身有名为expressioncomparator的访问器;因此,在子类中尝试覆盖这些装饰器时,可能需要首先使用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 的实例,表示表达式或比较器对象以及此混合对象。然而,该对象本身有名为 expressioncomparator 的访问器;因此,在尝试在子类上覆盖这些装饰器时,可能需要首先使用 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 的属性事件。但也可以是一个扩展类型,如 AssociationProxyhybrid_propertyInspectionAttr.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.HybridExtensionTypesqlalchemy.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 的属性事件。但也可以是一个扩展类型,如 AssociationProxyhybrid_propertyInspectionAttr.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.HybridExtensionTypesqlalchemy.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

相关文章
|
6月前
|
SQL API 数据安全/隐私保护
SqlAlchemy 2.0 中文文档(三十二)(3)
SqlAlchemy 2.0 中文文档(三十二)
44 1
|
6月前
|
SQL 缓存 关系型数据库
SqlAlchemy 2.0 中文文档(三十七)(2)
SqlAlchemy 2.0 中文文档(三十七)
50 2
|
6月前
|
SQL 缓存 数据库连接
SqlAlchemy 2.0 中文文档(二十二)(3)
SqlAlchemy 2.0 中文文档(二十二)
32 5
|
6月前
|
SQL 存储 缓存
SqlAlchemy 2.0 中文文档(三十七)(3)
SqlAlchemy 2.0 中文文档(三十七)
41 1
|
6月前
|
SQL 缓存 API
SqlAlchemy 2.0 中文文档(三十七)(5)
SqlAlchemy 2.0 中文文档(三十七)
36 1
|
6月前
|
SQL 存储 缓存
SqlAlchemy 2.0 中文文档(三十七)(4)
SqlAlchemy 2.0 中文文档(三十七)
65 1
|
6月前
|
SQL 存储 关系型数据库
SqlAlchemy 2.0 中文文档(三十四)(4)
SqlAlchemy 2.0 中文文档(三十四)
51 1
|
6月前
|
SQL 关系型数据库 数据库
SqlAlchemy 2.0 中文文档(三十四)(5)
SqlAlchemy 2.0 中文文档(三十四)
50 0
|
6月前
|
SQL 缓存 数据库连接
SqlAlchemy 2.0 中文文档(二十二)(2)
SqlAlchemy 2.0 中文文档(二十二)
64 3
|
6月前
|
SQL 存储 数据库连接
SqlAlchemy 2.0 中文文档(二十二)(1)
SqlAlchemy 2.0 中文文档(二十二)
79 2

热门文章

最新文章