在 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 的用户将不需要知道具体实现是否将 a
和 b
作为真实字段以及 diff
作为虚拟字段,或者更确切地说 a
和 diff
作为真实字段,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 不断地 True
或 False
,并且不会充当函数。
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))
假设我有一个 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 的用户将不需要知道具体实现是否将 a
和 b
作为真实字段以及 diff
作为虚拟字段,或者更确切地说 a
和 diff
作为真实字段,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 不断地 True
或 False
,并且不会充当函数。
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))