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()
.
精彩评论