开发者

How to overwrite the put() method on a python app engine model?

In Appengine, I am trying to have a property value computed automatically and stored with the object.

I have a class, Rectangle, and it has a width, height and area. Obviously the area is a function of width and height, but I want it to be a property because I want to use it for sorting. So I try to modify the put() function to sneak the area in when storing the Rectangle like so:

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)

This works when I invoke put() on the Area object directly:

re1 = 开发者_StackOverflow中文版Rectangle(width=10, height=10)
re1.put()
print re1.area      # >> 10

But when I use db.put() (e.g. to save a lot of them at once), this breaks.

re2 = Rectangle(width=5, height=5)
db.put(re2)
print re2.area      # >> None

What is the proper way to 'sneak in' the computed value?


Don't override put - as you observe, it's fragile, and doesn't get invoked if you call db.put instead of the model's put function.

Fortunately, App Engine provides a ComputedProperty which makes your use-case really easy:

class Rectangle(db.Model):
    width   = db.IntegerProperty()
    height  = db.IntegerProperty()

    @db.ComputedProperty
    def area(self):
      return self.width * self.height


I agree that ComputedProperty is the way to go for the specific scenario described. However, it could still be useful to overload the put function. For instance, we employ the following code to keep track of all of the callers who are issuing datastore writes so that we can easily debug spikes in writes.

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

This code keeps a count of which codepaths are issuing writes in memcache and then occasionally flushes it out to the logs. The log lines look something like this:

3041: activity_summary.py:312 -- UserData.<put>

Where 3041 is the number of times line 312 of activity_summary.py issued a UserData.put().

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜