get_value_for_datastore与make_value_from_datastore的用法

标签:Google App Engine, Python

以前曾写过一篇《避免ReferenceProperty自动解引用》,那篇文章里提到了使用get_value_for_datastore来获取原始值,避免自动解引用的方法。
不过Property还有个奇怪的make_value_from_datastore方法,它的用法其实正好和get_value_for_datastore相反:后者是直接返回实体中与数据库里存储的一致的属性值,即真实存储的值是它;而前者则是在保存属性值时,对其做相应的转换,使它可以转换成datastore中适合存储的类型。(注:这里是指原始实现,你可以覆盖它以实现想要的效果。)

要理解它们,读读ReferenceProperty的源码就懂了,由于比较长,我就不列出了。
可以看到__get__和__set__会做些额外的事,在赋值时会把key设成self.__id_attr_name(),而model_instance设为self.__resolved_attr_name();取值时则尝试取self.__resolved_attr_name(),取不到再用self.__id_attr_name()作为key去访问数据库获取。
但是get_value_for_datastore就不会去访问数据库,而是直接返回self.__id_attr_name(),于是就拿到key了。
至于make_value_from_datastore,由于没有覆盖,因此是保存原值;但由于__set__已经进行了转换,因此也没差。

再来看一个更为有趣的例子,来自aetycoon的PickleProperty,稍微加了点测试代码:
import pickle
from google.appengine.ext import db

class PickleProperty(db.Property):

	data_type = db.Blob

	def __get__(self, model_instance, model_class):
		print '__get__'
		return super(PickleProperty, self).__get__(model_instance, model_class)

	def __set__(self, model_instance, value):
		print '__set__'
		super(PickleProperty, self).__set__(model_instance, value)

	def get_value_for_datastore(self, model_instance):
		print 'get_value_for_datastore'
		value = self.__get__(model_instance, model_instance.__class__)
		if value is not None:
			return db.Blob(pickle.dumps(value))

	def make_value_from_datastore(self, value):
		print 'make_value_from_datastore'
		if value is not None:
			return pickle.loads(str(value))


class Test(db.Model):
	obj = PickleProperty()

def main():
	print 'Content-Type: text/plain\n'
	t = Test(key_name='1')
	t.obj = [1, 2]
	print
	print t.obj
	print '\nbefore put'
	t.put()
	print 'after put\n'

	print 'before get'
	t = Test.get_by_key_name('1')
	print 'after get\n'
	print t.obj
	t.obj = '123'
	print t.obj

if __name__ == '__main__':
	main()
照上文所述,这个PickleProperty会在保存到数据库时,自动把属性dump成二进制值;而在获取时,又load成Python对象。而结果也证明是对的:
__set__
__set__

__get__
[1, 2]

before put
get_value_for_datastore
__get__
after put

before get
make_value_from_datastore
__set__
after get

__get__
[1, 2]
__set__
__get__
123
可以看到,只有和数据库交互时才会自动执行get_value_for_datastore和make_value_from_datastore,其他的访问时都是执行__get__和__set__,因此这就避免了多次转换带来的性能问题。
此外还得注意,获取数据库的实体虽然是get,但是它会设置model对象的属性,因此调用的是make_value_from_datastore和__set__;而保存实体则正好相反。

最后再来看一个JsonProperty的实现:
from django.utils import simplejson
from google.appengine.ext import db

class JsonProperty(db.TextProperty):
	def validate(self, value):
		return value

	def get_value_for_datastore(self, model_instance):
		result = super(JsonProperty, self).get_value_for_datastore(model_instance)
		result = simplejson.dumps(result)
		return db.Text(result)

	def make_value_from_datastore(self, value):
		try:
			value = simplejson.loads(str(value))
		except:
			pass

		return super(JsonProperty, self).make_value_from_datastore(value)
只是把pickle换成了simplejson而已,因此没什么需要解释的。

0条评论 你不来一发么↓

    想说点什么呢?