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 )
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
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. See App 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
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
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
里面定义。
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" )
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" )
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 ())
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.
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" )
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
类型的实例。
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
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
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.
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 :
# ...
# 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
参数来指定固定属性。