GAE 的雲端資料儲存具有下列兩種特性 :
- 擴展性 (Scalable) : 可自動管理使用容量, 具有極大彈性
- 強固性 (Robust) : 資料儲存於多個位置
學習如何使用 Datastore 前必須了解幾個專門術語 :
- 實體 (Entity) :
Datastore 事實上是一個物件資料庫, 物件的屬性直接對應到資料欄位. 每一個儲存在 Datastore 中的物件稱為 Entity (資料實體). 相當於關聯式資料庫中的列 (Rows) 或紀錄 (Records). - 鍵 (key) :
Datastore 中的每一個資料實體都具有一個 key, 這在整個系統中是獨一無二的, 用來辨識每一個 Datastore 中的實體. 在 Datastore Python API 中, key 由 db 函式庫中的 Key 類別的實體負責管理. 資料實體 Entity 也提供 key() 與 getkey() 兩個方法來 注意, Key 並非實體的屬性 (property), 因為一個實體一旦建立後其 key 值就固定不能改變, 而屬性值是可以改變的. Key 由下列幾個部份組成 :
Application ID :
用來區別此實體屬於哪一個應用程式, 避免別的應用存取它. 透過 key 可以快速地從整個 Datastore 中擷取該資料物件.
Entity ID/Key name :
Entity ID 用來辨識實體, 可以在應用程式中指定 (字串), 這時稱為 Key name (使用 key_name 參數), 程式不指定時由 Datastore 自動指派 (數字), 稱為 Entity ID, 但兩者不會同時存在.
Kind :
Kind (資料類型) 就是資料類別的名稱, 也用來辨識資料實體 (因為同名實體可能來自不同類型), 相當於關聯式資料庫中的資料表 (Table), 但不同的是, 一個實體的 Kind 與其屬性沒有關係, 因為屬於同一 Kind 的兩個不同資料實體可以有不同的屬性集合, 而且同名屬性的值可以是不同資料類型. 而關聯式資料庫的資料表與其欄位是相關的, 而且資料表內每一個紀錄列, 其欄位屬性與資料類型都須一致. - 屬性 (property) :
Property 就是資料實體的欄位, 每一個欄位有其資料類型, 相當於關連式資料庫中資料表的欄位 (Field) 一樣, 不同的是, 實體的欄位允許多值 (multiple values). 注意, 資料實體的Property 對應到物件的 Attribute, 雖然中文均為屬性, 但用的地方不同.
GAE 提供了操作 Datastore 的 Python API, 其中 Python 物件與 Datastore 之間有直接的對應 :
- Python 物件=Datastore 的 Entity (實體)
- Python 物件的屬性 attribute=Datastore 的 Eintity 的 property (屬性)
- Python 資料類別 (Class)=Entity 的 Kind (資料類型)
Python Datastore API 詳見 :
# https://developers.google.com/appengine/docs/python/datastore/
GAE 的資料儲存類別放在 google.appengine.ext 路徑下的 db 套件, 因此要在程式中使用資料儲存必須先匯入 db 套件 :
from google.appengine.ext import db
在 Datastore 中建立資料實體的方法有兩種 :
- 繼承 db.Model 類別
- 繼承 db.Expando 類別
接下來我將在之前的兩篇舊作基礎上進行 Datastore 功能測試, 參考 :
# 如何在 GAE 上佈署 jQuery 與 ExtJS 專案
# 如何在 GAE 中處理 HTTP 要求
下面範例 1 將以建立一個可調整的 jQuery UI 頁籤面板為目標, 順便來測試 Datastore 的功能. 我的構想是在 Datastore 中建立各頁籤的資訊, 當點選頁籤時, 就帶入該頁籤內容, 同時我們可以新增, 刪除, 或修改頁籤設定.
首先用 db.Model 來做, 頁籤 Entity 的屬性定義如下 :
tab_name : 頁籤名稱, 唯一識別用, 英數字串
tab_label : 頁籤顯示文字, 文字
tab_link : 頁籤超連結, 文字
tab_level : 頁籤層級, 1=使用者, 9=管理者, 整數
tab_tip : 頁籤提示語, 文字
tab_order : 頁籤顯示順序, 整數
依據這需求撰寫 tabs.py 程式來定義一個資料類別 Tabs 如下 :
# -*- coding: utf-8 -*-
from google.appengine.ext import db
class Tabs(db.Model):
tab_name=db.StringProperty()
tab_label=db.StringProperty()
tab_link=db.StringProperty()
tab_level=db.IntegerProperty()
tab_tip=db.StringProperty()
tab_order=db.IntegerProperty()
tab1=Tabs()
tab1.tab_name="admin"
tab1.tab_label=u"管理"
tab1.tab_link="/tabtest_admin"
tab1.tab_level=9
tab1.tab_tip=u"系統管理"
tab1.tab_order=99
tab1.put()
tab2=Tabs()
tab2.tab_name="logout"
tab2.tab_label=u"登出"
tab2.tab_link="/tab_logout"
tab2.tab_level=1
tab2.tab_tip=u"系統登出"
tab2.tab_order=99
tab2.put()
在這個 tabs.py 程式中, 資料類別 Tabs 繼承了 db.Model 類別, 並定義了 6 個屬性 (property), 然後先呼叫 Tabs() 建構子建立資料物件 tab, 對屬性賦值後呼叫 put() 方法將 tab 物件儲存至 Datastore 中成為一個資料實體 (Entity), 此處我們儲存了 2 個資料實體. 注意, 這裡只要有中文的部分前面都加了一個 u, 這樣做是為了將中文從原先的 byte string 轉成 unicode string 之故, 因為這兩種字串不能混用, 而程式一開始就宣告 # -*- coding: utf-8 -*-, 如此一來所有的中文字串都會以 utf-8 編碼為 byte string, 而其他字串則為 unicode string, 因此執行時會出現如下錯誤 :
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe7 in position 0: ordinal not in range(128)
只要如上在每一個中文屬性值前面加上 u 改為 unicode string 就可以了, 詳細參閱 :
<div id='tabs'>
<ul>
<li title='系統管理'>
<a href='/tab_admin'>管理</a>
</li>
<li title='系統登出'>
<a href='/tab_logout'>登出</a>
</li>
</ul>
</div>
<script language="JavaScript">
$(document).ready(function(){
$(this).attr("title","jQuery Tabs Test");
$("#tabs").tabs();
});
</script>
事實上, Datastore 定義了 24 種 Property 類別, 可用來指定 Entity 各屬性的資料型態, 請參考 :
# Datastore 的 24 種 Property 類別
首先要在 "如何在 GAE 中處理 HTTP 要求" 的範例 3/4 基礎上修改 Members 類別, 擴充其屬性以便對這 24 種屬性進行測試, 如下列範例 1 所示 :
註 : 以上寫於 8/8/2014, 母親住院時的 31 病房.
20160129 補充 :
資料物件實體存入 Datastore 時必須注意兩件事 :
- 最好要指定 key_name 屬性 :
通常是指定不會重複, 可用來作為 primary key 的欄位值做為 key_name 值, 這樣在更新資料或刪除資料時要抓出這筆資料就很方便. - 使用建立物件同時賦值方式 :
這種方式搭配指定 key_name 可以確保不會有許多重複的 entity 存在於資料儲存庫中. 不要用上面先建立空的資料物件, 再一一指配屬性值的方式, 此法即使有指定 key_name 似乎無法避免產生許多重複的資料物件.
正確做法如下 :
tab1=Tabs(key_name="admin",tab_name="admin",
tab_label=u"管理", tab_link="/tabtest_admin", tab_level=9,
tab_tip=u"系統管理", tab_order=99)
tab1.put()
參考 :
# Google App Engine HTTP請求處理
# Google App Engine 及 Web Programming 簡介
# Python 沒有 switch - case
# 用 Python來開 Microsoft Access的.mdb資料檔的中文欄位問題
沒有留言:
張貼留言