建立實體模型

您必須為實體建立模型類別。建立方式有以下兩種:

  • 建立一個定義實體屬性的模型類別。
  • 建立一個未事先定義實體的 Expando 模型類別。

本文件說明如何建立各種類型的模型類別, 同時亦說明如何建立模型掛鉤,讓您的應用程式可以在某些類型的作業之前或之後執行特定程式碼,例如每次執行 get() 之前。

建立包含屬性的模型類別

建立實體之前,您必須先建立一個定義一或多個實體屬性的模型類別。例如:

from google.appengine.ext import ndb
...
class Account(ndb.Model):
    username = ndb.StringProperty()
    userid = ndb.IntegerProperty()
    email = ndb.StringProperty()

您可以為其中每個「帳戶」實體的使用者名稱、使用者 ID 和電子郵件地址指定屬性。

如要查看屬性類型的完整清單,請參閱實體屬性參考資料

建立 Expando 模型類別

您不需要使用事先定義屬性的模型類別。 名為 Expando 的特殊模型子類別會變更其實體的行為,因此任何受指派的屬性都會儲存至資料儲存庫。請注意,這類屬性的開頭不得為底線。

以下說明如何建立 Expando 模型:

class Mine(ndb.Expando):
    pass
...
e = Mine()
e.foo = 1
e.bar = 'blah'
e.tags = ['exp', 'and', 'oh']
e.put()

這樣寫入資料儲存庫的實體會含有 foo 屬性且整數值為 1bar 屬性且字串值為 'blah',以及重複的 tag 屬性且字串值為 'exp''and''oh'。 屬性會編入索引,您可以使用實體的 _properties 屬性進行檢查:

return e._properties
# {
#     'foo': GenericProperty('foo'),
#     'bar': GenericProperty('bar'),
#     'tags': GenericProperty('tags', repeated=True)
# }

Expando 是透過從資料儲存庫取得值的方式建立,會包含儲存在資料儲存庫中所有屬性值的屬性。

應用程式可將預先定義的屬性新增至 Expando 子類別中:

class FlexEmployee(ndb.Expando):
    name = ndb.StringProperty()
    age = ndb.IntegerProperty()
...
employee = FlexEmployee(name='Sandy', location='SF')

這會讓 employee 含有值為 'Sandy'name 屬性、值為 Noneage 屬性、以及值為 'SF' 的動態屬性 location

如要建立不將屬性編入索引的 Expando 子類別,請在子類別定義中設定 _default_indexed = False

class Specialized(ndb.Expando):
    _default_indexed = False
...
e = Specialized(foo='a', bar=['b'])
return e._properties
# {
#     'foo': GenericProperty('foo', indexed=False),
#     'bar': GenericProperty('bar', indexed=False, repeated=True)
# }

您也可以在 Expando 實體上設定 _default_indexed。 在這種情況下,這會影響設定「之後」指派的所有屬性。

另一項實用技巧是查詢動態屬性的 Expando 種類。查詢如下所示

FlexEmployee.query(FlexEmployee.location == 'SF')

將無法運作,因為類別沒有位置屬性適用的屬性物件。反之,使用 GenericProperty 的話,Expando 類別可用於動態屬性:

FlexEmployee.query(ndb.GenericProperty('location') == 'SF')

使用模型掛鉤

NDB 提供簡易的掛鉤機制。定義掛鉤後,應用程式可以在某些類型的作業之前或之後執行特定程式碼,例如 Model 可能會在每次執行 get() 之前執行特定函式。

使用同步、非同步和適當方法的多個版本時,就會執行 hook 函式。舉例來說,「pre-get」掛鉤會套用到所有 get()get_async()get_multi()。每個掛鉤都有「RPC 前」和「RPC 後」的版本。

掛鉤機制在下列情況相當實用:

  • 快取查詢
  • 稽核每位使用者的 Cloud Datastore 活動
  • 模擬資料庫觸發條件

以下範例顯示如何定義 hook 函式:

from google.appengine.ext import ndb
...
class Friend(ndb.Model):
    name = ndb.StringProperty()

    def _pre_put_hook(self):
        _notify('Gee wiz I have a new friend!')

    @classmethod
    def _post_delete_hook(cls, key, future):
        _notify('I have found occasion to rethink our friendship.')
...
f = Friend()
f.name = 'Carole King'
f.put()  # _pre_put_hook is called
fut = f.key.delete_async()  # _post_delete_hook not yet called
fut.get_result()  # _post_delete_hook is called

如果您將後置掛鉤與非同步 API 搭配使用,則掛鉤的觸發方式為呼叫 check_result()get_result() 或生成 (在 tasklet 中) 非同步方法的結果。後置掛鉤不會確認遠端程序呼叫 (RPC) 是否成功,即使失敗也會照樣執行。

所有後置掛鉤在呼叫簽章的結尾都有一個 Future 引數。這個 Future 物件會保留動作的結果。針對這個 Future 呼叫 get_result() 即可擷取結果;由於 Future 會在呼叫掛鉤之前完成,因此您可以確定 get_result() 不會遭到封鎖。

在執行前置掛鉤時引發例外狀況會造成無法提出要求。雖然掛鉤是在 <var>*</var>_async 方法中觸發,但您不可先佔遠端程序呼叫 (RPC) (藉由在 RPC 前置掛鉤中引發 tasklets.Return)。

後續步驟