Suppose I have a peewee model which looks more or less as follows:
class MyModel(peewee.Model):
a = peewee.IntegerField()
b = peewee.IntegerField()
And I wish to add a property to that model as follows:
@property
def diff(self):
return self.a - self.b
This is helpful sometimes; now, if Object
is a MyModel
instance, I can easily check its diff
with Object.diff
.
What I can't do is the following:
objects = list(MyModel.select().where(MyModel.diff < 17))
And this is since MyModel.diff is a simple property, and is probably always greater than 17. It is not an Expression
like MyModel.a < 17
.
It would be very nice to expose diff
as if it was a field; so the user of that API will not need to know whether the specific implementation has a
and b
as real fields and diff
as a virtual one, or rather a
and diff
as real fields and b
as a virtual one.
Of course, my real intention is to use properties which involve, in some cases, much more sophisticated calculation that that presented on diff
; an example is
@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)
On the other hand, it can be a very simple property, such as
@property
def seven(self):
return 7
This means it cannot, in general, be converted to SQL, but should rather filter the results after they are retrieved from the database.
Is that possible?
I've just discovered peewee playhouse's hybrid methods/properties. These provide a partial solution to my question.
For example, my diff
method can become a hybrid_property
, and work as expected. My complicated_property
cannot become one, or at least it seems like it; the if
condition in its beginning will return either True
or False
constantly, and will not act as a function.
Peewee probably has some more magic hiding there; I'll keep looking and report my findings.
Sounds like hybrid_property
will be what you're looking for. Here is the hybrid methods documentation
As to your update, if you had just read a little further in the docs...
@hybrid_property
def radius(self):
return abs(self.length) / 2
@radius.expression
def radius(cls):
return fn.ABS(cls.length) / 2
So there you see two functions for the same property, radius
. The first function will be invoked when called on the model instance. The second when called in a query.
You might write:
@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))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With