Python 2 NDB 用戶端程式庫總覽

Google Datastore NDB 用戶端程式庫可讓 App Engine Python 應用程式連線至 Cloud Datastore。NDB 用戶端程式庫以舊版 DB Datastore 程式庫為基礎,但新增了以下資料儲存庫功能:

  • StructuredProperty 類別,可讓實體採用巢狀結構。
  • 經過整合的自動快取功能,通常可以透過內容快取和 Memcache 以成本低廉的方式快速讀取資料。
  • 支援同步 API 和用於並行操作的非同步 API。

本頁頁概略說明 NDB 用戶端程式庫。

定義實體、金鑰和屬性

Cloud Datastore 會儲存稱為「實體」的資料物件。實體具有一或多項「屬性」,也就是屬於其中一種可支援資料類型的具名值。例如,屬性可以是字串、整數,也可以是另一個實體的參照。

每個實體各有一個用於識別的「金鑰」,這是在應用程式儲存庫中不重複的 ID。金鑰可以另有一個「父項」金鑰。這個父項的本身也可以有其父項,以此類推;在這個父項「連鎖」的頂端,就是一個沒有父項的金鑰,稱為「根」

呈現實體群組中根實體與子系實體之間的關係

實體的金鑰若屬於同一個根,則會構成「實體群組」或「群組」。若實體屬於不同的群組,有時候對這些實體進行的變更會表現出「亂序」現象。若這些實體在您的應用程式語意中並不相關,就無所謂。但是,若其中一些實體的變更應該保持一致,則您的應用程式應在建立這些實體時將其歸為同一個群組。

下列實體關係圖和程式碼範例說明一個 Guestbook 可以有多個 Greetings,其中每一個各有其 contentdate 屬性。

呈現使用隨附程式碼範例建立的實體關係

以下程式碼範例會應用這個關係。

import cgi
import textwrap
import urllib

from google.appengine.ext import ndb

import webapp2

class Greeting(ndb.Model):
    """Models an individual Guestbook entry with content and date."""
    content = ndb.StringProperty()
    date = ndb.DateTimeProperty(auto_now_add=True)

    @classmethod
    def query_book(cls, ancestor_key):
        return cls.query(ancestor=ancestor_key).order(-cls.date)

class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.out.write('<html><body>')
        guestbook_name = self.request.get('guestbook_name')
        ancestor_key = ndb.Key("Book", guestbook_name or "*notitle*")
        greetings = Greeting.query_book(ancestor_key).fetch(20)

        greeting_blockquotes = []
        for greeting in greetings:
            greeting_blockquotes.append(
                '<blockquote>%s</blockquote>' % cgi.escape(greeting.content))

        self.response.out.write(textwrap.dedent("""\
            <html>
              <body>
                {blockquotes}
                <form action="/sign?{sign}" method="post">
                  <div>
                    <textarea name="content" rows="3" cols="60">
                    </textarea>
                  </div>
                  <div>
                    <input type="submit" value="Sign Guestbook">
                  </div>
                </form>
                <hr>
                <form>
                  Guestbook name:
                    <input value="{guestbook_name}" name="guestbook_name">
                    <input type="submit" value="switch">
                </form>
              </body>
            </html>""").format(
                blockquotes='\n'.join(greeting_blockquotes),
                sign=urllib.urlencode({'guestbook_name': guestbook_name}),
                guestbook_name=cgi.escape(guestbook_name)))

class SubmitForm(webapp2.RequestHandler):
    def post(self):
        # We set the parent key on each 'Greeting' to ensure each guestbook's
        # greetings are in the same entity group.
        guestbook_name = self.request.get('guestbook_name')
        greeting = Greeting(parent=ndb.Key("Book",
                                           guestbook_name or "*notitle*"),
                            content=self.request.get('content'))
        greeting.put()
        self.redirect('/?' + urllib.urlencode(
            {'guestbook_name': guestbook_name}))

app = webapp2.WSGIApplication([
    ('/', MainPage),
    ('/sign', SubmitForm)
])

使用模型儲存資料

模型是一種類別,用於說明實體的類型,包括其屬性的類型和設定。模型大致可比 SQL 資料表。要建立實體,可以呼叫模型的類別建構函式,而要儲存實體,則可呼叫 put() 方法。

這個程式碼範例定義 Greeting 模型類別。每個 Greeting 實體各有兩項屬性:問候語的文字內容,以及建立該問候語的日期。

class Greeting(ndb.Model):
    """Models an individual Guestbook entry with content and date."""
    content = ndb.StringProperty()
    date = ndb.DateTimeProperty(auto_now_add=True)
class SubmitForm(webapp2.RequestHandler):
    def post(self):
        # We set the parent key on each 'Greeting' to ensure each guestbook's
        # greetings are in the same entity group.
        guestbook_name = self.request.get('guestbook_name')
        greeting = Greeting(parent=ndb.Key("Book",
                                           guestbook_name or "*notitle*"),
                            content=self.request.get('content'))
        greeting.put()

為建立並儲存新的招呼語,應用程式會建立一個新的 Greeting 物件,並會呼叫其 put() 方法。

為避免留言板中的問候語發生「亂序」現象,應用程式會在建立新的 Greeting 時設定父項金鑰。因此會將新問候語和同一個留言板中的其他問候語歸在同一個實體群組中。應用程式進行查詢時會運用這項事實:會使用父項查詢。

查詢和索引

應用程式可以透過查詢功能尋找符合特定篩選條件的實體。

    @classmethod
    def query_book(cls, ancestor_key):
        return cls.query(ancestor=ancestor_key).order(-cls.date)

class MainPage(webapp2.RequestHandler):
    def get(self):
        self.response.out.write('<html><body>')
        guestbook_name = self.request.get('guestbook_name')
        ancestor_key = ndb.Key("Book", guestbook_name or "*notitle*")
        greetings = Greeting.query_book(ancestor_key).fetch(20)

一般 NDB 查詢會依種類篩選實體。在此範例中,query_book 會產生傳回 Greeting 實體的查詢。查詢也可以指定實體屬性值和金鑰的篩選條件。如同此範例所示,查詢可以指定一個父項,以利單純尋找「屬於」某個父項的實體。查詢可以指定排序順序。針對篩選條件和排序順序中的每個屬性,若指定實體至少包含一個值 (有可能是空值),且其屬性值符合所有篩選準則,傳回的結果就會包含該實體。

每一次查詢均會使用一個「索引」,也就是一個資料表,表中會按照所需順序排序查詢結果。基礎 Datastore 會自動維護簡單索引 (只使用一種屬性的索引)。

並且會使用 index.yaml 這個設定檔定義其複雜索引。如果開發網頁伺服器遇到尚未設定索引的查詢,會自動在這個檔案中新增建議。

您可以在上傳應用程式之前編輯設定檔,以手動方式微調索引。如果您只要更新索引而不上傳應用程式,請執行 gcloud app deploy index.yaml。如果您的資料庫包含許多實體,就需要較長的時間為實體建立新索引;在此情況下,最好先更新索引定義再上傳使用新索引的程式碼。您可以使用「管理控制台」確認完成建構索引的時間。

這種索引機制支援多種查詢,也適用於大多數的應用程式。但不支援其他資料庫技術常見的幾種查詢功能,尤其不支援彙整功能。

瞭解 NDB 寫入:提交、撤銷及套用快取

NDB 資料寫入步驟:

  • 在「提交」階段,基礎資料儲存庫服務會記錄變更。
  • NDB 會撤銷相關實體的快取內容。因此,在其後的讀取作業中,系統會讀取及快取基礎資料儲存庫中的資料,而非快取資料中過時的值。
  • 最後,在大約幾秒鐘之後,基礎資料儲存庫就會「套用」變更。基礎資料儲存庫會開放全域查詢和最終一致性讀取作業存取變更內容。

寫入資料的 NDB 函式 (例如 put()) 會在快取撤銷後傳回結果,「套用」階段並不會同步發生。

「提交」階段如出現失敗情形,會自動重試,但若持續失敗,應用程式就會出現例外狀況。若「提交」階段成功,但「套用」失敗,只要發生以下任一情形,就會將「套用」向前輪動至完成:

  • 定期執行的資料儲存庫清除作業會檢查未完成的「提交」工作,並予以套用。
  • 相關實體群組下一次執行寫入、交易或同步一致性讀取作業時,會在執行讀取、寫入或交易作業之前套用尚未套用的變更。

這個行為會影響應用程式取得資料的方式和時機。NDB 函式傳回後,可能會發生經過數百毫秒左右仍未將變更完全套用於基礎資料儲存庫的情形。若在套用變更時執行非祖系查詢,可能會出現不一致的狀態,亦即只套用部分變更,而非所有變更。如要進一步瞭解寫入及查詢時間點,請參閱 App Engine 中的交易隔離

交易與快取資料

NDB 用戶端程式庫可以群組單項「交易」中的多項作業。交易中的每一項作業皆須成功,該項交易才會成功;若有任何一項作業失敗,交易就會自動復原。這項特性特別適合分散式網路應用程式,因為可能會有多位使用者同時存取或處理同一筆資料。

NDB 使用 Memcache 快取服務處理資料中的「作用點」。若應用程式經常讀取某些實體,NDB 可以快速自快取中讀取這些實體。

同時使用 Django 與 NDB

如要同時使用 NDB 與 Django 網路架構,請將 google.appengine.ext.ndb.django_middleware.NdbDjangoMiddleware 新增至 Django settings.py 檔案中的 MIDDLEWARE_CLASSES 項目。最好將它插入在任何其他中介軟體類別之前,因為可能會有某個其他中介軟體呼叫資料儲存庫,若先叫用該中介軟體再呼叫此中介軟體,可能無法正確處理這些中介軟體。如要進一步瞭解,請參閱 Django 中介軟體

相關資源

進一步瞭解:

本頁內容對您是否有任何幫助?請提供意見:

傳送您對下列選項的寶貴意見...

這個網頁
Python 2 適用的 App Engine 標準環境