沒錯,傑夫老師最近在玩 App Inventor,正好研究到 Firebase Realtime Database 的部分,而且發現 App Inventor 與 Python 程式上很多不同的用法與結果,網路上找了半天也沒人說的清楚,那傑夫老師只好寫下來供自己也供大家參考,有任何問題可與傑夫老師討論喔!
除了說明用的是 App Inventor 中歸類於 Experimental 的 FirebaseDB 元件外,其他 Firebase Realtime Database 的建立與 App Inventor 基礎就不多說,直接進入主題。
(AI2: App Inventor 縮寫、FDB: Firebase Realtime Database 縮寫)
上一 part 最後已經做到可以用 AI2 在 FDB 中建立一個 dict 型態的資料庫,目的為的是要讓在不同的開發環境 (AI2 or Python) 都能用同樣的型態寫入資料,進而利用 JSON 格式方便的分析資料。然而,這回到了一開始的問題,如果一定要記錄資料的總筆數,那應該用 Tag 0 來記錄嗎?
因為 FB_test1 中存的資料格式是一個 list (清單),這會給人一個印象是清單裡的資料都是一致的,存的都是 Name, Score 組成的 dict (字典) 資料,然而 Tag: 0 偏偏是個異類裡頭存的僅是一個數字,這個數字代表的是總筆數,它其實是這個資料庫的屬性。
這雖然沒有什麼問題,資料庫中怎麼存資料,只要讀、寫的人約定好就好。況且,資料從 1 開始編號,好像也沒什麼錯。但繼續深入一點看問題,如果我要算總分,也就是把每筆資料的 Score 加起來,那要怎麼做呢?原本只要簡單地將清單裡的 Score 相加,但卻會因為第 0 筆資料格式與其他不同而需要特殊處理。
來看看以下 Python 程式:
from firebase import firebase
fbdb_url="https://000000-default-rtdb.firebaseio.com/"
fbdb = firebase.FirebaseApplication(fbdb_url, None)
D1={"Name":'Mew', "Score":63} #每筆資料都是dict型態
D2={"Name":'Nue', "Score":79}
D3={"Name":'Omh', "Score":86}
D4={"Name":'Pie', "Score":90}
D_list=[4,D1,D2,D3,D4] #建立一個清單,注意Tag:0是數字
fbdb.put("", name="FB_test1", data=D_list) #存到FB_test1
datas = fbdb.get("FB_test1", None) #讀取FB_test1所有資料
print(datas, type(datas))
sum=0
for item in datas: #很直覺的用這個方法
sum+=item["Score"] #但這行會出錯
print(sum)
輸出結果:
[4, {'Name': 'Mew', 'Score': 63}, {'Name': 'Nue', 'Score': 79}, {'Name': 'Omh', 'Score': 86}, {'Name': 'Pie', 'Score': 90}] <class 'list'>
Traceback (most recent call last):
File "d:\Jeff\myCourses\Python\myPy\PlayGround\fbtest.py", line 20, in <module>
sum+=item["Score"]
TypeError: 'int' object is not subscriptable
程式必須改寫成:
sum=0
for i in range(1,len(datas)): #需要從1開始
sum+=datas[i]["Score"]
print(sum)
輸出結果:
[4, {'Name': 'Mew', 'Score': 63}, {'Name': 'Nue', 'Score': 79}, {'Name': 'Omh', 'Score': 86}, {'Name': 'Pie', 'Score': 90}] <class 'list'>
318
從資料庫結構的角度來看,FB_test1 算是一個專案,當你打開這個專案時,得到一個資料 list (清單),那你會對這裡面的資料做一致性的操作 (例如加總),不會特意地為了 Tag: 0 做例外處理。為了維持這樣的一致性,就應該把總筆數存在其他 Tag 下,也就是要把屬性與資料分開記錄。
Python 程式改成以下這樣:
from firebase import firebase
fbdb_url="https://000000-default-rtdb.firebaseio.com/"
fbdb = firebase.FirebaseApplication(fbdb_url, None)
D1={"Name":'Mew', "Score":63} #每筆資料都是dict型態
D2={"Name":'Nue', "Score":79}
D3={"Name":'Omh', "Score":86}
D4={"Name":'Pie', "Score":90}
D_list=[D1,D2,D3,D4] #建立一個只有資料的清單
fbdb.put("FB_test1", name="Students", data=D_list) #資料存在Tag: Students中
fbdb.put("FB_test1", name="LastNumber", data=len(D_list)) #總筆數另外存在Tag:LastNumber中
datas = fbdb.get("FB_test1", name="Students")
print(datas, type(datas))
輸出結果:
[{'Name': 'Mew', 'Score': 63}, {'Name': 'Nue', 'Score': 79}, {'Name': 'Omh', 'Score': 86}, {'Name': 'Pie', 'Score': 90}] <class 'list'>
如果要算總分,程式就不用特別處理了:
sum=0
for item in datas:
print(item["Score"])
sum+=item["Score"]
print(sum)
輸出結果:
63
79
86
90
318 #總分
那 AI2 程式該怎麼做呢?看看以下的程式:
Screen初始與讀取所有資料程式
取得值時的程式
因為 ProjectBucket 設的是 FB_test1,那就直接讀取 Tag: Students 的資料就可以把所有 Name & Score 值讀回來,還可以利用 list by walking key path 與 walk all at level 兩個指令,來列出所有的 Score 值。
執行結果
資料筆數顯示的數字 4 就是 LastNumber 的值,下方顯示的
[63. 79, 86, 90]
318
就是各 Score 的值以及總和 318,利用 list by walking key path 程式就變的相當簡單明瞭。
看似這樣就大功告成了?但這其中還有貓膩,傑夫老師留到下一 part 最終回說咯! Bye now~