混合属性


在具有 “hybrid” 行为的 ORM 映射类上定义属性。


“hybrid” 表示该属性在类级别和实例级别定义了不同的行为。


混合扩展提供了一种特殊形式的方法装饰器,并且对 SQLAlchemy 的其余部分的依赖性最小。它的基本作理论适用于任何基于描述符的表达系统。


考虑一个映射 Interval,表示整数 startend 值。我们可以在生成 SQL 的映射类上定义更高级别的函数 表达式,以及 实例级别。 下面,每个函数都用 hybrid_methodhybrid_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 属性返回 endstart 属性。对于 Interval 的实例,这种减法在 Python 中使用普通的 Python 描述符机制进行:

>>> i1 = Interval(5, 10)
>>> i1.length
5


在处理 Interval 类本身时,hybrid_property descriptor 计算给定 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 应用于 attribute 的方法。这些方法返回布尔值,并利用 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"


定义不同于 Attribute 行为的表达式行为


在上一节中,我们在 Interval.containsInterval.intersects 方法中使用 & 和 |位运算符是幸运的,因为我们的函数对两个布尔值进行作以返回一个新的布尔值。在许多情况下,Python 内部函数和 SQLAlchemy SQL 表达式的构造具有足够的差异,因此应该定义两个单独的 Python 表达式。为此,混合装饰器定义了一个修饰符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.radiushybrid_property由后续名为 Interval._radius_expression,使用 decorator @radius.inplace.expression,它将两个修饰符链接在一起 hybrid_property.inplacehybrid_property.expression 中。使用 hybrid_property.inplace 表示 hybrid_property.expression() 修饰符应就地改变 Interval.radius 处的现有混合对象,而无需创建新对象。有关此修饰符及其基本原理的说明,将在下一节 Using inplace to create compliant pep-484 hybrid properties(使用就地创建符合 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 属性


在上一节中,说明了一个 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 进一步限定了 decorator 的 intent 调用,以便不应创建新副本,从而维护 Interval.radius 名称,同时允许其他方法 Interval._radius_setterInterval._radius_expression 的名称不同。


2.0.4 版本中的新功能: 添加了 hybrid_property.inplace,以允许对复合hybrid_property对象进行不太详细的构造,同时不必使用重复的方法名称。此外,还允许在 hybrid_property.expression 中使用 @classmethod hybrid_property.update_expression hybrid_property.comparator 允许键入工具识别 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) 方法现在在 set 时调用:

>>> i1 = Interval(5, 10)
>>> i1.length
5
>>> i1.length = 12
>>> i1.end
17


允许批量 ORM 更新


混合体可以在使用启用 ORM 的更新时定义自定义的 “UPDATE” 处理程序,从而允许在更新的 SET 子句中使用混合体。


通常,当将 hybrid 与 update() 一起使用时,SQL expression 用作作为 SET 目标的列。 如果 Intervalstart_point具有链接到 Interval.start,这可以直接替换:

from sqlalchemy import update

stmt = update(Interval).values({Interval.start_point: 10})


但是,当使用像 Interval.length 这样的复合混合时,此混合表示多个列。我们可以使用 hybrid_property.update_expression() 装饰器设置一个处理程序,该处理程序将容纳在 VALUES 表达式中传递的值,该值可能会影响此影响。与我们的 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)


此 SET 表达式由 ORM 自动容纳。


另请参阅


启用 ORM 的 INSERT、UPDATE 和 DELETE 语句 - 包括启用 ORM 的 UPDATE 语句的背景信息


使用关系


创建与基于列的数据一起使用的混合时,没有本质区别。对不同表达的需求往往更大。我们将说明的两个变体是 “join-dependent” hybrid和 “correlated subquery” hybrid。


连接依赖关系 hybrid


请考虑以下将 UserSavingsAccount 关联的声明性映射:

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,
        )


上述混合属性平衡适用于第一个 SavingsAccount 条目。Python 中的 getter/setter 方法可以将账户视为 self 上可用的 Python 列表。


提示


上面示例中的 User.balance getter 访问 self.acccounts 集合,该集合通常通过 selectinload() 加载器策略。relationship() 上未另行说明时,默认加载器策略是 lazyload(),它按需发出 SQL。使用 asyncio 时,不支持 lazyload() 等按需加载器,因此应注意确保 self.accounts collection 可供此混合访问器访问。


在表达式级别,预计 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 表达式级别以不同的方式表达出来,我们基本上会使用外部连接:

>>> 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


相关子查询关系混合


当然,我们可以放弃依赖于封闭查询对 joins 的使用,而支持相关子查询,它可以移植地打包到单个列表达式中。相关子查询的可移植性更强,但在 SQL 级别的性能通常更差。使用使用 column_property 中演示的相同技术,我们可以调整 SavingsAccount 示例以聚合 all accounts,并对列表达式使用相关子查询:

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


构建自定义比较器


hybrid 属性还包括一个允许构建自定义 parator 的 helper 。比较器对象允许单独自定义每个 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 Properties


可以从超类中引用 hybrid,以允许修改 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 对象上的同名访问器冲突。要在直接引用父类 Descriptors 时覆盖这些方法,请添加特殊限定符 hybrid_property.overrides,它将取消引用 instrumented 属性回 hybrid 对象:

class FirstNameLastName(FirstNameOnly):
    # ...

    last_name: Mapped[str]

    @FirstNameOnly.name.overrides.expression
    @classmethod
    def name(cls):
        return func.concat(cls.first_name, " ", cls.last_name)


混合值对象


请注意,在前面的示例中,如果我们要比较 word_insensitive 属性转换为纯 Python 字符串,则 Python 字符串不会被强制为小写 - 该 CaseInsensitiveComparator 返回的 @word_insensitive.comparator 的 SQL 端。


定制比较器的一种更全面的形式是构建一个 Hybrid value 对象。此方法将目标值或表达式应用于值对象,然后在所有情况下由访问器返回该对象。value 对象允许控制对值的所有作以及如何处理比较值,无论是在 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 的 “converted” 形式工作,无论是 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 博客上


值不可知类型,第 II 部分 - techspot.zzzeek.org 博客


API 参考


对象名称

描述


比较仪


允许轻松构建自定义的帮助程序类 PropComparator (属性比较器) 用于 Hybrids 的类。

hybrid_method


一个装饰器,允许定义具有实例级和类级行为的 Python 对象方法。

hybrid_property


一个装饰器,允许定义具有实例级和类级行为的 Python 描述符。

HybridExtensionType


一个枚举。


sqlalchemy.ext.hybrid 中。hybrid_method


一个装饰器,允许定义具有实例级和类级行为的 Python 对象方法。


方法 sqlalchemy.ext.hybrid.hybrid_method. __init__func Callable[[Concatenate[Any _P]], _R], expr:Callable[[Concatenate[Any,_P]],SQLCoreOperations[_R]]None=None


创建新hybrid_method


通常通过 decorator 进行使用:

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. expressionexpr Callable[[Concatenate[Any _P]], SQLCoreOperations[_R]] hybrid_method[_P _R]


提供定义 SQL 表达式生成方法的修改修饰器。


属性 sqlalchemy.ext.hybrid.hybrid_method. extension_type: InspectionAttrExtensionType = 'HYBRID_METHOD'


扩展类型(如果有)。默认为 NotExtension.NOT_EXTENSION


属性 sqlalchemy.ext.hybrid.hybrid_method. 就地


返回此hybrid_method的就地 mutator。


hybrid_method.expression() 装饰器被调用时,hybrid_method 类已经执行了 “in place” 更改,因此此属性返回 Self。


2.0.4 新版功能.


属性 sqlalchemy.ext.hybrid.hybrid_method. is_attribute = True


如果此对象是 Python 描述符,则为 True。


这可以指多种类型之一。 通常为 QueryableAttribute,它代表 MapperProperty 处理属性事件。但也可以是扩展类型,如 AssociationProxyhybrid_propertyInspectionAttr.extension_type 将引用标识特定子类型的常量。


sqlalchemy.ext.hybrid 中。hybrid_property


一个装饰器,允许定义具有实例级和类级行为的 Python 描述符。


类签名


sqlalchemy.ext.hybrid.hybrid_property sqlalchemy.orm.base.InspectionAttrInfo sqlalchemy.orm.base.ORMDescriptor


方法 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


通常通过 decorator 进行使用:

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

方法 sqlalchemy.ext.hybrid.hybrid_property. ComparatorComparator _HybridComparatorCallableType[_T] hybrid_property[_T]


提供定义自定义比较器生成方法的修改装饰器。


装饰方法的返回值应该是 比较器


注意


hybrid_property.comparator() 装饰器 替换hybrid_property.expression() 装饰。 它们不能一起使用。


在类级别调用 hybrid 时, 这里给出的 Comparator 对象被包装在一个专门的 QueryableAttribute 中,该对象与 ORM 用来表示其他 Map 属性的对象类型相同。这样做的原因是,其他类级属性(例如文档字符串和对 hybrid 本身的引用)可以在返回的结构中维护,而无需对传入的原始 comparator 对象进行任何修改。


注意


当从拥有类引用混合属性时(例如 SomeClass.some_hybrid)、一个 QueryableAttribute 返回,将表达式或比较器对象表示为此混合对象。但是,该对象本身具有名为 expression比较器;因此,当尝试在 子类中,可能需要使用 hybrid_property.overrides 修饰符。有关详细信息,请参阅该修饰符。


方法 sqlalchemy.ext.hybrid.hybrid_property. deleterfdel _HybridDeleterType[_T] hybrid_property[_T]


提供定义删除方法的修改修饰器。


method sqlalchemy.ext.hybrid.hybrid_property. expressionexpr _HybridExprCallableType[_T] hybrid_property[_T]


提供定义 SQL 表达式生成方法的修改修饰器。


当在类级别调用混合时,这里给出的 SQL 表达式被包装在一个专门的 QueryableAttribute 中,该对象与 ORM 用来表示其他映射属性的对象类型相同。这样做的原因是,其他类级属性(例如文档字符串和对 hybrid 本身的引用)可以在返回的结构中维护,而无需对传入的原始 SQL 表达式进行任何修改。


注意


当从拥有类引用混合属性时(例如 SomeClass.some_hybrid)、一个 QueryableAttribute 返回,表示表达式或比较器对象以及此混合对象。但是,该对象本身具有名为 expression比较器;因此,当尝试在 子类中,可能需要使用 hybrid_property.overrides 修饰符。有关详细信息,请参阅该修饰符。


属性 sqlalchemy.ext.hybrid.hybrid_property. extension_type: InspectionAttrExtensionType = 'HYBRID_PROPERTY'


扩展类型(如果有)。默认为 NotExtension.NOT_EXTENSION


方法 sqlalchemy.ext.hybrid.hybrid_property. getterfget _HybridGetterType[_T] hybrid_property[_T]


提供定义 getter 方法的修改装饰器。


在 1.2 版本加入.


属性 sqlalchemy.ext.hybrid.hybrid_property. 就地


返回此 hybrid_property的就地 mutator。


这是为了允许 hybrid 的就地更改,允许重用特定名称的第一个 hybrid 方法,以便添加更多方法,而不必为这些方法命名相同的方法,例如:

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 新版功能.


属性 sqlalchemy.ext.hybrid.hybrid_property. is_attribute = True


如果此对象是 Python 描述符,则为 True。


这可以指多种类型之一。 通常为 QueryableAttribute,它代表 MapperProperty 处理属性事件。但也可以是扩展类型,如 AssociationProxyhybrid_propertyInspectionAttr.extension_type 将引用标识特定子类型的常量。


属性 sqlalchemy.ext.hybrid.hybrid_property. 覆盖


覆盖现有属性的方法的前缀。


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 版本加入.


方法 sqlalchemy.ext.hybrid.hybrid_property. setterfset _HybridSetterType[_T] hybrid_property[_T]


提供定义 setter 方法的修改修饰器。


方法 sqlalchemy.ext.hybrid.hybrid_property. update_expressionMETH _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 版本加入.


sqlalchemy.ext.hybrid 中。比较器


允许轻松构建自定义的帮助程序类 PropComparator (属性比较器) 用于 Hybrids 的类。


sqlalchemy.ext.hybrid 中。HybridExtensionType


一个枚举。


属性 sqlalchemy.ext.hybrid.HybridExtensionType. HYBRID_METHOD = 'HYBRID_METHOD'


指示 hybrid_method 类型的 InspectionAttr 的符号。


分配给 InspectionAttr.extension_type 属性。


另请参阅

Mapper.all_orm_attributes


属性 sqlalchemy.ext.hybrid.HybridExtensionType. HYBRID_PROPERTY = 'HYBRID_PROPERTY'

指示 InspectionAttr 的符号


类型 hybrid_method


分配给 InspectionAttr.extension_type 属性。


另请参阅

Mapper.all_orm_attributes