(Google App Engine Datastore API)GAE数据库API翻译

http://blog.csdn.net/lobby/article/details/2508279

概览

  具备可伸缩性的 GAE 数据存储中,能够存储和查询的数据对象,称之为 实体: entities. 一个 entity 有一个或多个属性,表示所支持的几种支持的数据类型的一种值。属性还可以关联到其它 entity ,创建一对多或者多对多的关系。
 
 GAE 数据存储能在一个事务当中进行多个操作,并且可以在失败后回滚。在分布式的 Web 应用程序中这尤其重要。因为可能有多个用户同时读取和操作同样的数据。在多个用户都在改变数据时, GAE 数据存储尝试重新执行事务。
 
和传统的数据库不一样, GAE 数据存储利用分布式的结构以存储海量数据。一个 GAE 应用程序可以能够通过定义关联,查询索引来优化数据的分布式存储。
 
GAE 数据存储并不是关系数据库。在提供与传统数据库的相似 介面 的同时,利用它本身具备了可伸缩性的能力,提供了另外的方法来管理和设计数据库。
 
GAE 数据存储 API 用一种别具特色的机制定义数据模型。一个模型描述了一种 实体: entity ,包括多个属性的类型和配置。应用程序利用 python 类来定义模型,类的 attributes 描述属性。一种类型的 entity 对应了一个模型类的对象实例。实例的 python attributes 则对应了属性值。一个 entity 可以用类的构造函数创建,并通过调用 put() 方法之后保存到服务器。
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()
GAE 数据存储提供了 2 种查询方式介面:一种是查询对象的介面,一种是类 SQL 的语言: GQL 。一个查询返回了多个 entitiy ,放在对应的类模型的实例里面。可以修改,并返回到存储服务器。
 
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

一个 entity 有一个 key 和属性集。应用程序利用 API 来定义数据模型,并且建立模型的实例来存储 entity 模型为这些 API 创建的 entity 提供一个通用的结构,并能定义规则来效验属性值。

Datastore Entities

大家都知道在 GAE 存储的一个数据对象称为 entity 。一个 entity 有多个属性,表示几种支持的数据类型中的一种的值。
每个 entity 都有一个 key 唯一标识这个 entity 。最简单的 key 有一个表类型和一个唯一的数字 id ,是 GAE 给它提供的。 这个 ID 值也可以是应用程序提供的一个字符串。 更多的信息参考 Keys and Entity Groups.
 
一个应用程序可以通过 key 从数据存储中 取出一个 entity ,或者通过匹配 entity 的属性来查询也可以。一个查询还可以匹配 key 的“祖先”。参阅 Keys and Entity Groups ,一个查询可以返回 0 或者多条记录的 entity 。并可以预先排序。也可以限制返回的结果行数以节约内存。
 
不象传统的数据库, GAE 不需要所有某种 dentity 都有相同的属性。应用程序能够通过 API 来规约他们的数据模型。

The Model Interface

利用模型,应用程序描述了数据的不同类型。一个模型是一个 python 类,继承自 Model 类。这个模型类定义了一个新的 entity 类型和以及这个类型所期望具备的属性。模型类利用 python 类的 attributes 来定义模型属性。每个类 attribute Property 子类的实例。通常是一些已经定义好的属性类。一个属性实例包含了属性的配置,例如效验是否必须录入,或者当没有提供值的时候给它一个缺省值。
from google . appengine . ext import db

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 )
在编程接口里面,一个模型类对应的实例表现为一个表类型的 entity 。应用程序通过构造方法产生一个 entity 。通过实例的 attributes 来对属性进行存储。构造函数通过参数得到属性的初始值。
from google . appengine . api import users

pet
= Pet ( name = "Fluffy" ,
          type
= "cat" ,
          owner
= users . get_current_user ())
pet
. weight_in_pounds = 24
注意:模型类的 attributes 是配置模型的属性的,他们的值是 Property 类的实例。模型实例的 attribute 是实际的属性值,是 python 类能够接受的值。
模型类利用 Property 实例来校验给模型实例的属性赋的值。当模型对象一开始建立校验就起作用了,在修改的时候也会进行校验。这保证了属性永远不会非法。由于实例构建的时候就开始校验,标志为必须录入的属性必须在构造方法里面提供值。在这个例子里面, name type owner 都是 required 的,因此调用构建函数的时候就需要提供。而 weight in pounds 就不需要,所以一开始它们是没有赋值,可以在之后的程序当中赋值。
模型实例创建之后并没有在 datastore 里面存储的,必须在调用 put 方法之后才真正写入数据库。
 
注意:模型属性配置以及所有的 python attribute ,在模块或者脚本第一次导入的时候初始化。因为 GAE request 里面缓存了导入的模块,模块配置可能在一个用户请求之后,重新被其它用户请求所利用。 Do not initialize model property configuration, such as default values, with data specific to the request or the current user. SeeApp Caching for more information( 不太理解 )

Expando Models

一个模型定义利用模型类建立固定数量的属性。所有的实例都必须具备这些属性。这是通常的做法,但是 GAE 其实并不需要所有的 entity 具有所有同样的属性。
 
有时候,一个 entity 和其它同类的 entity 具有不同的属性,是很有用的。这种 entity API 里面表示为 expando 模型。一个 expando 模型类是 Expando 的子类。任何赋给 expando 实例的 attribute 的值变成 entity 的同名属性,这些属性叫做动态属性,而那些用 Property 类实例定义了的属性是固定属性。
一个 expando 模型可以同时具备固定属性和动态属性。固定属性是类模型用属性配置来定义的,而动态属性是应用程序在赋值给的时候才建立它们。
class Person ( db . Expando ):
  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
由于动态属性没有相应的模型属性定义,动态属性不会被校验。任何动态属性可以具备基本类型的值,包括 None 。同类的 2 entity 同一个动态属性可以有不同类型的值,也可以一个设置了属性而另外一个不设置。
                                                                                                
和固定属性不同,动态属性不一定存在。一个具有 None 值的动态属性不同于不存在的动态属性。如果 expando 模型实例没有任何动态属性,对应的数据 entity 就没有这些属性,你可以删除通过删除 attribute 来删除一个动态属性。
del p . chess_elo_rating
一个在过滤里面用了动态属性的查询将只返回数据类型和查询语句一致的那些 entity 。同样的,查询将值返回设置了指定属性的 entities
p1 = Person ()
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
Expando 类是 Model 类的子类,并继承所有它的方法。

Properties and Types

Entity 属性支持固定的一些类型,包括 unicode ,整数,浮点,日期, key byte ,二进制,和各种 GData 类型。每种类型都对应 Property 的子类,在 google.appengine.ext.db 里面定义。
 
这里: Types and Property Classes 描述了所有支持的属性类,有几种特殊的类型下面介绍一下。

Strings, Long Strings and Blobs

Datastore 支持 2 中类型的文本:短字符串, 500 字节以下,长字符串, 500 以上。短字符串可以被索引并能够在查询的过滤子句里面用,也可以排序,长的就不行。
 
短字符串可以是 unicode 或者 str 值。如果是一个 str ,缺省是 ’ascii’ 编码。为 str 值指定一个不同的编码,可以用 unicode() 类型构造函数,把它转换为 unicode 值,参数是 str 和编码的名字。短字符串可以用 StringProperty 类来建模。
class MyModel ( db . Model ):
 
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" )
一个长字符串对应 db.Text 实例。它的构造器可以用 unicode 或者 str 值,和一个类型的可选参数。长字符串可用 TextProperty 类来建模
class MyModel ( db . Model ):
  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" )
Datastore 还支持非文本的类型, Blob ,和长字符串一样, blob 不会被索引,也不可过滤查询、排序。 Blob 实例表现为字符字节的集合,创建时用 str 值来作为参数。用 BlogProperty 建模
class MyModel ( db . Model ):
  blob
= db . BlobProperty ()

obj
= MyModel ()

obj
. blob = db . Blob ( open ( "image.png" ). read ())

Lists

一个属性可以有多个值,在 datastore API 表现为 Python list List 包含任意被 datastore 支持的类型。
 
ListProperty 构建 list 模型,并强制 list 里面所有的值为指定的类型。为了方便,库提供了 StringListProperty ,等同于 ListProperty(basestring) .
 
class MyModel ( db . Model ):
  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.
一个查询过滤 List 属性检测 list 的成员。如果至少有一个成员符合条件就通过这个条件过滤。
# Get all entities where numbers contains a 6.
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" )
查询过滤只作用于成员。没办法测试 2 list 是否一致。
 
内部处理上, datastore 用多个值来表示 list 属性。如果一个 list 属性值是空列表,那么这个属性就没有表现在 datastore 。编程接口对待于静态的 ListProperty 属性和动态属性,是不一样的处理的。
  • 一个静态的 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

一个属性值可以包含扩其它 entity key 。这个值是一个 Key 类型的实例。
RefrenceProperty 类对 key 值建模,并迫使所有的值对应某个类型的 entity 。为了方便使用,提供了 SelfReferenceProperty 类型指向自己。
class FirstModel ( db . Model ):
  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
一个 ReferenceProperty 属性值象一个模型实例 entity 一样的使用。如果引用的 entity 在内存不存在,访问它的时候自动从存储 里面取出相应记录。
obj2 . reference . prop = 999
obj2
. reference . put ()

results
= db . GqlQuery ( "SELECT * FROM FirstModel" )
another_obj
= results . fetch ( 1 )[ 0 ]
v
= another_obj . reference . prop
当一个 entity 的关联属性值执行了删除,这个关联属性不会被改变。一个关联属性值不能是一个不再有效的 key 。如果应用程序认为可以存在无效的关联,可以用 db.get() 来取出 entity ,并在使用它之前检测它的属性值。
obj1 = db . get ( obj2 . reference )

if not obj1 :
 
# Referenced entity was deleted.
ReferenceProperty 有另外的方便特征:往回关联。如果一个模型 ReferenceProperty 了另外一个模型,每个被关联的 entity 得到一个属性,它返回第一个指向它的模型一个查询结果 entities
# To fetch and iterate over every SecondModel entity that refers to the
# FirstModel instance obj1:
for obj in obj1 . firstmodel_set :
 
# ...
往回关联属性缺省是 modelname_set(_set  前面的名字是模型类的小写名字 ) ,也可以在 ReferenceProperty 的构造函数的用 collection_name 参数来改变。
 
只有在使用 ReferenceProperty 模型属性类的时候,可以自动关联和废弃关联 , 类型检查和往回关联。用 Expando 动态属性或者 ListProperty 来储存 key 值的就没有这些特征了。

Property Names

Datastore 用前后的 2 个连续下划线保留所有的属性名称,应用程序必须避免这样的命名来定义属性。
 
 
因为缺省情况下 Python API 用模型实例的 attributes 来作为属性名称,所以那些已经被实例方法用过的 attributes ,就不能再作为属性名字了。同样的,模型构造方法里面 keyword 参数里面的名字也都不能用作属性的名字。
 
 
但其实 Datastore 本身是允许这些名字的。如果一个应用程序需要一个 entity
python API 保留的关键字作为属性名称,程序可以在构造函数用 name 参数来指定固定属性。

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

·         Deleting an Entity

Creating and Updating an Entity

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)

Getting EntitiesUsing a Query

Datastore能够执行查询某个类型的entity。一个查询可以用条件子句来过滤entity的属性值,并能够返回经过排序的结果集。一个查询也可以通过祖先来限制查询结果的范围。

 

完整对查询语句的描述信息,包括它不能够做什么,请参阅Queries and Indexes.

DatastoreAPI 通过 2 中界面来执行查询: Query ,用查询对象的一些方法。还有 GqlQuery ,是一种类似 SQL 的语言。

The Query Interface

Model 或者Expandoall() 方法返回查询对象,对应这种表类所有的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

一直等到应用程序操作结果集的时候,QueryGqlQuery 对象才真正执行查询。当程序操作返回结果的时候,查询就会被执行了。结果将作为模型类的实例存储在内存里面。每个查询类都提供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行。如果没有指定限制,或者指定的限制大于最大限制,那么将采用最大限制。

Getting anEntity Using a Key

Entity 被存储到 datastore 之后,就有了一个唯一的 key 。在 API 里面表现 key 值是用 Key 类的一个实例。 Put() 方法返回它的 key 。在第一次被保存之后,就用 key() 来得到 key 值。

 

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传送给用户之前加密它。

Deleting an Entity

应用程序能够根据模型实例或者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 仍然是可以访问的。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值