使用GAE Datastore模型的属性内置

0 投票
2 回答
705 浏览
提问于 2025-04-15 19:31

我想给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的数据存储有自己的一套属性类。我查了一下文档,发现我可以重写get_value_for_datastore(model_instance)来实现我的目标。不过,我不太明白model_instance是什么,以及如何从中提取相应的字段。

重写GAE属性类是提供类似获取和设置功能的正确方法吗?如果是的话,应该怎么做呢?

补充:

我想到的一个潜在问题是,重写get_value_for_datastore可能在对象被放入数据存储之前不会被调用。因此,在存储对象之前获取属性可能会得到一个不正确的值。

2 个回答

1

你想要的是一个叫做 派生属性(DerivedProperty) 的东西。写这个的步骤在那篇文章里有说明,跟Alex说的差不多,不过你需要重写get这个方法,而不是get_value_for_datastore,这样就可以避免需要写入数据存储来更新的问题。我的aetycoon库里有这个功能,还有其他一些有用的属性。

3

如果你想在一个或多个模型中使用多个“字段”,而这些字段有相似的行为,那么继承GAE的Property类会特别有帮助。别担心,get_value_for_datastoremake_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(这是一个完全不同的描述符)中,跟数据存储的配合就不好了(例如,你不能基于那些实际上不是db.Property实例而是property实例的字段名来写GQL查询——这些字段在数据存储中!)。

所以如果你想同时处理数据存储那些从未真正存储过的Model实例,你需要为逻辑上“相同”的字段选择两个名字——一个是你在内存模型实例中使用的属性名,这个可以是内置的property;另一个是最终存储在数据存储中的属性名,这个需要是db.Property子类的实例并且这个第二个名字是你在查询中需要使用的。当然,第一个名字对应的方法需要读取和写入第二个名字,但你不能只是“隐藏”后者,因为这是会在数据存储中的名字,也是查询时能理解的名字!

撰写回答