2019年3月21日 星期四

[GAE] 使用NDB client library建立Model

介紹Google Cloud Datastore的NDB client library如何建立Model。

建立Model儲存資料:

  model是用來描述entity,就像是SQL裡的表格(table)一樣,而宣告的屬性就像表的field。
  entity是呼叫ndb.Model subclass的建構子建立,呼叫 put() 儲存。下面的例子是建立一個Account model,裡面有username、userid、email三種屬性。
from google.appengine.ext import ndb

class Account(ndb.Model):
    username = ndb.StringProperty()
    userid = ndb.IntegerProperty()
    email = ndb.StringProperty()

  也可以建立能動態追加屬性的entity,須改繼承ndb.Expando:
class FlexEmployee(ndb.Expando):
    name = ndb.StringProperty()
    age = ndb.IntegerProperty()

employee = FlexEmployee(name='Sandy', location='SF') # age會是None, location是動態追加的屬性

logging.info(employee._properties)  # 可查看property
# {
#     'name': StringProperty('name'),
#     'age': IntegerProperty('age'),
#     'location': GenericProperty('location') # GenericProperty用於動態屬性
# }

* 要注意的是使用 FlexEmployee.query(FlexEmployee.location == 'SF')是搜不到東西的,因為該類沒有location屬性的屬性對象。若要以動態屬性進行搜尋要寫像這樣: FlexEmployee.query(ndb.GenericProperty('location') == 'SF')。

使用Model hooks:

  有時我們在作動作時固定都會再作些預處理或後處理,例如讀取資料庫的資料時,我們會先查看資料是否在cache中,而NDB提供我們輕量方便的hook方法來完成這樣的動作。

  hook可用於以下方面:
    1. 查詢緩存
    2. 旁聽每個用戶的存儲活動
    3. 模仿數據庫觸發器 (mimicking database triggers)

範例:
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

* asynchronous post-hook要由check_result(), get_result()來觸發


Entity Property:

  一個entity可有20000個properties,NDB提供了以下儲存屬性:

屬性
說明
IntegerProperty
64-bit int long
FloatProperty
雙精準度浮點數
BooleanProperty
boolean
StringProperty
短字串(上限是1500bytes)資料儲存庫會將短字串建立索引,可用於篩選器和排序順序。系統會將str值認定為使用ascii編碼而成的文字,且會在儲存值之前轉換成unicode值。資料儲存庫會將該值當成unicode值傳回。至於使用其他轉碼器的短字串,則使用unicode值。
TextProperty
無長度限制的長字串,不會建立索引。argunicodestr值。如果argstr,則會由「encoding」指定的編碼剖析,如果沒有指定編碼則為ascii
BlobProperty
未解譯的二進位資料。若設定indexed=True則上限是1500bytes且會建立索引;設定indexed=False則長度無上限但不會建立索引
DateTimeProperty
日期與時間 (可設定auto_now_add、auto_now)
DateProperty
日期 (可設定auto_now_add、auto_now)
TimeProperty
時間 (可設定auto_now_add、auto_now)
GeoPtProperty
浮點經緯度座標 (ndb.GeoPt)
KeyProperty
Cloud Datastore Key
BlobKeyProperty
Blobstore Key
StructuredProperty
存放modelmodel裡用
LocalStructuredProperty
StructuredProperty,但on-disk representation is an opaque blob and is not indexed (可設定compressed)
JsonProperty
 Value is a Python object (such as a list or a dict or a string) that is serializable using Python's json module; Cloud Datastore stores the JSON serialization as a blob. Unindexed by default. (可設定compressed)
PickleProperty
Value is a Python object (such as a list or a dict or a string) that is serializable using Python's pickle protocol; Cloud Datastore stores the pickle serialization as a blob. Unindexed by default. (可設定compressed)
GenericProperty
Generic value,常用在Expando class
ComputedProperty
Value computed from other properties by a user-defined function

  Property內常用的參數:

參數
型態
預設值
說明
Indexed
bool
usually True
if False, values cannot be queried but writes are faster.
repeated
bool
False
Any property with repeated=True becomes a repeated property. The property takes a list of values of the underlying type, rather than a single value.
required
bool
False
一定要有值
default
值由property決定
None
預設值
choices
List of values (值由property決定)
None
可被輸入的值的list (選單)
validator
function
None
Optional function to validate and possibly coerce the value.
verbose_name
string
None
Optional HTML label to use in web form frameworks like jinja2


Writing Property Subclasses:

  A Property subclass that implements a specific transformation between user values and serializable values should implement two methods, _to_base_type() and _from_base_type().

舉例:IntegerProperty只支援儲存64-bit整數,若我們要存超過此值應要轉存成字串,可以建立一個繼承StringProperty的property subclass來處理。

class LongIntegerProperty(ndb.StringProperty):
    def _validate(self, value):
        if not isinstance(value, (int, long)):
            raise TypeError('expected an integer, got %s' % repr(value))

    def _to_base_type(self, value):
        return str(value)  # Doesn't matter if it's an int or a long

    def _from_base_type(self, value):
        return long(value)  # Always return a long 

* _validate():當設定property值時運作。
  _to_base_type():當要把entity寫入datastore時運作。
  _from_base_type():當要從datastore讀entity時運作。



沒有留言:

張貼留言