在 peewee 中定义虚拟字段

Defining virtual fields in peewee

假设我有一个 peewee 模型,大致如下所示:

class MyModel(peewee.Model):
    a = peewee.IntegerField()
    b = peewee.IntegerField()

并且我希望向该模型添加一个 属性,如下所示:

    @property
    def diff(self):
        return self.a - self.b

这有时很有用;现在,如果 Object 是一个 MyModel 实例,我可以轻松地用 Object.diff.

检查它的 diff

不能做的是:

objects = list(MyModel.select().where(MyModel.diff < 17))

这是因为 MyModel.diff 是一个简单的 属性,并且可能总是大于 17。它不像 MyModel.a < 17.[=35 这样的 Expression =]

如果把 diff 当作一个字段来公开就好了;因此 API 的用户将不需要知道具体实现是否将 ab 作为真实字段以及 diff 作为虚拟字段,或者更确切地说 adiff 作为真实字段,b 作为虚拟字段。

当然,我的真正意图是使用在某些情况下涉及比 diff 上呈现的复杂得多的计算的属性;一个例子是

@property
def complicated_property(self):
    if 17 <= self.a <= 173:
        return a_complicated_math_function(self.a + self.b)
    return another_complicated_math_function(self.a * self.b ** 2)

另一方面,也可以是很简单的属性,比如

@property
def seven(self):
    return 7

这意味着它通常不能转换为 SQL,而是应该在从数据库中检索结果后对其进行过滤。

这可能吗?

更新

我刚刚发现了 peewee 剧场的混合体 methods/properties。这些为我的问题提供了部分解决方案。

例如,我的 diff 方法可以变成 hybrid_property,并按预期工作。我的 complicated_property 不能变成一个,或者至少看起来像;开始的 if 条件将 return 不断地 TrueFalse,并且不会充当函数。

Peewee 可能隐藏着更多魔法;我会继续寻找并报告我的发现。

听起来 hybrid_property 就是您要找的。这里是the hybrid methods documentation

关于您的更新,如果您刚刚进一步阅读了文档...

@hybrid_property
def radius(self):
    return abs(self.length) / 2

@radius.expression
def radius(cls):
    return fn.ABS(cls.length) / 2

因此,您会看到相同 属性、radius 的两个函数。在模型实例上调用时将调用第一个函数。第二个在查询中调用时。

你可以这样写:

@hybrid_property
def complicated_property(self):
    if 17 <= self.a <= 173:
        return a_complicated_math_function(self.a + self.b)
    return another_complicated_math_function(self.a * self.b ** 2)

@complicated_property.expression
def complicated_property(cls):
    # Here you will need to use a CASE statement most likely.
    # If you want to turn it into SQL, you obviously need to know
    # what SQL you want to turn it into...
    return case(
        None,
        (cls.a.between(17, 173), fn.math(fn.more_math(cls.a, 1.23))),
        default=fn.another_complicated_math(cls.a))