数据库中的每个实体都有一个键,一个对于应用程序中所有实体都唯一的标识符。一个键有若干个组成部分:路径描述两个实体之间的父子关系,实体的类型,以及由应用程序分配给实体的名称,或由数据库分配的数字 ID。
每个实体都属于特定的类型,类型是可以由查询返回的一组实体。与表中的行不同,虽然应用程序可以在一个数据模型中建立此类限制,然而相同类型的两个实体不需要有相同的属性。数据库 API 使用 Model(或 Expando)子类的名称作为类型的名称。
例如,该类定义了名为 [Story] 的类型的 Model。
class Story(db.Model): title = db.StringProperty() author = db.StringProperty()
每个实体都有标识符。应用程序可以通过授予实例构造函数 key_name 参数(str 值)来分配自己的标识符以便在键中使用。
s = Story(key_name="xzy123")
key_name 存储为 Unicode 字符串,str 值转换为 ASCII 文本。key_name 绝不能以数字开头,绝不能采用 __*__ 形式(以两根下划线开头和结尾)。如果您的应用程序使用用户提交的数据作为实体键名(例如电子邮件地址),应用程序应首先清理值,例如用已知的字符串作为前缀以符合这些要求。
如果未指定 key_name,则第一次将实体存储到数据库中时,会向其分配数字 ID。
s2 = Story() # s2 does not have a name or an ID. s2.put() # s2 is given an ID by the datastore.
一旦创建了实体,就不能更改其 ID 或名称。
提示:不能用与查询中的属性值类似的方式使用键名和 ID。但是,您可以使用命名的键,然后将该名称作为属性存储。您可以通过存储对象以分配 ID、使用 obj.key().id() 获取 ID 值、用 ID 设置属性,然后再次存储对象,以对数字 ID 执行类似的操作。
每个实体都属于一个实体组,它是可以在一个事务中控制的一组实体(一个或多个)。实体组关系会让 App Engine 在分布式网络的相同部分中存储若干实体。事务会针对实体组设置数据库操作,且所有操作都会以组的形式应用。如果事务失败,则全都不应用。
当应用程序创建一个实体时,它将另一个实体分配为新实体的父实体。向新实体分配父实体会将新实体放置在与父实体相同的实体组。
没有父实体的实体是根实体。作为另一个实体的父实体的实体也可以有父实体。从某实体到根的父实体链是该实体的路径,路径的成员是该实体的祖先。实体的父实体是在创建该实体时定义的,且以后不能再更改。
每个采用指定根实体作为祖先的实体都在相同的实体组中。一个组中的所有实体都存储在相同的数据库节点中。一个事务可以修改一个组中的多个实体,或向组添加新实体(方法是以组中的现有实体作为新实体的父实体)。
有关事务的详细信息,请参阅事务。
如果删除了某个作为其他实体的祖先的实体,则后续实体不会被删除。仍可以使用后续实体完整的键或路径对其进行访问。
您可以用祖先路径创建实体而无需先创建父实体。为此,您可以使用类型和键名为祖先创建一个 Key,然后将其用作新实体的父实体。所有具有同一根祖先的实体都属于同一实体组,与路径的根是否代表实际实体无关。
使用实体组的提示:
实体的完整键(包括路径、类型、名称或数字 ID)都是唯一的并特定于该实体。在数据库中创建实体时,会分配完整的键,且不能更改其任何部分。
只要至少一个部分不同,两个不同实体的键就可以有相似的部分。例如,如果两个实体有不同的父实体,它们可以有相同的类型和名称。类似地,如果两个实体的类型不同,它们可以有相同的父实体(或没有父实体)和名称。
应用程序不应依赖于以增序(实体创建的顺序)分配的数字 ID。这是通常情况,但并无保证。