如何重写Python App Engine模型中的put()方法?
在Appengine中,我想让一个属性值自动计算并和对象一起存储。
我有一个类,叫做Rectangle(矩形),它有宽度、长度和面积。显然,面积是宽度和长度的函数,但我想把它当作一个属性,因为我想用它来排序。所以我尝试修改put()函数,在存储Rectangle的时候偷偷把面积加进去,像这样:
class Rectangle(db.Model):
width = db.IntegerProperty()
height = db.IntegerProperty()
area = db.IntegerProperty()
def put(self, **kwargs):
self.area = self.width * self.height
super(Rectangle, self).put(**kwargs)
当我直接在面积对象上调用put()
时,这样是有效的:
re1 = Rectangle(width=10, height=10)
re1.put()
print re1.area # >> 10
但是当我使用db.put()
(比如一次性保存很多对象)时,这就不行了。
re2 = Rectangle(width=5, height=5)
db.put(re2)
print re2.area # >> None
那么,正确的方式是什么呢,才能把计算出来的值“偷偷加进去”?
2 个回答
1
我同意在这个特定情况下使用 ComputedProperty
是个好主意。不过,重载 put
函数也是有用的。比如,我们可以用下面的代码来记录所有进行数据存储写入的调用者,这样可以方便我们调试写入量的突然增加。
from google.appengine.ext import db
_orig_db_put_async = db.put_async
_orig_db_model_put = db.Model.put
def _new_db_put_async(models, *args, **kwargs):
"""Instrumented version of db.put_async (which db.put also calls)."""
retval = _orig_db_put_async(models, *args, **kwargs)
msg = ['query: %s' % _get_caller()]
# 'models' can be either a single model instance, or a list of them.
try:
for model in models:
msg.append(model.__class__.__name__ + '.<db.put>')
except TypeError:
msg.append(models.__class__.__name__ + '.<db.put>')
instance_cache.increment(' -- '.join(msg))
return retval
def _new_db_model_put(self, *args, **kwargs):
"""Like entity.put() but stores put-stats in the instance cache."""
retval = _orig_db_model_put(self, *args, **kwargs)
msg = ['query: %s' % _get_caller()]
msg.append(self.__class__.__name__ + '.<put>')
instance_cache.increment(' -- '.join(msg))
return retval
这段代码会统计哪些代码路径在使用内存缓存进行写入,然后偶尔把这些信息记录到日志里。日志的内容大概是这样的:
3041: activity_summary.py:312 -- UserData.<put>
这里的 3041
表示在 activity_summary.py 的第 312
行调用了 UserData.put()
的次数。
7
不要重写 put 方法——正如你所看到的,这样做很脆弱,而且如果你直接调用 db.put
而不是模型的 put 函数,它是不会被触发的。
幸运的是,App Engine 提供了一个 ComputedProperty
,这让你的使用场景变得非常简单:
class Rectangle(db.Model):
width = db.IntegerProperty()
height = db.IntegerProperty()
@db.ComputedProperty
def area(self):
return self.width * self.height