在 Datastore 中存储数据


Python 留言板代码演示的这一部分介绍了如何在 Datastore 中存储结构化数据。借助 App Engine 和 Datastore,您无需担心数据的分布、复制和负载平衡问题。所有这些都由一个简单的 API 完成,同时您还可以体验强大的查询引擎和事务功能。

本页面是多页教程中的一页。如需从头开始并查看设置说明,请转到创建留言板

存储提交的问候语

在 Datastore 中,我们将数据被写入到的对象称为实体。每个实体都有一个,用于唯一标识该实体。对于某个实体,您可以选择性地指定另一实体作为其父实体;第一个实体是父实体的子实体。因此,Datastore 中的实体能形成一个与文件系统目录结构类似的分层结构空间。如需了解详情,请参阅设计数据结构以确保高度一致性

App Engine 包括一个针对 Python 的数据建模 API。为使用数据建模 API,示例应用导入了 google.appengine.ext.ndb 模块。每条问候语都包含作者姓名、消息内容以及发布消息的日期和时间。应用按时间顺序显示消息。以下代码定义了数据模型:

class Author(ndb.Model):
    """Sub model for representing an author."""
    identity = ndb.StringProperty(indexed=False)
    email = ndb.StringProperty(indexed=False)

class Greeting(ndb.Model):
    """A main model for representing an individual Guestbook entry."""
    author = ndb.StructuredProperty(Author)
    content = ndb.StringProperty(indexed=False)
    date = ndb.DateTimeProperty(auto_now_add=True)

该代码定义了一个 Greeting 模型,它具有三个属性:author,其值是包含电子邮件地址和作者身份的 Author 对象;content,其值是一个字符串;date,其值是 datetime.datetime

有些属性构造函数会使用参数进一步配置其行为。向 ndb.StringProperty 构造函数传递 indexed=False 参数,表示此属性的值不会编入索引。这样可以省去不必要的写入,因为应用永不会在查询中使用该属性。向 ndb.DateTimeProperty 构造函数传递 auto_now_add=True 参数,模型会配置为自动将新对象的 datetime 时间戳指定为该对象的创建时间(如果应用未另提供值的话)。如需查看属性类型及其选项的完整列表,请参阅 NDB 属性

该应用使用数据模型来创建新的 Greeting 对象,并将其放入 Datastore 中。Guestbook 处理程序创建新的问候语,并将其保存到 Datastore:

class Guestbook(webapp2.RequestHandler):

    def post(self):
        # We set the same parent key on the 'Greeting' to ensure each
        # Greeting is in the same entity group. Queries across the
        # single entity group will be consistent. However, the write
        # rate to a single entity group should be limited to
        # ~1/second.
        guestbook_name = self.request.get('guestbook_name',
                                          DEFAULT_GUESTBOOK_NAME)
        greeting = Greeting(parent=guestbook_key(guestbook_name))

        if users.get_current_user():
            greeting.author = Author(
                    identity=users.get_current_user().user_id(),
                    email=users.get_current_user().email())

        greeting.content = self.request.get('content')
        greeting.put()

        query_params = {'guestbook_name': guestbook_name}
        self.redirect('/?' + urllib.urlencode(query_params))

Guestbook 处理程序创建了新的 Greeting 对象,然后使用用户发布的数据设置其 authorcontent 属性。Greeting 的父实体是 Guestbook 实体。Guestbook 实体只有在准备设置为另一实体的父实体时才需要创建。在本示例中,为执行事务和一致性目的而将父实体用作占位符。如需了解详情,请参阅事务页面。祖先实体相同的对象都属于同一实体组。该代码不设置 date 属性,因此会使用 auto_now_add=True 自动将 date 设置为当前时间。

最后,greeting.put() 将新对象保存到 Datastore。如果我们从查询中获取了该对象,则 put() 就会更新现有对象。因为我们是使用模型构造函数创建此对象的,所以 put() 会将新对象添加到 Datastore。

由于 Datastore 中的查询仅在实体组内才具有高度一致性,因此代码会为各条问候语设置相同的父实体,从而将一个留言板的所有问候语都分配到同一实体组。这意味着用户始终会在问候语写入后立即看到该问候语。不过,向同一实体组写入的速率被限制为每秒 1 次。当您设计真正的应用时,需要记住这一点。请注意,使用 Memcache 等服务可以降低用户在写入后跨实体组查询时看到过时结果的可能性。

检索提交的问候语

Datastore 提供一个用于数据模型的复杂查询引擎。 由于 Datastore 不是传统的关系型数据库,因此不使用 SQL 指定查询。而是通过以下两种方式之一查询数据:使用 Datastore 查询,或者使用一种类似于 SQL 的 GQL 查询语言。如需使用 Datastore 的全部查询功能,我们建议您通过 GQL 方式进行查询。

MainPage 处理程序会检索和显示之前提交的问候语。greetings_query.fetch(10) 调用会执行查询。

有关 Datastore 索引的更多信息

Datastore 中的每个查询都是通过一条或多条索引计算得出的;索引是将经过排序的属性值映射到实体键的表。因此,无论您应用的数据存储区多大,App Engine 都能够快速提供结果。许多查询都可以通过内置索引计算出来,但对于那些更加复杂的查询,Datastore 要求您指定自定义索引。如果没有自定义索引,Datastore 将无法高效执行这些查询。

例如,留言板应用是按照留言板过滤并按照日期排序的,这些都使用祖先查询和排序顺序完成。这就需要在应用的 index.yaml 文件中指定自定义索引。您可以手动修改此文件,也可以通过在应用本地运行查询来自动进行处理。在 index.yaml 中定义索引后,部署应用时也可部署自定义索引信息。

index.yaml 中查询的定义与以下内容类似:

indexes:
- kind: Greeting
  ancestor: yes
  properties:
  - name: date
    direction: desc

如需全面了解 Datastore 索引,请参阅 Datastore 索引页面。如需了解 index.yaml 文件的正确规范,请参阅 Python Datastore 索引配置