顯示具有 Firebase 標籤的文章。 顯示所有文章
顯示具有 Firebase 標籤的文章。 顯示所有文章

2022年1月10日 星期一

Python 學習筆記 : Firebase 即時資料庫 (二)

在上一篇測試中已完成 Firebase 資料庫的建立與線上資料新增與刪除等操作, 本篇主要是測試如何利用 python-firebase 套件連線 Firebase 即時資料庫並進行 CRUD (Create, Retrieve, Update, Delete) 操作. 

本系列上一篇文章參考 :

 

六. 安裝 python-firebase 套件 : 

python-firebase 為第三方套件, 須先安裝才能使用, 我先用 pip 指令安裝 :

pip install python-firebase 

C:\Users\User>pip install python-firebase   
Collecting python-firebase
  Downloading python-firebase-1.2.tar.gz (10 kB)
  Preparing metadata (setup.py) ... done
Requirement already satisfied: requests>=1.1.0 in c:\python37\lib\site-packages (from python-firebase) (2.21.0)
Requirement already satisfied: urllib3<1.25,>=1.21.1 in c:\python37\lib\site-packages (from requests>=1.1.0->python-firebase) (1.24.1)
Requirement already satisfied: chardet<3.1.0,>=3.0.2 in c:\python37\lib\site-packages (from requests>=1.1.0->python-firebase) (3.0.4)
Requirement already satisfied: idna<2.9,>=2.5 in c:\python37\lib\site-packages (from requests>=1.1.0->python-firebase) (2.8)
Requirement already satisfied: certifi>=2017.4.17 in c:\python37\lib\site-packages (from requests>=1.1.0->python-firebase) (2018.11.29)
Building wheels for collected packages: python-firebase
  Building wheel for python-firebase (setup.py) ... done
  Created wheel for python-firebase: filename=python_firebase-1.2-py3-none-any.whl size=11534 sha256=3f46bf6f132adf9c4e4dcab2eed24f211fc75ad48b807a4b7ee2eac16fa6cbfc
  Stored in directory: c:\users\user\appdata\local\pip\cache\wheels\0f\6d\06\32ba434ed7b712403f944a537c25e5b9560d766b58d172c8e6
Successfully built python-firebase
Installing collected packages: python-firebase
Successfully installed python-firebase-1.2

但是匯入 firebase.firebase 模組時卻出現語法錯誤 : 

>>> from firebase import firebase    
Traceback (most recent call last):
  File "<pyshell>", line 1, in <module>
  File "C:\Python37\lib\site-packages\firebase\__init__.py", line 3
    from .async import process_pool    
              ^
SyntaxError: invalid syntax

經查詢找到下面這篇 Stackoverflow 文章 :


原來是 python-firebase 的作者似乎沒將修正過的版本同時更新到 PyPi 網站, 導致 pip 從 PyPi 下載安裝到有 bug 的套件, 解決辦法是用 pip 指令直接從 python-firebase 的 GitHub 寄存庫下載套件來安裝 : 

pip install git+https://github.com/ozgur/python-firebase

C:\Users\User>pip install git+https://github.com/ozgur/python-firebase     
Collecting git+https://github.com/ozgur/python-firebase
  Cloning https://github.com/ozgur/python-firebase to c:\users\user\appdata\local\temp\pip-req-build-vtrzu0_q
  Running command git clone --filter=blob:none -q https://github.com/ozgur/python-firebase 'C:\Users\User\AppData\Local\Temp\pip-req-build-vtrzu0_q'
  Resolved https://github.com/ozgur/python-firebase to commit 0d79d7609844569ea1cec4ac71cb9038e834c355
  Preparing metadata (setup.py) ... done
Requirement already satisfied: requests>=1.1.0 in c:\python37\lib\site-packages (from python-firebase==1.2.1) (2.21.0)
Requirement already satisfied: certifi>=2017.4.17 in c:\python37\lib\site-packages (from requests>=1.1.0->python-firebase==1.2.1) (2018.11.29)
Requirement already satisfied: idna<2.9,>=2.5 in c:\python37\lib\site-packages (from requests>=1.1.0->python-firebase==1.2.1) (2.8)
Requirement already satisfied: urllib3<1.25,>=1.21.1 in c:\python37\lib\site-packages (from requests>=1.1.0->python-firebase==1.2.1) (1.24.1)
Requirement already satisfied: chardet<3.1.0,>=3.0.2 in c:\python37\lib\site-packages (from requests>=1.1.0->python-firebase==1.2.1) (3.0.4)
Building wheels for collected packages: python-firebase
  Building wheel for python-firebase (setup.py) ... done
  Created wheel for python-firebase: filename=python_firebase-1.2.1-py3-none-any.whl size=12612 sha256=3ac6b887368946f2e6518e3c14ac262d76d90aa72521031ac6c1ce32292f2951
  Stored in directory: C:\Users\User\AppData\Local\Temp\pip-ephem-wheel-cache-xqlzf6r5\wheels\e7\7e\19\4f87f6d9a85bccbedc016446ef2314ed1f3fff01879f8f2be9
Successfully built python-firebase
Installing collected packages: python-firebase
  Attempting uninstall: python-firebase
    Found existing installation: python-firebase 1.2
    Uninstalling python-firebase-1.2:
      Successfully uninstalled python-firebase-1.2
Successfully installed python-firebase-1.2.1

果然重新從 GitHub 安裝後就可以順利匯入了.


七. python-firebase 套件的 CRUD 操作 : 

安裝好後先來看看此套件的內容, 我們會用到的是 firebase 套件裡的 firebase 模組 : 

>>> from firebase import firebase    
>>> type(firebase)    
<class 'module'>
>>> dir(firebase)   
['FirebaseApplication', 'FirebaseAuthentication', 'FirebaseTokenGenerator', 'FirebaseUser', 'JSONEncoder', '__all__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'http_connection', 'json', 'make_delete_request', 'make_get_request', 'make_patch_request', 'make_post_request', 'make_put_request', 'process_pool', 'urlparse']

可以用內建函式 dir(), type(), 以及 eval() 來過濾 firebase 模組中的成員 :

>>> members=dir(firebase)   
>>> for mbr in members:                 # 走訪 execjs 模組成員
    obj=eval('firebase.' + mbr)          # 用 eval() 求值取得 firebase 成員之參考
    if not mbr.startswith('_'):             # 走訪所有不是 "_" 開頭的成員
        print(mbr, type(obj))
        
FirebaseApplication <class 'type'>
FirebaseAuthentication <class 'type'>
FirebaseTokenGenerator <class 'type'>
FirebaseUser <class 'type'>
JSONEncoder <class 'type'>
http_connection <class 'function'>
json <class 'module'>
make_delete_request <class 'function'>
make_get_request <class 'function'>
make_patch_request <class 'function'>
make_post_request <class 'function'>
make_put_request <class 'function'>
process_pool <class 'firebase.lazy.LazyLoadProxy(function)'>
urlparse <class 'module'>

其中 FirebaseApplication 類別就是用來與 Firebase 即時資料庫建立連線的工具, 可以用它的建構子 FirebaseApplication() 來建立一個 FirebaseApplication 物件, 語法如下 : 

firebase.FirebaseApplication(url, None)  

第一參數 url 為資料庫的網址 (即 Firebase 即時資料庫主控台上方的資料庫根節點網址), 第二參數用來指定物件建立失敗時的傳回值, 預設為 None. 

>>> url='https://tony1966test-default-rtdb.asia-southeast1.firebasedatabase.app/'      
>>> fbapp=firebase.FirebaseApplication(url, None)     
>>> type(fbapp)      
<class 'firebase.firebase.FirebaseApplication'>   
>>> dir(fbapp)    
['NAME_EXTENSION', 'URL_SEPERATOR', '__class__', '__delattr__', '__dict__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__le__', '__lt__', '__module__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', '_authenticate', '_build_endpoint_url', 'authentication', 'delete', 'delete_async', 'dsn', 'get', 'get_async', 'patch', 'patch_async', 'post', 'post_async', 'put', 'put_async'] 

同樣可用內建函式 dir(), type(), 以及 eval() 來過濾 FirebaseApplication 物件中的成員 :

>>> members=dir(fbapp)   
>>> for mbr in members:          # 走訪 execjs 模組成員
    obj=eval('fbapp.' + mbr)       # 用 eval() 求值取得 FirebaseApplication 成員之參考
    if not mbr.startswith('_'):      # 走訪所有不是 "_" 開頭的成員
        print(mbr, type(obj))
        
NAME_EXTENSION <class 'str'>
URL_SEPERATOR <class 'str'>
authentication <class 'NoneType'>
delete <class 'method'>
delete_async <class 'method'>
dsn <class 'str'>
get <class 'method'>
get_async <class 'method'>
patch <class 'method'>
patch_async <class 'method'>
post <class 'method'>
post_async <class 'method'>
put <class 'method'>
put_async <class 'method'>

其中常用的四個物件方法為 post(), get(), put(), 以及 delete(), 分別對應資料庫存取中最常用的 CRUD (Create, Retrieve, Update, Delete) 操作, 用法摘要如下表 :


 物件方法 說明
 get(url, None) 讀取指定 url 節點之資料, 傳回值為 dict 物件 [Retrieve]
 post(url, data) 在指定 url 節點新增一筆資料 data (dict 物件) [Create]
 delet(url + id, None) 刪除指定 url 節點下鍵為 id 之資料 [Delete]
 put(url, name, data) 於 url 節點新增或更新資料, 鍵=name 值=data [Create/Update]


其中 put() 方法與 post() 方法都可以新增資料, 它們的差別有三個, 其一為 post() 無法指定 ID (即 key), 而是由系統指定一個隨機 ID, 而 put() 方法則可以自行指定 ID, 若該 ID 已存在則更新該 ID 內的資料, 所以 put() 方法兼具 Create 與 Update 功能, 這是它們的第二個差別. 其三是 post() 的傳回值是一個鍵為 'name', 值為所新增之資料的 ID; 而 put() 的傳回值為所新增的資料 (值). 


1. 用 get() 方法讀取資料 : 

呼叫 FirebaseApplication 物件的 get(url, None) 方法可讀取指定 url 節點下的全部資料, 成功就以 dict 型態傳回這些資料, 失敗就傳回第二個參數 (通常設為 None). 在上一篇測試中, 我們已經透過 Firebase 即時資料庫主控台在 tony1966test 這個資料庫中手動建立如下資料 : 




在 get() 中傳入資料庫根節點將傳回此資料庫內所有資料, 例如 : 

>>> from firebase import firebase
>>> import time 
>>> url='https://tony1966test-default-rtdb.asia-southeast1.firebasedatabase.app/'      
>>> fbapp=firebase.FirebaseApplication(url, None) 
>>> for k, v in data.items():       
    print(f'id={k} value={v}')      
    time.sleep(1)      
    
id=motto value={'motto1': '處事宜帶春風, 律己宜帶秋氣 (林則徐)', 'motto2': '物質生活向下看, 精神生活向上看 (胡適)'}
id=motto1 value=處事宜帶春風, 律己宜帶秋氣 (林則徐)

此處匯入 time 模組是為了讓每次存取 Firebase 有時間間隔 (例如每秒存取一次), 避免傳輸延遲問題. 上面是讀取資料庫根節點的結果, 可見 id=motto 因為底下還有一層, 因此其值為一個 dict 物件; 而 id=motto1 因為是直接放在根節點下, 所以其值是一個 str 物件. 由於 Firebase 資料庫支援 RESTful 存取, 因此如果將 url 指定為根節點的下一層節點, 則 get() 將傳回該節點下的所有資料, 例如 :  

>>> from firebase import firebase
>>> import time 
>>> url='https://tony1966test-default-rtdb.asia-southeast1.firebasedatabase.app/'      
>>> fbapp=firebase.FirebaseApplication(url, None) 
>>> data=fbapp.get(url + "/motto", None)    
>>> data      
{'motto1': '處事宜帶春風, 律己宜帶秋氣 (林則徐)', 'motto2': '物質生活向下看, 精神生活向上看 (胡適)'}
>>> for k, v in data.items():    
    print(f'id={k} value={v}')       
    time.sleep(1)   
    
id=motto1 value=處事宜帶春風, 律己宜帶秋氣 (林則徐)
id=motto2 value=物質生活向下看, 精神生活向上看 (胡適)

可見傳入 get() 的 url 參數若加上鍵的路徑, 就可以取得該節點下的全部資料, 例如此處是取得根節點的下一層 id=motto 節點下的全部資料. 


2. 用 post() 方法新增資料 : 

呼叫 post(url, data) 方法可在指定節點 url 底下新增資料, 由於是純粹的 Create 操作, 新增的資料會被指定一個隨機 ID 以避免鍵的衝突. post() 的傳回值是一個 dict 物件, 其鍵固定為 'name', 其值為該新增資料的鍵 ID. 

如果第二參數為一個基本類型物件 (數值或字串等), 則此新增資料會被放在根節點下, 例如 : 

>>> from firebase import firebase
>>> import time 
>>> url='https://tony1966test-default-rtdb.asia-southeast1.firebasedatabase.app/'      
>>> fbapp=firebase.FirebaseApplication(url, None) 
>>> data=fbapp.post(url, "初念淺, 轉念深")       
>>> data      
{'name': '-MsxZcjlmRCZQxXo9miT'}      # post() 的傳回值為一個字典物件

這時去看主控台會發現在根節點下已經新增了一筆 ID 為自動指派的資料 : 




如果傳入 post(url, data) 的第二參數 data 是一個字典, 則這筆資料不會放在根節點下, 而是放在下一層,  Firebase 會先指派一個隨機 ID 建立一個目錄節點, 然後將這筆資料放在這節點下面, 其 key 就是 dict 物件的 key. 傳回值同樣是一個鍵為 'name' 的字典, 值為目錄節點之 ID, 例如 :  

>>> data=fbapp.post(url, {'motto3': '初念淺, 轉念深'})    
>>> data   
{'name': '-MsxcUA65p_Q_kYtnd5S'}     

這時在主控台就可以看到這筆掛在根節點的下一層, 自動指派 ID 目錄節點下的新增資料了 : 




如果要將資料放在指定之目錄節點, 例如 /motto 節點下面, 則 post() 的 url 參數應該指定為 url + '/motto', 且 data 參數應該直接傳入字串, 而非 dict 物件, 例如 : 

>>> data=fbapp.post(url + '/motto', '初念淺, 轉念深')    # url 加掛 /motto, data=字串
>>> data   
{'name': '-MszU-El7e99pqBDTFZC'}

這時在主控台就可以看到這筆新增資料是直接掛在 /motto 節點之下 : 




如果 data 參數是傳入字典, 那麼這筆資料就不會直接放在 /motto 下, 而是 /motto 的下一層, 自動指派 ID 的目錄節點下, 例如 : 

>>> data=fbapp.post(url + '/motto', {'motto3': '初念淺, 轉念深'})     
>>> data      
{'name': '-Mt0Uq9CEa93xZoyhrbn'}

新增後到主控台就可以看見 /motto 下新增了一個自動指派 ID 的目錄節點, 新增資料是掛在此節點下, 而不是直接掛在 /motto 下 : 




由上面測試可知, post() 是個純粹的 Create 操作, 所新增的資料都會由系統指派一個隨機 ID, 無法自行指定, 若想自行指派 ID 必須使用 put() 方法. 


 3. 用 put() 方法新增或更新資料 : 

put(url, name, data) 兼具新增 (Create) 與更新 (Update) 操作功能, 它有三個參數, name 是 ID (鍵), 而 data 是值. 注意, 使用關鍵字參數時 name 與 data 位置可互換, 但若使用位置參數時, 第二參數為 name, 第三參數為 data. 傳回值為所新增的值 (即 data), 例如 : 

>>> from firebase import firebase
>>> import time 
>>> url='https://tony1966test-default-rtdb.asia-southeast1.firebasedatabase.app/'      
>>> fbapp=firebase.FirebaseApplication(url, None) 
>>> data=fbapp.put(url + '/motto', name='motto3', data='初念淺, 轉念深')   
>>> data     
'初念淺, 轉念深'

此處呼叫 put() 時指定 url 為根節點的下一層目錄節點 /motto, 並以關鍵字參數 name 指定資料的 ID (鍵) 為 motto3, 以 data 指定資料的值, 由於 /motto 下面並沒有 ID=motto3 的資料, 故此為一新增資料的操作, put() 的傳回值為所新增資料的值. 

這時到主控台就可看到 /motto 節點下這筆鍵為 motto3 的新增資料 : 




上面若使用位置參數也是可以的, 結果完全一樣 :

>>> data=fbapp.put(url + '/motto', 'motto3', '初念淺, 轉念深')    
>>> data  
'初念淺, 轉念深' 

由於前面已經用 put() 新增了 ID=motto3, 所以上面這個使用位置參數的 put() 其實是 Update 的更新動作, 因為值相同所以看不出有甚麼更新, 下面用不一樣的值就看得出來了 :

>>> data=fbapp.put(url + '/motto', name='motto3', data='天下沒有白吃的午餐')     
>>> data   
'天下沒有白吃的午餐'

這時去看主控台就會發現 /motto 下的 motto3 內容已經被更新了 : 



 
在物聯網應用中, 若要保存歷史資料就用 post(), 新的資料不會覆蓋舊資料; 否則就用 post() 不斷更新一個固定的 ID. 


4. 用 delete() 方法刪除資料 : 

呼叫 delete(url + id, None) 可刪除指定 ID 的那筆資料, 其中第二參數為傳回值, 一般設為 None. 如果所指定的 ID 不存在也不會出現錯誤訊息, 一樣傳回 None, 例如刪除 /motto 下的 motto3 :

>>> from firebase import firebase
>>> import time 
>>> url='https://tony1966test-default-rtdb.asia-southeast1.firebasedatabase.app/'      
>>> fbapp=firebase.FirebaseApplication(url, None) 
>>> data=fbapp.delete(url + '/motto/motto3', None)    
>>> print(data)    
None

這時查看主控台會發現 /motto 底下的 motto3 已經被刪除了 :




要刪除用 post() 新增的資料, 同樣也是將那看似亂數的隨機 ID 帶在 url 後面即可, 例如刪除根節點底下 ID=-MsxZcjlmRCZQxXo9miT 的這筆資料 : 

>>> id='-MsxZcjlmRCZQxXo9miT'  
>>> data=fbapp.delete(url + id, None)   
>>> print(data)  
None

檢視主控台可知  ID=-MsxZcjlmRCZQxXo9miT 這筆資料已被刪除 : 




接下來 ID='-MsxcUA65p_Q_kYtnd5S' 這筆資料, 其值是一個鍵為 motto3 的 JSON, 刪除它會將整個資料刪除, 例如 :  

>>> id='-MsxcUA65p_Q_kYtnd5S'    
>>> data=fbapp.delete(url + id, None)    
>>> print(data)   
None

檢查主控台該筆資料已被刪除 : 




如果刪除某個節點, 此節點底下不管有多少層, 所有資料都會被刪除. 例如上圖若刪除 /motto 這節點, 它底下全部資料都會全部刪除. 


5. 用迴圈批次新增資料 : 

最後來測試用迴圈批次新增資料的方法, 這些資料是放在一個字典串列中 : 

motto=[{ "motto1" : "處事宜帶春風, 律己宜帶秋氣 (林則徐)"},
             { "motto2" : "物質生活向下看, 精神生活向上看 (胡適)"},
             { "motto3" : "初念淺, 轉念深"},
             { "motto4" : "天下沒有白吃的午餐"}]

做法是用迴圈依序讀取串列中的字典, 然後用 put() 或 post() 寫到資料庫裡. 首先用 delete() 或在主控台手動清空所有資料 :




首先用 post() 來新增資料, 如上所述, post() 無法自訂節點名稱 (name), 因此若將串列中的字典寫入 /motto 下的話, 會新增一層隨機名稱的節點來存放該鍵值對, 完整程式如下 :

# firbase-batch-create-data-post-1.py
from firebase import firebase

mottos=[{"motto1" : "處事宜帶春風, 律己宜帶秋氣 (林則徐)"},
        {"motto2" : "物質生活向下看, 精神生活向上看 (胡適)"},
        {"motto3" : "初念淺, 轉念深"},
        {"motto4" : "天下沒有白吃的午餐"}]

url='https://tony1966test-default-rtdb.asia-southeast1.firebasedatabase.app/'
fbapp=firebase.FirebaseApplication(url, None)
for motto in mottos:
    fbapp.post(url + '/motto', data=motto)

結果如下 : 




可見資料如預期地被放在 /motto 的下一層隨機名稱節點下. 如果想去掉多出來的一層, 只好犧牲原始資料中字典的鍵 (motto1, motto2, ...), 直接使用隨機名稱為鍵, 這樣就不能將字典傳給 post() 的第二參數, 而是只取出字典中的值傳入即可. 同樣先清空資料庫, 然後再執行下列程式 : 

# firbase-batch-create-data-post-2.py
from firebase import firebase

mottos=[{"motto1" : "處事宜帶春風, 律己宜帶秋氣 (林則徐)"},
        {"motto2" : "物質生活向下看, 精神生活向上看 (胡適)"},
        {"motto3" : "初念淺, 轉念深"},
        {"motto4" : "天下沒有白吃的午餐"}]

url='https://tony1966test-default-rtdb.asia-southeast1.firebasedatabase.app/'
fbapp=firebase.FirebaseApplication(url, None)
for motto in mottos:
    value=list(motto.values())[0]
    fbapp.post(url + '/motto', data=value) 

此處呼叫字典的 values() 方法會傳回一個狀似串列的 dict_values 物件, 此物件內容不能使用索引存取, 故必須先用 list() 將其轉成串列才行, 結果如下 : 





如果想要自訂節點名稱, 則必須使用 put() 方法, 但必須先從原始資料中取出字典中的鍵與值, 分別傳給 put() 的參數 name 與 daya. 先將資料庫清空再執行下列程式 :

# firbase-batch-create-data-put.py
from firebase import firebase

mottos=[{"motto1" : "處事宜帶春風, 律己宜帶秋氣 (林則徐)"},
        {"motto2" : "物質生活向下看, 精神生活向上看 (胡適)"},
        {"motto3" : "初念淺, 轉念深"},
        {"motto4" : "天下沒有白吃的午餐"}]

url='https://tony1966test-default-rtdb.asia-southeast1.firebasedatabase.app/'
fbapp=firebase.FirebaseApplication(url, None)
for motto in mottos:
    key=list(motto.keys())[0]
    value=list(motto.values())[0]
    fbapp.put(url + '/motto', name=key, data=value)

結果如下 : 




我把資料匯出如下 : 

{
  "motto" : {
    "motto1" : "處事宜帶春風, 律己宜帶秋氣 (林則徐)",
    "motto2" : "物質生活向下看, 精神生活向上看 (胡適)",
    "motto3" : "初念淺, 轉念深",
    "motto4" : "天下沒有白吃的午餐"
  }
}

可見匯出的 Firebase 資料庫為 JSON 格式, 也可以用 URL 直接取得 (若存取權限 read=True 的話), 其 URL 結構為 :

https://App網址/資料庫名稱.json  

例如上面的 motto 資料庫網址為 :


注意, 以前使用 firebaseio.com 網域 (很多網路資料都是舊的), 但後來改成 firebasedatabase.app 了, 若用舊網域會得到如下錯誤回應 : 


{
  "correctUrl" : "https://tony1966test-default-rtdb.asia-southeast1.firebasedatabase.app",
  "error" : "Database lives in a different region. Please change your database URL to https://tony1966test-default-rtdb.asia-southeast1.firebasedatabase.app"
}

參考 : 


2022年1月5日 星期三

Python 學習筆記 : Firebase 即時資料庫 (一)

Firebase 創立於 2011 年 9 月, 是專為行動 App 開發者提供的 BAAS (Backend As A Service) 雲端服務平台, 主要產品 Firebase 即時資料庫是以 JSON 格式儲存的 NoSQL 資料庫, 可與 Python 字典的鍵值對資料型態完美匹配. Firebase 公司於 2014 年被 Google 收購, 並與旗下之 Google Cloud 雲端服務整合, 為行動與 Web 開發者提供一站式整體解決方案, 將複雜之技術細節包裝起來, 使開發者專注於使用者需求, 以利快速開發出高品質之應用. 因此 Firebase 不再只是一個即時雲端資料庫系統, 而是一個行動 App 服務平台 (Platform), 參考 :

NoSQL 與傳統之關聯式資料庫迴然不同, 不需事先定義資料表與欄位. Firebase 特殊之處在於即時, 資料可已在所有客戶端之間即時同步, 設計者可設定監聽事件, 當資料庫中的資料變動時會向應用程式發出訊息, 讓應用程式可據此馬上做出回應. 與傳統的 Request-Response 模式不同的是, Firebase 資料庫的同步機制可在數毫秒內與所有相連此外 Firebase 並與 AdMob, AdWords 等服務結合以擴增使用者層面, 參考官網介紹 :

Firebase 提供免費的 Spark 方案與付費的 Blaze 方案 (依量實支實付, pay as you go), Firebase 提供線上費用計算器供開發者估算所需費用. 免費方案則有如下連線數與流量容量限制 :
  • 連線 : 同時 100 個
  • 容量 : 1 GB
  • 流量 : 每月10 GB
  • 檔案 : 每日 50KB (讀取), 20KB (寫入, 刪除)
對一般流量不大或開發中的應用而言免費方案即綽綽有餘. 





Firebase 支援 Android, iOS, 與 Web 等相應的 API 函式庫, 教學文件參考 :

# https://firebase.google.com/docs/guides

參考書籍 : 

# 超簡單 Python/MicroPython 物聯網應用 (博碩, 陳會安, 2021) 第 13-3 節


一. 新增專案 : 

一個專案代表一個資料庫, 在建立資料庫之前須先新增一個專案 (一個帳戶下可以建立多個專案, 也就是多個資料庫). 由於 Firebase 已被 Goolge 併購整合, 故只要有 Google 帳號即可在登入後使用 Firebase, 首先連線 Firebase 網站 :




按 "Get started" 鈕會進入 Google 登入頁面  :



登入成功後會進入專案管理頁面 : 




此頁面會以按鈕形式列出已建立之 Firebase 專案, 若尚未建立過專案, 則只有 "新增專案" 一個按鈕, 按此新增一個專案 :





在 "專案名稱" 欄填入專案名稱, 若名稱已被別人使用, 則底下會顯示建議的專案 ID, 通常是在想要的名稱後面添加幾個隨機字元, 可點那枝筆去編輯. 這個專案 ID 是 App 或應用程式用來辨認資料庫用的唯一識別, 不可重複, 此處填入 tony1966test 後上下一致表示此 ID 尚無人使用. 按 "繼續" 進入步驟 2 設定是否加入 Google Analytics : 




採用預設設定按 "繼續" 即可 : 




此處只要從下拉式選單中選擇一個 Google 帳戶, 按 "建立專案" 鈕就會開始建立專案 : 





按 "繼續" 鈕即進入此專案的管理儀表板 (Dashboard) 頁面 : 





二. 建立資料庫 : 

建立專案後就可以在此專案下面建立 NoSQL 資料庫, 點選儀表板左方的 "Realtime Database", 再按右方框內的 "建立資料庫" 鈕會彈出資料庫設定頁面 :  





即時資料庫預設位置為美國中部, 亞洲地區可選擇較近的新加坡, 選好後按 "下一步" 進入安全性規則設定頁面 (即讀寫權限控管模式設定) :




預設是 "以鎖定模式啟動", 此模式下對資料庫的存取將受到安全性規則的限制, 此處先改為 "以測試模式啟動", 此模式會讓所有使用者在未來 30 天內均擁有讀寫刪除資料庫的權限. 按 "啟用" 即建立資料庫, 並顯示 Firebase 資料庫主控台頁面 : 




主控台頁面預設顯示在 "資料" 頁籤, 可在此處手動操作資料庫 (新增, 編輯, 或刪除鍵值資料). 首先切換到控制台頁面上方的 "規則" 頁籤會顯示目前的安全性規則 : 





如果要讓所有使用者永遠可讀寫此資料庫 (例如即時通訊 App 資料庫), 可在下方編輯器中將規則中的兩個鍵 .read 與 .write 都改成 true :

{
  "rules": {
    ".read": true,  
    ".write": true, 
  }
}

然後按 "發布" 鈕更改設定 : 




如果 App 只允許一般使用者讀取, 不允許寫入, 安全性規則要改為如下 :

{
  "rules": {
    ".read": true,  
    ".write": false, 
  }
}

如果只允許 Google 登入者寫入, 則規則要設為如下 : 

{
  "rules": {
    ".read": true,  
    ".write": "auth !== null", 
  }
}

這樣就完成資料庫的設定, 可以開始新增資料到 NoSQL 資料庫裡面了. 


三. 新增與刪除資料 : 

在 Firebase 即時資料庫主控台頁面切回 "資料" 頁籤即可進行線上資料庫操作 : 




主控台 "資料" 頁籤最上方是 App 的網址, 這個 URL 在使用 Python 或網頁存取 Firease 資料庫時會用到. 下方是專案 (資料庫) 名稱, 按後面的 + 鈕可在此資料庫下新增 key-value 資料, 在 "名稱" 欄輸入 key, 在 "值" 欄輸入 value, 再按 "新增" 即可, 例如 : 





這樣就在 tony1966test 資料庫下新增一筆鍵值對資料了, 但這資料是直接放在資料庫根目錄底下, 也可以用樹狀 JSON 結構來儲存資料, 亦即先在根目錄下輸入一個 key (類似檔案系統的子目錄或關聯式資料庫中的資料表), 但不輸入值, 而是按 "值" 欄後面的 + 鈕, 這樣會在此 key 底下出現建值對輸入框可新增鍵值對, 例如 : 






可見在 motto 鍵下已經新增了 key=motto1 的資料了. 如果要在此鍵下輸入第二筆資料, 可將滑鼠移到 motto 鍵上, 按後面的 + 鈕輸入第二筆鍵值對即可 : 





以上就是手動新增鍵值對資料的方法. 若要刪除資料只要將滑鼠移到該筆資料上, 再按出現在後面的 x 鈕即可. 注意, 線上操作只能新增與刪除資料, 無法直接編輯資料, 若要修改某筆資料的值, 只要新增一筆 key 一樣的資料就會蓋掉原來的資料, 與編輯功能一樣.


四. 匯出資料庫 : 

即時資料庫可匯出成 JSON 檔案下載保存, 在主控台頁面按右上角的垂直三點按鈕, 然後在彈出選單中點選 "匯出 JSON" 後按 "另存新檔" :





用記事本開啟所下載的 tony1966test-default-rtdb-export.json 檔案內容如下 : 




可見這是一個標準的 JSON 格式檔案 :

{
  "motto" : {
    "motto1" : "處事宜帶春風, 律己宜帶秋氣 (林則徐)",
    "motto2" : "物質生活向下看, 精神生活向上看 (胡適)"
  },
  "motto1" : "處事宜帶春風, 律己宜帶秋氣 (林則徐)"
}


五. 匯出資料庫 : 

外部 JSON 檔案可以直接匯入資料庫, 這對資料庫轉移 (例如將 MySQL 資料庫轉到 Firebase), 或資料庫備份很方便 (匯出保存必要時再匯入). 首先線上操作資料庫, 點選根結點後面的 x 按鈕, 然後在彈出視窗中按 "刪除", 這會將目前資料庫內的所有資料全部刪除 : 





清空資料庫後, 按右上角的垂直三點按鈕, 然後在彈出選單中點選 "匯入 JSON", 接著於另一個彈出頁面中按 "瀏覽" 鈕選取上面匯出備份的 JSON 檔後按 "匯入" 鈕即可 :






可見匯入完成後原先的資料又回來了. 


2019年3月24日 星期日

好站 : 用 Python 玩硬體

今天在網路上偶然發現下面這個網頁, 裡面標註了參考我的部落格中關於 MicroPython 的文章, 沒想到我的實驗筆記還有人看哩, 更高興的是遇到也喜歡 MicroPython 的同道, 且此文寫得極好, 作者使用了 Firebase 線上資料庫, 而我 Firebase 的書買了卻還沒時間看 :

# 用 Python 玩硬體:MicroPython 簡介與實作

看完手實在很癢, 但目前須忍住, 今年須專心做好做滿 Fintecch 專案, 絕不可再心猿意馬. 等做完後再來好好地大玩特玩 MicroPython 吧! 我有很多專案等著做實驗. 暫時記在這裡.