flask
flask_ext
python
SQLAlchemy
在Flask實作_ext_01_Flask-SQLAchemy_初探中,我們瞭解到如何透過類別的建置來產生表單,此篇主要討論資料表之間的關聯設置。
關聯式資料庫中,不外乎一對一、一對多、多對一與多對多的關聯方式,關聯的設置影響到我們對物件的操作,老話一句,需求推動功能,先瞭解基本,有進階需求的時候自然會尋找答案,另外,flask-sqlalchemy
的官方文件對幾種關聯方式都有案例,如果臨時想不到如何設置的時候記得查詢官方文件如官方文件Declaring Models。
我們利用上一篇的範例來做調整,如下修正:
SQLAlchemy
中,每個Model都要求要設置主鍵,名稱通常設置為id
範例中設置了兩個類別,一個使用者(User),一個聯絡方式(Contact),在寫程式的時候我會特別註記好設置關聯的那一方是『一對多的一或一對多的多』,透過兩邊的設置讓SQLAlchemy
瞭解關聯的關係。
在Python Shell中執行db.create_all()
之後,可以透過sqlite
圖形介面查看,已經確實的產生了兩個Table,如下圖:
db.relationship
,透過關聯設置讓人家知道Contact
跟User
相愛,兩個人之間恩愛透過backref
來連接關係。(也可以透過back_populates
設置)不過實際上這是一個在雙方都設置監聽器的作法,透過db.relationship
讓SQLAlchemy
知道Contact
跟User
是有關聯的,但前提是必需設置ForeignKey
,更深入的說明可參閱參考文件中Relationships-backref
db.ForeignKey
可很以直觀的以資料庫的角度去看,這代表我們在保存聯絡方式的時候會在資料庫內存儲使用者的IDusers.id
透過Python Shell來improt
設定的兩個資料類別,並新增使用者,範例如下:
第4行:建立一個新的使用者,使用者名稱為admin
第5行:查詢關聯,回傳為空的list,這是因為我們尚未建置任何關聯資料
現在我們來建立聯絡方式,範例如下:
第1行:建立新的聯絡方式
第2行:利用append
的方式將該物件寫入list
,也就是我們設置的關聯contacts
第3行:查詢目前使用者的聯絡方式
第5行:查詢這聯絡方式的使用者,注意到user
即是我們在db.relationship
中設置的backref=user
到這邊,應該可以理解相關的設置邏輯,也可能很驚訝就這樣就已經完成了一個表單的關聯,而且是雙向的設置,這代表如果我們要清除資料的話,只需要刪除一邊即可,如下:
上面的案例可以看的出來,將contact.user
設置為None
之後,admin.contacts
也變空的list
了,非常令人驚豔。
現在我們來看將資料正式的寫入資料庫的情況,如下:
執行之後會發現,雖然session.add
只有將admin
這個類寫入,但是在關聯設置之下contact
也一併寫入資料庫。
第5行:實作中加入關聯物件
上面範例,在實作『多』的物件時直接利用backref
屬性設置關聯之後寫入資料庫。
在建立好User
跟Contact
的關聯之後,我們嚐試著select
一筆使用者資料,如下:
透過user_query.contacts
查詢會發現相關關聯資料已經幫你一起帶出了,很不錯吧,又一次的驚豔!
只是這種情況下會有一個問題,如果你想再透過查詢式query
來查詢你的contacts
就沒有辦法了,因為生米已成熟飯,它的type
已經是list
,我們必需保留它是物件的狀態,在有需要的時候我們再來對關聯資料做二次查詢。
這時候就需要調整db.relationship
的參數,加入lazy
,調整如下:
第7行:加入參數lazy='dynamic'
透過參數lazy='dynamic'
可以讓SQLAlchemy
在搜尋關聯資料的時候保留物件狀態,如下說明:
這時候回傳資料是一個SQLAlchemy
物件類別,這樣就可以透過filter
的方式再來做二次搜尋,或是透過order_by
做排序,如下範例:
兩個Model之間的關聯如果是一對一的話,宣告方式與一對多相同,只需要在主要Model的db.relationship
中加入參數uselist=False
就可以,相同於一對多,也可以利用backref
在主要Model中設置uselist
。
多對多關聯使用情境,舉例來說:
在SQLAlchemy
中設置多對多我們並不需要去特別的定義一個Model來做中繼,而是透過Table
,設罝MetaData
記錄兩個Model的ForeignKey
,再於db.relationship
加入參數secondary
來設置關聯表,因為我們是利用flask-sqlalchemy
來操作資料庫,所以設置上可以參考flask-sqlalchemy的範例。
flask-sqlalchemy強烈建議,以真正的資料表來記錄多對多的關聯
相關建置說明如下註解:
在利用Python Shell產生資料庫之後,我們來看一下資料庫的狀態,如下圖:
可以看到,雖然我們沒有利用Model來設置table
,但SQLAlchemy
會依db.Table
的設置來產生中繼表,接著產生資料看看它的存取狀態為何,如下:
首先確認資料庫的記錄狀態,可以發現資料庫的中繼表確實的記錄的雙邊的關聯,如下圖:
現在,手動再新增一個使用者,並且套用相同的聯絡人看看,如下:
資料庫的變化狀況如下:
接著測試搜尋Contact
,結果如下,直接取下文字貼上:
利用Contact
回查user
會依我們設置的relation lazy
參數來回傳,因為我們設置lazy=True
,意義等同於lazy=select
,如果我們希望在必要時候再透過filter
來取值的話,那就記得使用lazy=dynamic
,這時候就會如下方一般的回傳物件:
最後,測試一下刪除,如果我們將聯絡人刪除,這時候的資料庫狀況為何?
這時候的中繼表相關資料也被刪除了,如果不想要這樣連動刪除資料的話可以透過參數設置來避免。
sqlalchemy的官方說明,雖然說我們使用的是flask-sqlalchemy
,但本質上還是小調整之後可以完成的。
多筆寫入的情況下可以透過db.session.add_all([...list...])
的方式來寫入,如果是大量資料要寫入的話,可以利用bulk
,這部份也可以查詢SQLAlchemy
官方文件
幾個關聯方式練習下來相信對SQLAlchemy
都有了初步的瞭解,這已經足夠我們建置系統使用了,不足的在有需求的時候查詢官方文件相信是會有相對應的答案。
Flask-SQLAlchemy_初探:Flask實作_ext_01_Flask-SQLAlchemy_初探
Flask-SQLAlchemy_Query:Flask實作_ext_18_Flask-SQLAlchemy_Query
tag設置並非本篇的主旨,但是tag並非一定要設置為多對多,也可以設置一個字串欄位來保存,這邊提供二篇用於mysql中的設置說明,讓人驚訝的是,在tag愈見增長的時候,多對多的效能反而是較不好的。
設置說明
效能測試