我想制作GAE模型属性的属性。原因是在存储之前将值转换为大写的情况。对于普通的Python类,我会做类似的事情:
Foo(db.Model):
def get_attr(self):
return self.something
def set_attr(self, value):
self.something = value.upper() if value != None else None
attr = property(get_attr, set_attr)
但是,GAE数据存储区有自己的Property类概念,我查看了documentation,似乎我可以覆盖get_value_for_datastore(model_instance)
来实现我的目标。然而,我不知道model_instance
是什么以及如何从中提取相应的字段。
重写GAE Property类是提供类似getter / setter功能的正确方法吗?如果是的话,该怎么做?
加了:
我认为覆盖get_value_for_datastore
的一个潜在问题是在将对象放入数据存储区之前可能无法调用它。因此,在存储对象之前获取属性会产生不正确的值。
答案 0 :(得分:3)
如果您想在一个或多个模型中使用多个具有相似行为的“字段”,则对GAE的Property类进行子类化会特别有用。不用担心,get_value_for_datastore
和make_value_from_datastore
将分别在任何商店和取件上进行调用 - 所以如果您需要做任何花哨的事情(包括但不限于此)要大写一个字符串,实际上并不是那个花哨;-),在你的子类中覆盖这些方法就好了。
修改:让我们看一些示例代码(扣除导入和main
):
class MyStringProperty(db.StringProperty):
def get_value_for_datastore(self, model_instance):
vv = db.StringProperty.get_value_for_datastore(self, model_instance)
return vv.upper()
class MyModel(db.Model):
foo = MyStringProperty()
class MainHandler(webapp.RequestHandler):
def get(self):
my = MyModel(foo='Hello World')
k = my.put()
mm = MyModel.get(k)
s = mm.foo
self.response.out.write('The secret word is: %r' % s)
这表明字符串在数据存储区中已大写 - 但如果您将get
调用更改为简单mm = my
,则会看到内存中实例没有受到影响。
但是,db.Property
实例本身就是一个描述符 - 将其包装到内置的property
(一个完全不同的描述符)中将不能很好地与数据存储区配合使用(例如,你可以'根据字段名称编写GQL查询,这些字段名称实际上不是db.Property
的实例,而是property
的实例 - 这些字段在数据存储区中不!)。
因此,如果您希望同时使用数据存储和来处理从未真正进入数据存储区并返回的Model
实例,则必须选择两个逻辑上“相同”字段的名称 - 一个是您将在内存模型实例中使用的属性的名称,而一个可以是内置的property
;另一个是最终在数据存储区中的属性的名称,并且该属性需要是db.Property
子类的实例和它是您需要的第二个名称在查询中使用。当然,名字底层的方法需要读取和写入第二个名称,但是你不能只是“隐藏”后者,因为这是将在数据存储区中的名称,因此这是有意义的名称。查询!
答案 1 :(得分:1)
你想要的是DerivedProperty。在该帖子中概述了编写一个的过程 - 它与Alex描述的类似,但是通过重写 get 而不是get_value_for_datastore,可以避免需要写入数据存储区来更新它的问题。我的aetycoon库包含它和其他有用的属性。