Try   HackMD

Flask Extension(03)_Flask-SQLAlchemy(3)

tags: book flask flask 2.x flask extension sqlalchemy python

Flask-SQLAlchemy
SQLAlchemy
Relationship Configuration

關聯式資料庫中最重要的就是關聯的設置,當我們把資料庫當做物件之後應該如何來設置,就是這一邊要說明的部份。比較有趣的是,改版之後的說明文件並沒有看到關聯設置的範例,所以這部份不是看舊版文件就是要看SQLAlchemy,個人是建議看SQLAlchemy的文件就是了。

關聯

關聯式資料庫的關聯方式有很多種,一對一、一對多、多對多,我們就把SQLAlchemy的範例調整成用Flask-SQLAlchemy來做說明:

class Parent(db.Model): __tablename__ = "parent_table" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String) children = db.relationship("Child") class Child(db.Model): __tablename__ = "child_table" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String) parent_id = db.Column(db.Integer, db.ForeignKey("parent_table.id"))
  • 5:利用db.relationship來做關聯設置,參數中放的是關聯目標的類別名稱,案例中為Child
  • 12:關聯設置需同時設置外來鍵,也就是db.ForeignKey,參數中放的是目標資料表中的鍵值,案例中為parent_table.id

上面是根據SQLAlchemy的範例調整過來的,不難發現它的關聯邏輯就是父表對子表,屬一對多,主要利用db.relationship來做關聯的設定。

事實上,如果你想設置一對一的關聯的話,只需要調整為db.relationship("Child", uselist=False)即可。

下面我們來做幾個操作,快速瞭解orm帶給我們的一些便利:

pp = Parent(name='A') pp.children []
  • 1:新增一筆記錄
  • 2:觀察我們設置的關聯是否正確

因為我們的資料表是新建的,而且裡面都沒有任何資料,所以當我們去看pp.children的時候會得到一個空的list

cc = Child(name='A_C') pp.children.append(cc) with app.app_context(): db.session.add(pp) db.session.commit()
  • 1:新增一筆記錄
  • 2:將新增的資料寫入Partent
  • 3:資料寫入資料表,並完成交易記錄

這邊我們把產生的Child做為一個物件寫入Parent設置的關聯屬性,也就是上面我們說的那個空的list,然後完成這筆交易記錄,記得,flask-sqlalchemy的交易記錄只能寫在flask的context之中。然後回頭看一下我們的資料表:

可以先看一下,基本上parent_table有資料不稀奇,這很正常,不過如果你去看child_table的話就會發現:

經過我們剛剛在ParentChildappend進去那個關聯設置之後,一次性的add可以在兩張表同時生成資料,而且連關聯用的鍵值也同時記錄了。這就是orm給我們的一個便利性。

上面的設置是一對多,反過來就是多對一,互相認識一下,這只需要做一個小調整:

class Parent(db.Model): __tablename__ = "parent_table" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String) children = db.relationship("Child", back_populates='parent') class Child(db.Model): __tablename__ = "child_table" id = db.Column(db.Integer, primary_key=True) name = db.Column(db.String) parent_id = db.Column(db.Integer, db.ForeignKey("parent_table.id")) parent = db.relationship('Parent', back_populates='children')

上面程式碼可以看的出來關聯設置上的差異,利用back_populates.back_populates來做為雙邊關聯的設置。

官方文件中有提到,backref是屬於遺留下來的接口,建議還是用上面的設置方式較為直觀。

一樣的,你可以利用select Child的方式來確認是不是能取得它的相對應的Parent

stms = db.select(Child) results = db.session.execute(stms) result = results.fetchone()[0] result.parent.name >>> 'A' result.name >>> 'A_C'

這邊可能要稍微注意一下,使用fetchonescalar得到的回傳是不一樣的,這部份可以參考官方文件說明。最主要是fetchonefetchall回傳的是sqlalchemy的row;而scalarscalars則是回傳實例,親手做一次就會有感覺。

結論

大致上我們已經知道如何設置一對一、一對多、多對一的關聯,當然還有多對多的關聯,不過這部份不急,後續實作過程中如果有緣就會使用到,一篇文章如果有太多資訊就會造成消化不良以及閱讀障礙。

實務上在資料庫的操作中還會有刪除、更新的需求,刪除的話我們可能會希望當Child沒有資料的時候就刪除相對應的Parent,這一樣的,後續實作中我們就會明白如何處理。