如何重写Python App Engine模型中的put()方法?

6 投票
2 回答
746 浏览
提问于 2025-04-17 03:11

在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

撰写回答