http://blog.youkuaiyun.com/lobby/article/details/2508279
概览
from google.appengine.ext import db from google.appengine.api import users classPet(db.Model): name = db.StringProperty(required=True) type = db.StringProperty(required=True, choices=set(["cat","dog","bird"])) birthdate = db.DateProperty() weight_in_pounds = db.IntegerProperty() spayed_or_neutered = db.BooleanProperty() owner = db.UserProperty() pet =Pet(name="Fluffy", type="cat", owner=users.get_current_user()) pet.weight_in_pounds =24 pet.put()
if users.get_current_user(): user_pets = db.GqlQuery("SELECT * FROM Pet WHERE pet.owner = :1", users.get_current_user()) for pet in user_pets: pet.spayed_or_neutered =True db.put(user_pets)
Entities and Models
Datastore Entities
The Model Interface
class Pet ( db . Model ):
name = db . StringProperty ( required = True )
type = db . StringProperty ( required = True , choices = set ([ "cat" , "dog" , "bird" ]))
birthdate = db . DateProperty ()
weight_in_pounds = db . IntegerProperty ()
spayed_or_neutered = db . BooleanProperty ()
owner = db . UserProperty ( required = True )
pet = Pet ( name = "Fluffy" ,
type = "cat" ,
owner = users . get_current_user ())
pet . weight_in_pounds = 24
Expando Models
first_name = db . StringProperty ()
last_name = db . StringProperty ()
hobbies = db . StringListProperty ()
p = Person ( first_name = "Albert" , last_name = "Johnson" )
p . hobbies = [ "chess" , "travel" ]
p . chess_elo_rating = 1350
p . travel_countries_visited = [ "Spain" , "Italy" , "USA" , "Brazil" ]
p . travel_trip_count = 13
p1 . favorite = 42
p1 . put ()
p2 = Person ()
p2 . favorite = "blue"
p2 . put ()
p3 = Person ()
p3 . put ()
people = db . GqlQuery ( "SELECT * FROM Person WHERE favorite < :1" , 50 )
# people has p1, but not p2 or p3
people = db . GqlQuery ( "SELECT * FROM Person WHERE favorite > :1" , 50 )
# people has no results
Properties and Types
Strings, Long Strings and Blobs
string = db . StringProperty ()
obj = MyModel ()
# Python Unicode literal syntax fully describes characters in a text string.
obj . string = u "kittens"
# unicode() converts a byte string to a Unicode value using the named codec.
obj . string = unicode ( "kittens" , "latin-1" )
# A byte string is assumed to be text encoded as ASCII (the 'ascii' codec).
obj . string = "kittens"
# Short string properties can be used in query filters.
results = db . GqlQuery ( "SELECT * FROM MyModel WHERE string = :1" , u "kittens" )
text = db . TextProperty ()
obj = MyModel ()
# Text() can take a Unicode value.
obj . text = db . Text ( u "lots of kittens" )
# Text() can take a byte string and the name of an encoding.
obj . text = db . Text ( "lots of kittens" , "latin-1" )
# If no encoding is specified, a byte string is assumed to be ASCII text.
obj . text = db . Text ( "lots of kittens" )
# Text properties can store large values.
obj . text = db . Text ( open ( "a_tale_of_two_cities.txt" ). read (), "utf-8" )
blob = db . BlobProperty ()
obj = MyModel ()
obj . blob = db . Blob ( open ( "image.png" ). read ())
Lists
numbers = db . ListProperty ( long )
obj = MyModel ()
obj . numbers = [ 2 , 4 , 6 , 8 , 10 ]
obj . numbers = [ "hello" ] # ERROR: MyModel.numbers must be a list of longs.
results = db . GqlQuery ( "SELECT * FROM MyModel WHERE numbers = 6" )
# Get all entities where numbers contains at least one element less than 10.
results = db . GqlQuery ( "SELECT * FROM MyModel WHERE numbers < 10" )
-
一个静态的 ListProperty 能够把一个空的 list 赋值给它。这个值在 datastore 不存在,但模型实例表现为好像这个值是一个空的 list 。静态的 ListProperty 不能够是 None 值。
-
一个 List 动态属性的值不能够是一个空的 list 。然而它可以是 None ,并可以删除。
-
如果 entities 对列表属性进行正序排序,用来排序的值是列表的最小元素。
-
如果 entities 倒序排序,用列表最大的值来排序。
-
其它列表元素既不影响排序也,列表长度也不影响排序。
-
In the case of a tie, the key of the entity is used as the tie-breaker. ( 不太理解 ) 。这个排序会导致 [1..9] 不管是倒序还是正序都是在 [4, 5, 6, 7] 的前面,这种没意思的结果。
References
prop = db . IntegerProperty ()
class SecondModel ( db . Model ):
reference = db . ReferenceProperty ( FirstModel )
obj1 = FirstModel ()
obj1 . prop = 42
obj1 . put ()
obj2 = SecondModel ()
# A reference value is the key of another entity.
obj2 . reference = obj1 . key ()
# Assigning a model instance to a property uses the entity's key as the value.
obj2 . reference = obj1
obj2 . reference . put ()
results = db . GqlQuery ( "SELECT * FROM FirstModel" )
another_obj = results . fetch ( 1 )[ 0 ]
v = another_obj . reference . prop
if not obj1 :
# Referenced entity was deleted.
# FirstModel instance obj1:
for obj in obj1 . firstmodel_set :
# ...
Property Names
Creating, Gettingand Deleting Data
DatastoreAPI 把 entity 用模型类的实例来表示。模型类的方法可以创建,修改和删除 entity 。利用查询,或者直接通过 key ,可以从物理 datastore 服务器里面得到 entity 。· Creating and Updating an Entity
· Getting Entities Using a Query
· Getting an Entity Using a Key
Model 或者Expando类的实例表示entity。应用程序通过对应模型类的构造方法来创建新的实例 entity。
pet=Pet(name="Fluffy",
type="cat",
owner=users.get_current_user())
直到第一次调用 put()方法之后,新的数据entity才写入数据库datastore。可以直接调用对象的put方法,也可以调用 db.put()
pet.put()
db.put(pet)
如果entity已经在数据库存在了,那么put()方法会去更新它。
查询返回多个模型实例。这些实例可以修改并更新回datastore.
if users.get_current_user():
user_pets = db.GqlQuery("SELECT * FROM Pet WHERE pet.owner = :1",
users.get_current_user())
for pet inuser_pets:
pet.spayed_or_neutered=True
db.put(user_pets)
Datastore能够执行查询某个类型的entity。一个查询可以用条件子句来过滤entity的属性值,并能够返回经过排序的结果集。一个查询也可以通过祖先来限制查询结果的范围。
完整对查询语句的描述信息,包括它不能够做什么,请参阅Queries and Indexes.
DatastoreAPI 通过 2 中界面来执行查询: Query ,用查询对象的一些方法。还有 GqlQuery ,是一种类似 SQL 的语言。The Query Interface
Model 或者Expando的all() 方法返回查询对象,对应这种表类所有的entity。应用程序通过Filter()、Order()、ancesitor()来准备查询。
classStory(db.Model):
title = db.StringProperty()
date = db.DateTimeProperty()
query =Story.all()
query.filter('title=','Foo')
query.order('-date')
query.ancestor(key)
# These methods can bechained together on one line.
query.filter('title=','Foo').order('-date').ancestor(key)
The GqlQuery Interface
GqlQuery类构造函数的参数包括查询语句和可选的参数。语句包括数据的种类,条件过滤,排序还有祖先条件。还可以包括对结果集的限制以及偏移。
# Parameters can be bound with positional arguments.
query = db.GqlQuery("SELECT * FROM Story WHERE title = :1 "
"AND ANCESTOR IS :2 "
"ORDER BY date DESC",
'Foo',key)
# Or, parameters can be boundwith keyword arguments.
query = db.GqlQuery("SELECT * FROM Story WHERE title = :title "
"AND ANCESTOR IS :parent "
"ORDER BY date DESC",
title='Foo', parent=key)
# String, number and Booleanvalues can be literal values in the string.
query = db.GqlQuery("SELECT * FROM Story WHERE title = 'Foo' "
"AND ANCESTOR IS :parent "
"ORDER BY date DESC",
parent=key)
Model类的Gql()方法也是从查询语句来准备一个GqlQuery对象。相对来说它隐藏了select * from model的语句,因为已经隐含包含了。
query=Story.gql("WHERE title= :title "
"AND ANCESTOR IS :parent "
"ORDER BY date DESC",
title='Foo', parent=key)
可以用bind()方法来再次绑定参数。应用程序可以通过重新绑定参数的方法来重新利用GqlQuery对象。
Executing the Query and Accessing Results
一直等到应用程序操作结果集的时候,Query和GqlQuery 对象才真正执行查询。当程序操作返回结果的时候,查询就会被执行了。结果将作为模型类的实例存储在内存里面。每个查询类都提供2个途径来执行查询和操作结果,fetch()方法,和迭代器。
Fetch()方法返回最大的数量(limit),一个可选的参数用于跳过(偏移值)。这个方法会执行查询,返回记录,直到没有为止。一旦所有的结果被放到内存里面,结果集作为list列表的形式返回(如果指定了偏移它会忽略响应的记录)。如果调用fetch()都会执行一个完全的查询。
注意:偏移参数不会影响返回的结果行数。所有超过它限制的记录都被返回到内存中。偏移参数只影响返回哪些记录
results= query.fetch(10)
for result in results:
print"Title: "+ result.title
Fetch()方法里面的行数限制和偏移会覆盖GQL里面的。
如果是在迭代器里面用,查询的执行将不会有限制和偏移,结果返回到内存中。所有结果在每次的迭代中被返回,迭代变量对应产生模型的实例。
for resultin query:
print"Title: "+ result.title
注意:Query有一个系统的最大限制:1000行。如果没有指定限制,或者指定的限制大于最大限制,那么将采用最大限制。
entity.put()
key = entity.key()
# ...
entity = db.get(key)
Key值的常见用处是在其它的entity中用一个属性来存储。ReferenceProperty类把自动引用和废弃引用key:一个模型实例可以直接赋值给一个ReferenceProperty。并且它的key可以代替值来引用。
classPet(db.Model):
name =StringProperty()
owner =ReferenceProperty(PetOwner)
classPetOwner(db.Model):
name =StringProperty()
owner =PetOwner(name="Albert")
pet =Pet(name="Fluffy",owner=owner)
# This is equivalent:
pet =Pet(name="Fluffy",owner=owner.key())
类似的,通过ReferenceProperty属性对它的访问和访问它自身的实例一样,也是在fetch()的时候自动进行的,如果没有访问就不会fetch()
pets=GqlQuery("SELECT * FROM Pet WHERE name =:1","Fluffy")
pet = pets.get()
owner_name = pet.owner.name
如果不是用ReferenceProperty存储key值,例如 Expando动态属性,或者 ListProperty 元素,就没有自动废弃关联的行为。(好像不太对。。。)
Db.get()函数通过key或者key的列表可以从datastore返回entity。
Key可以被编码为字符串分发给应用程序的外部。然后把字符编码传递给Key类的构造函数可以重新得到key对象。
obj=MyModel(name="Foo")
self.response.write('<ahref="/view?key=%s">%s</a>'%(str(obj.key()),
obj.name()))
# ...
key_name = self.request.get('key')
obj = db.get(db.Key(key_name))
注意:对key的字符串编码是虽然是晦涩难懂的,但没有加密,如果你的程序需要key是不能够猜测的,你要在在把key传送给用户之前加密它。
应用程序能够根据模型实例或者key值来删除一个entity。实例方法是:delete()。Db.delete()则通过key值或者key值列表来删除。
q= db.GqlQuery("SELECT * FROM Message WHERE create_date < :1", earliest_date)
results = q.fetch(10)
for result in results:
result.delete()
# db.delete() requires thatall entities in one call be of the same
# entity group, because allof the deletes happen in one transaction.
q = db.GqlQuery("SELECT* FROM Message WHERE create_date < :1"+
"AND ANCESTOR IS :2",
earliest_date, parent)
results = q.fetch(10)
db.delete(results)
删除一个entity不会改变引用它的key值。如果需要进行废除关联一个已经删除的entity,应用程序应调用db.get(),在访问属性的时候测试一下返回值。
删除一个其它entity 的祖先 entity不会影响其它的entity。因为程序并不需要根据祖先entity 来构建后代entity 的key,所以后代entity 仍然是可以访问的。